diff --git a/.gitsecret/keys/pubring.kbx b/.gitsecret/keys/pubring.kbx new file mode 100644 index 0000000..8b5e6e7 Binary files /dev/null and b/.gitsecret/keys/pubring.kbx differ diff --git a/.gitsecret/keys/pubring.kbx~ b/.gitsecret/keys/pubring.kbx~ new file mode 100644 index 0000000..9e736bb Binary files /dev/null and b/.gitsecret/keys/pubring.kbx~ differ diff --git a/.gitsecret/keys/trustdb.gpg b/.gitsecret/keys/trustdb.gpg new file mode 100644 index 0000000..fcca739 Binary files /dev/null and b/.gitsecret/keys/trustdb.gpg differ diff --git a/.gitsecret/paths/mapping.cfg b/.gitsecret/paths/mapping.cfg new file mode 100644 index 0000000..65485bf --- /dev/null +++ b/.gitsecret/paths/mapping.cfg @@ -0,0 +1 @@ +files/2309944673836897605-key.pem diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl new file mode 100644 index 0000000..39bf434 --- /dev/null +++ b/.terraform.lock.hcl @@ -0,0 +1,29 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.55.0" + constraints = ">= 4.20.1" + hashes = [ + "h1:NHgKROQfH2vdYgpcD046DrCbFLIONgIzj4UeVNdku3w=", + ] +} + +provider "registry.terraform.io/hashicorp/random" { + version = "3.6.2" + hashes = [ + "h1:wmG0QFjQ2OfyPy6BB7mQ57WtoZZGGV07uAPQeDmIrAE=", + "zh:0ef01a4f81147b32c1bea3429974d4d104bbc4be2ba3cfa667031a8183ef88ec", + "zh:1bcd2d8161e89e39886119965ef0f37fcce2da9c1aca34263dd3002ba05fcb53", + "zh:37c75d15e9514556a5f4ed02e1548aaa95c0ecd6ff9af1119ac905144c70c114", + "zh:4210550a767226976bc7e57d988b9ce48f4411fa8a60cd74a6b246baf7589dad", + "zh:562007382520cd4baa7320f35e1370ffe84e46ed4e2071fdc7e4b1a9b1f8ae9b", + "zh:5efb9da90f665e43f22c2e13e0ce48e86cae2d960aaf1abf721b497f32025916", + "zh:6f71257a6b1218d02a573fc9bff0657410404fb2ef23bc66ae8cd968f98d5ff6", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:9647e18f221380a85f2f0ab387c68fdafd58af6193a932417299cdcae4710150", + "zh:bb6297ce412c3c2fa9fec726114e5e0508dd2638cad6a0cb433194930c97a544", + "zh:f83e925ed73ff8a5ef6e3608ad9225baa5376446349572c2449c0c0b3cf184b7", + "zh:fbef0781cb64de76b1df1ca11078aecba7800d82fd4a956302734999cfd9a4af", + ] +} diff --git a/.terraform/modules/main/.gitignore b/.terraform/modules/main/.gitignore new file mode 100644 index 0000000..9b8a46e --- /dev/null +++ b/.terraform/modules/main/.gitignore @@ -0,0 +1,34 @@ +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log +crash.*.log + +# Exclude all .tfvars files, which are likely to contain sensitive data, such as +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject +# to change depending on the environment. +*.tfvars +*.tfvars.json + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* + +# Ignore CLI configuration files +.terraformrc +terraform.rc diff --git a/.terraform/modules/main/.terraform.lock.hcl b/.terraform/modules/main/.terraform.lock.hcl new file mode 100644 index 0000000..f4159fd --- /dev/null +++ b/.terraform/modules/main/.terraform.lock.hcl @@ -0,0 +1,26 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.49.0" + constraints = ">= 4.20.1" + hashes = [ + "h1:BKrMq4aIOvXbJA9fd0kdmIm3Q01MQcheDIEzXtrkNf4=", + "h1:Y3xvYjzBIwYSbcnZDcs6moiy30uxRoY5oT2ExQHKG5A=", + "zh:0979b07cdeffb868ea605e4bbc008adc7cccb5f3ba1d3a0b794ea3e8fff20932", + "zh:2121a0a048a1d9419df69f3561e524b7e8a6b74ba0f57bd8948799f12b6ad3a1", + "zh:573362042ba0bd18e98567a4f45d91b09eb0d223513518ba04f16a646a906403", + "zh:57be7a4d6c362be2fa586d270203f4eac1ee239816239a9503b86ebc8fa1fef0", + "zh:5c72ed211d9234edd70eac9d77c3cafc7bbf819d1c28332a6d77acf227c9a23c", + "zh:7786d1a9781f8e8c0079bf58f4ed4aeddec0caf54ad7ddcf43c47936d545a04f", + "zh:82133e7d39787ee91ed41988da71beecc2ecb900b5da94b3f3d77fbc4d4dc722", + "zh:8cdb1c154dead85be8352afd30eaf41c59249de9e7e0a8eb4ab8e625b90a4922", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:ac215fd1c3bd647ae38868940651b97a53197688daefcd70b3595c84560e5267", + "zh:c45db22356d20e431639061a72e07da5201f4937c1df6b9f03f32019facf3905", + "zh:c9ba90e62db9a4708ed1a4e094849f88ce9d44c52b49f613b30bb3f7523b8d97", + "zh:d2be3607be2209995c80dc1d66086d527de5d470f73509e813254067e8287106", + "zh:e3fa20090f3cebf3911fc7ef122bd8c0505e3330ab7d541fa945fea861205007", + "zh:ef1b9d5c0b6279323f2ecfc322db8083e141984cfe1bb2f33c0f4934fccb69e3", + ] +} diff --git a/.terraform/modules/main/CODEOWNERS b/.terraform/modules/main/CODEOWNERS new file mode 100644 index 0000000..d91b77f --- /dev/null +++ b/.terraform/modules/main/CODEOWNERS @@ -0,0 +1,3 @@ +#### How to use this file: https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners +# These owners will be the default owners for everything in the repo. Unless a later match takes precedence +* @roknsound/terraform-reviewers diff --git a/.terraform/modules/main/CODE_OF_CONDUCT.md b/.terraform/modules/main/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..5b627cf --- /dev/null +++ b/.terraform/modules/main/CODE_OF_CONDUCT.md @@ -0,0 +1,4 @@ +## Code of Conduct +This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact +opensource-codeofconduct@amazon.com with any additional questions or comments. diff --git a/.terraform/modules/main/CONTRIBUTING.md b/.terraform/modules/main/CONTRIBUTING.md new file mode 100644 index 0000000..c4b6a1c --- /dev/null +++ b/.terraform/modules/main/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# Contributing Guidelines + +Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional +documentation, we greatly value feedback and contributions from our community. + +Please read through this document before submitting any issues or pull requests to ensure we have all the necessary +information to effectively respond to your bug report or contribution. + + +## Reporting Bugs/Feature Requests + +We welcome you to use the GitHub issue tracker to report bugs or suggest features. + +When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already +reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: + +* A reproducible test case or series of steps +* The version of our code being used +* Any modifications you've made relevant to the bug +* Anything unusual about your environment or deployment + + +## Contributing via Pull Requests +Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: + +1. You are working against the latest source on the *main* branch. +2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. +3. You open an issue to discuss any significant work - we would hate for your time to be wasted. + +To send us a pull request, please: + +1. Fork the repository. +2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. +3. Ensure local tests pass. +4. Commit to your fork using clear commit messages. +5. Send us a pull request, answering any default questions in the pull request interface. +6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. + +GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and +[creating a pull request](https://help.github.com/articles/creating-a-pull-request/). + + +## Finding contributions to work on +Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. + + +## Code of Conduct +This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact +opensource-codeofconduct@amazon.com with any additional questions or comments. + + +## Security issue notifications +If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. + + +## Licensing + +See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. diff --git a/.terraform/modules/main/LICENSE b/.terraform/modules/main/LICENSE new file mode 100644 index 0000000..1bb4f21 --- /dev/null +++ b/.terraform/modules/main/LICENSE @@ -0,0 +1,15 @@ +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/.terraform/modules/main/README.md b/.terraform/modules/main/README.md new file mode 100644 index 0000000..ef12a23 --- /dev/null +++ b/.terraform/modules/main/README.md @@ -0,0 +1,203 @@ +# AWS CodePipeline CI/CD example +Terraform is an infrastructure-as-code (IaC) tool that helps you create, update, and version your infrastructure in a secure and repeatable manner. + +The scope of this pattern is to provide a guide and ready to use terraform configurations to setup validation pipelines with end-to-end tests based on AWS CodePipeline, AWS CodeBuild, AWS CodeCommit and Terraform. + +The created pipeline uses the best practices for infrastructure validation and has the below stages + +- validate - This stage focuses on terraform IaC validation tools and commands such as terraform validate, terraform format, tfsec, tflint and checkov +- plan - This stage creates an execution plan, which lets you preview the changes that Terraform plans to make to your infrastructure. +- apply - This stage uses the plan created above to provision the infrastructure in the test account. +- destroy - This stage destroys the infrastructure created in the above stage. +Running these four stages ensures the integrity of the terraform configurations. + +## Directory Structure +```shell +|-- CODE_OF_CONDUCT.md +|-- CONTRIBUTING.md +|-- LICENSE +|-- README.md +|-- data.tf +|-- examples +| `-- terraform.tfvars +|-- locals.tf +|-- main.tf +|-- modules +| |-- codebuild +| | |-- README.md +| | |-- main.tf +| | |-- outputs.tf +| | `-- variables.tf +| |-- codecommit +| | |-- README.md +| | |-- data.tf +| | |-- main.tf +| | |-- outputs.tf +| | `-- variables.tf +| |-- codepipeline +| | |-- README.md +| | |-- main.tf +| | |-- outputs.tf +| | `-- variables.tf +| |-- iam-role +| | |-- README.md +| | |-- data.tf +| | |-- main.tf +| | |-- outputs.tf +| | `-- variables.tf +| |-- kms +| | |-- README.md +| | |-- main.tf +| | |-- outputs.tf +| | `-- variables.tf +| `-- s3 +| |-- README.md +| |-- main.tf +| |-- outputs.tf +| `-- variables.tf +|-- templates +| |-- buildspec_apply.yml +| |-- buildspec_destroy.yml +| |-- buildspec_plan.yml +| |-- buildspec_validate.yml +| `-- scripts +| `-- tf_ssp_validation.sh +`-- variables.tf + +``` +## Installation + +#### Step 1: Clone this repository. + +```shell +git@github.com:aws-samples/aws-codepipeline-terraform-cicd-samples.git +``` +Note: If you don't have git installed, [install git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git). + + +#### Step 2: Update the variables in `examples/terraform.tfvars` based on your requirement. Make sure you ae updating the variables project_name, environment, source_repo_name, source_repo_branch, create_new_repo, stage_input and build_projects. + +- If you are planning to use an existing terraform CodeCommit repository, then update the variable create_new_repo as false and provide the name of your existing repo under the variable source_repo_name +- If you are planning to create new terraform CodeCommit repository, then update the variable create_new_repo as true and provide the name of your new repo under the variable source_repo_name + +#### Step 3: Update remote backend configuration as required + +#### Step 4: Configure the AWS Command Line Interface (AWS CLI) where this IaC is being executed. For more information, see [Configuring the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html). + +#### Step 5: Initialize the directory. Run terraform init + +#### Step 6: Start a Terraform run using the command terraform apply + +Note: Sample terraform.tfvars are available in the examples directory. You may use the below command if you need to provide this sample tfvars as an input to the apply command. +```shell +terraform apply -var-file=./examples/terraform.tfvars +``` + +## Pre-Requisites + +#### Step 1: You would get source_repo_clone_url_http as an output of the installation step. Clone the repository to your local. + +git clone + +#### Step 2: Clone this repository. + +```shell +git@github.com:aws-samples/aws-eks-accelerator-for-terraform.git +``` +Note: If you don't have git installed, [install git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git). + +#### Step 3: Copy the templates folder to the AWS CodeCommit sourcecode repository which contains the terraform code to be deployed. +```shell +cd examples/ci-cd/aws-codepipeline +cp -r templates $YOUR_CODECOMMIT_REPO_ROOT +``` + + +#### Step 4: Update the variables in the template files with appropriate values and push the same. + +#### Step 5: Trigger the pipeline created in the Installation step. + +**Note1**: The IAM Role used by the newly created pipeline is very restrictive and follows the Principle of least privilege. Please update the IAM Policy with the required permissions. +Alternatively, use the _**create_new_role = false**_ option to use an existing IAM role and specify the role name using the variable _**codepipeline_iam_role_name**_ + +**Note2**: If the **create_new_repo** flag is set to **true**, a new blank repository will be created with the name assigned to the variable **_source_repo_name_**. Since this repository will not be containing the templates folder specified in Step 3 nor any code files, the initial run of the pipeline will be marked as failed in the _Download-Source_ stage itself. + +**Note3**: If the **create_new_repo** flag is set to **false** to use an existing repository, ensure the pre-requisite steps specified in step 3 have been done on the target repository. + + +## Requirements + +| Name | Version | +|------|-----------| +| [terraform](#requirement\_terraform) | \>= 1.0.0 | + + +## Providers + +| Name | Version | +|------|------------| +| [aws](#provider\_aws) | \>= 4.20.1 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [codebuild\_terraform](#module\_codebuild\_terraform) | ./modules/codebuild | n/a | +| [codecommit\_infrastructure\_source\_repo](#module\_codecommit\_infrastructure\_source\_repo) | ./modules/codecommit | n/a | +| [codepipeline\_iam\_role](#module\_codepipeline\_iam\_role) | ./modules/iam-role | n/a | +| [codepipeline\_kms](#module\_codepipeline\_kms) | ./modules/kms | n/a | +| [codepipeline\_terraform](#module\_codepipeline\_terraform) | ./modules/codepipeline | n/a | +| [s3\_artifacts\_bucket](#module\_s3\_artifacts\_bucket) | ./modules/s3 | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [build\_project\_source](#input\_build\_project\_source) | aws/codebuild/standard:4.0 | `string` | `"CODEPIPELINE"` | no | +| [build\_projects](#input\_build\_projects) | Tags to be attached to the CodePipeline | `list(string)` | n/a | yes | +| [builder\_compute\_type](#input\_builder\_compute\_type) | Relative path to the Apply and Destroy build spec file | `string` | `"BUILD_GENERAL1_SMALL"` | no | +| [builder\_image](#input\_builder\_image) | Docker Image to be used by codebuild | `string` | `"aws/codebuild/amazonlinux2-x86_64-standard:3.0"` | no | +| [builder\_image\_pull\_credentials\_type](#input\_builder\_image\_pull\_credentials\_type) | Image pull credentials type used by codebuild project | `string` | `"CODEBUILD"` | no | +| [builder\_type](#input\_builder\_type) | Type of codebuild run environment | `string` | `"LINUX_CONTAINER"` | no | +| [codepipeline\_iam\_role\_name](#input\_codepipeline\_iam\_role\_name) | Name of the IAM role to be used by the Codepipeline | `string` | `"codepipeline-role"` | no | +| [create\_new\_repo](#input\_create\_new\_repo) | Whether to create a new repository. Values are true or false. Defaulted to true always. | `bool` | `true` | no | +| [create\_new\_role](#input\_create\_new\_role) | Whether to create a new IAM Role. Values are true or false. Defaulted to true always. | `bool` | `true` | no | +| [environment](#input\_environment) | Environment in which the script is run. Eg: dev, prod, etc | `string` | n/a | yes | +| [project\_name](#input\_project\_name) | Unique name for this project | `string` | n/a | yes | +| [repo\_approvers\_arn](#input\_repo\_approvers\_arn) | ARN or ARN pattern for the IAM User/Role/Group that can be used for approving Pull Requests | `string` | n/a | yes | +| [source\_repo\_branch](#input\_source\_repo\_branch) | Default branch in the Source repo for which CodePipeline needs to be configured | `string` | n/a | yes | +| [source\_repo\_name](#input\_source\_repo\_name) | Source repo name of the CodeCommit repository | `string` | n/a | yes | +| [stage\_input](#input\_stage\_input) | Tags to be attached to the CodePipeline | `list(map(any))` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [codebuild\_arn](#output\_codebuild\_arn) | The ARN of the Codebuild Project | +| [codebuild\_name](#output\_codebuild\_name) | The Name of the Codebuild Project | +| [codecommit\_arn](#output\_codecommit\_arn) | The ARN of the Codecommit repository | +| [codecommit\_name](#output\_codecommit\_name) | The name of the Codecommit repository | +| [codecommit\_url](#output\_codecommit\_url) | The Clone URL of the Codecommit repository | +| [codepipeline\_arn](#output\_codepipeline\_arn) | The ARN of the CodePipeline | +| [codepipeline\_name](#output\_codepipeline\_name) | The Name of the CodePipeline | +| [iam\_arn](#output\_iam\_arn) | The ARN of the IAM Role used by the CodePipeline | +| [kms\_arn](#output\_kms\_arn) | The ARN of the KMS key used in the codepipeline | +| [s3\_arn](#output\_s3\_arn) | The ARN of the S3 Bucket | +| [s3\_bucket\_name](#output\_s3\_bucket\_name) | The Name of the S3 Bucket | + + +## Security + +See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. + +## License + +This library is licensed under the MIT-0 License. See the LICENSE file. + diff --git a/.terraform/modules/main/build_user.tf b/.terraform/modules/main/build_user.tf new file mode 100644 index 0000000..498949b --- /dev/null +++ b/.terraform/modules/main/build_user.tf @@ -0,0 +1,102 @@ +resource "aws_iam_user" "build_user" { + name = var.project_name + path = "/tf-pipeline/${var.environment}/" + tags = { + Project_Name = var.project_name + Environment = var.environment + Account_ID = local.account_id + Region = local.region + } +} + +data "aws_iam_policy_document" "codecommit_access" { + statement { + effect = "Allow" + actions = [ + "codecommit:GitPull", + "codecommit:GitPush", + "codecommit:GetBranch", + "codecommit:CreateCommit", + "codecommit:ListRepositories", + "codecommit:BatchGetCommits", + "codecommit:BatchGetRepositories", + "codecommit:GetCommit", + "codecommit:GetRepository", + "codecommit:GetUploadArchiveStatus", + "codecommit:ListBranches", + "codecommit:UploadArchive" + ] + resources = [ + "arn:${data.aws_partition.current.partition}:codecommit:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:${var.source_repo_name}", + var.ansible_repo.arn + ] + } +} + +data "aws_iam_policy_document" "packer_config" { + statement { + effect = "Allow" + actions = [ + "ec2:AttachVolume", + "ec2:AuthorizeSecurityGroupIngress", + "ec2:CopyImage", + "ec2:CreateImage", + "ec2:CreateKeyPair", + "ec2:CreateSecurityGroup", + "ec2:CreateSnapshot", + "ec2:CreateTags", + "ec2:CreateVolume", + "ec2:DeleteKeyPair", + "ec2:DeleteSecurityGroup", + "ec2:DeleteSnapshot", + "ec2:DeleteVolume", + "ec2:DeregisterImage", + "ec2:DescribeImageAttribute", + "ec2:DescribeImages", + "ec2:DescribeInstances", + "ec2:DescribeInstanceStatus", + "ec2:DescribeRegions", + "ec2:DescribeSecurityGroups", + "ec2:DescribeSnapshots", + "ec2:DescribeSubnets", + "ec2:DescribeTags", + "ec2:DescribeVolumes", + "ec2:DetachVolume", + "ec2:GetPasswordData", + "ec2:ModifyImageAttribute", + "ec2:ModifyInstanceAttribute", + "ec2:ModifySnapshotAttribute", + "ec2:RegisterImage", + "ec2:RunInstances", + "ec2:StopInstances", + "ec2:TerminateInstances" + ] + resources = ["*"] + } +} +resource "aws_iam_access_key" "build_user" { + user = aws_iam_user.build_user.name +} + +resource "aws_iam_user_policy" "build_user" { + for_each = tomap({ + build_permissions = var.build_permissions_iam_doc.json, + repo_permissions = data.aws_iam_policy_document.codecommit_access.json, + packer_permissions = data.aws_iam_policy_document.packer_config.json + }) + name = "${var.project_name}-build-user" + user = aws_iam_user.build_user.name + policy = each.value + lifecycle { + ignore_changes = [policy] + } +} + +resource "aws_secretsmanager_secret" "credentials" { + name = "${var.project_name}-aws-credentials" +} + +resource "aws_secretsmanager_secret_version" "credentials" { + secret_id = aws_secretsmanager_secret.credentials.id + secret_string = aws_iam_access_key.build_user.secret +} \ No newline at end of file diff --git a/.terraform/modules/main/data.tf b/.terraform/modules/main/data.tf new file mode 100644 index 0000000..e3b7f69 --- /dev/null +++ b/.terraform/modules/main/data.tf @@ -0,0 +1,15 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +data "aws_caller_identity" "current" {} +data "aws_region" "current" {} +data "aws_partition" "current" {} + + +locals { + arn_parts = split("/", data.aws_caller_identity.current.arn) + approver_role = "${local.arn_parts[0]}/${local.arn_parts[1]}" +} \ No newline at end of file diff --git a/.terraform/modules/main/examples/main.tf b/.terraform/modules/main/examples/main.tf new file mode 100644 index 0000000..29bdb55 --- /dev/null +++ b/.terraform/modules/main/examples/main.tf @@ -0,0 +1,29 @@ +data "aws_iam_policy_document" "s3_access" { + statement { + effect = "Allow" + actions = ["s3:*"] + resources = ["*"] + } +} + +locals { + example_build_variables = [ + { + name = "TF_VAR_greeting", + value = "Dave", + type = "PLAINTEXT" + } + ] +} + +module "main" { + source = "../" + project_name = "tf-hello-world" + environment = "dev" + source_repo_name = "terraform-sample-repo" + source_repo_branch = "main" + create_new_repo = true + create_new_role = true + build_permissions_iam_doc = data.aws_iam_policy_document.s3_access + build_environment_variables = local.example_build_variables +} diff --git a/.terraform/modules/main/locals.tf b/.terraform/modules/main/locals.tf new file mode 100644 index 0000000..157394c --- /dev/null +++ b/.terraform/modules/main/locals.tf @@ -0,0 +1,10 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +locals { + account_id = data.aws_caller_identity.current.account_id + region = data.aws_region.current.name +} \ No newline at end of file diff --git a/.terraform/modules/main/main.tf b/.terraform/modules/main/main.tf new file mode 100644 index 0000000..9ff780a --- /dev/null +++ b/.terraform/modules/main/main.tf @@ -0,0 +1,157 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +terraform { + required_version = ">= 1.0.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.20.1" + } + } + +} + +#Module for creating a new S3 bucket for storing pipeline artifacts +module "s3_artifacts_bucket" { + source = "./modules/s3" + project_name = var.project_name + kms_key_arn = module.codepipeline_kms.arn + codepipeline_role_arn = module.codepipeline_iam_role.role_arn + tags = { + Project_Name = var.project_name + Environment = var.environment + Account_ID = local.account_id + Region = local.region + } +} + +# Resources + +# Module for Infrastructure Source code repository +module "codecommit_infrastructure_source_repo" { + source = "./modules/codecommit" + + create_new_repo = var.create_new_repo + source_repository_name = var.source_repo_name + source_repository_branch = var.source_repo_branch + repo_approvers_arn = local.approver_role + kms_key_arn = module.codepipeline_kms.arn + tags = { + Project_Name = var.project_name + Environment = var.environment + Account_ID = local.account_id + Region = local.region + } + +} + + +module "codepipeline_kms" { + source = "./modules/kms" + codepipeline_role_arn = module.codepipeline_iam_role.role_arn + tags = { + Project_Name = var.project_name + Environment = var.environment + Account_ID = local.account_id + Region = local.region + } +} + +# Module for Infrastructure Validation - CodeBuild +module "codebuild_terraform" { + depends_on = [ + module.codecommit_infrastructure_source_repo + ] + source = "./modules/codebuild" + + project_name = var.project_name + environment = var.environment + role_arn = module.codepipeline_iam_role.role_arn + s3_bucket_name = module.s3_artifacts_bucket.bucket + build_projects = var.build_projects + build_project_source = var.build_project_source + test_project_source = var.test_project_source + builder_compute_type = var.builder_compute_type + builder_image = var.builder_image + builder_image_pull_credentials_type = var.builder_image_pull_credentials_type + builder_type = var.builder_type + vpc_config = local.vpc_config + terraform_version = var.terraform_version + state = var.state + environment_variables = concat( + var.build_environment_variables, + var.vpc_config != null ? [ + { + name = "PKR_VAR_security_group_id", + value = element(var.vpc_config.security_group_ids, 0), + type = "PLAINTEXT" + }, + { + name = "PKR_VAR_subnet_id", + value = element(var.vpc_config.subnets, 0), + type = "PLAINTEXT" + } + ] : [] + ) + kms_key_arn = module.codepipeline_kms.arn + + tags = { + Project_Name = var.project_name + Environment = var.environment + Account_ID = local.account_id + Region = local.region + } +} + +module "codepipeline_iam_role" { + source = "./modules/iam-role" + project_name = var.project_name + environment = var.environment + create_new_role = var.create_new_role + codepipeline_iam_role_name = var.create_new_role == true ? "${var.project_name}-codepipeline-role" : var.codepipeline_iam_role_name + source_repository_name = var.source_repo_name + ansible_repo = var.ansible_repo + goss_repo = var.goss_repo + kms_key_arn = module.codepipeline_kms.arn + s3_bucket_arn = module.s3_artifacts_bucket.arn + credentials_secret_arn = aws_secretsmanager_secret.credentials.arn + vpc_config = local.vpc_config + state = var.state + tags = { + Project_Name = var.project_name + Environment = var.environment + Account_ID = local.account_id + Region = local.region + } +} + + +# Module for Infrastructure Validate, Plan, Apply and Destroy - CodePipeline +module "codepipeline_terraform" { + depends_on = [ + module.codebuild_terraform, + module.s3_artifacts_bucket + ] + source = "./modules/codepipeline" + + project_name = var.project_name + source_repo_name = var.source_repo_name + source_repo_branch = var.source_repo_branch + ansible_repo = var.ansible_repo + goss_repo = var.goss_repo + s3_bucket_name = module.s3_artifacts_bucket.bucket + codepipeline_role_arn = module.codepipeline_iam_role.role_arn + stages = var.stage_input + kms_key_arn = module.codepipeline_kms.arn + tags = { + Project_Name = var.project_name + Environment = var.environment + Account_ID = local.account_id + Region = local.region + } +} diff --git a/.terraform/modules/main/modules/codebuild/.terraform.lock.hcl b/.terraform/modules/main/modules/codebuild/.terraform.lock.hcl new file mode 100644 index 0000000..6571324 --- /dev/null +++ b/.terraform/modules/main/modules/codebuild/.terraform.lock.hcl @@ -0,0 +1,24 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.53.0" + hashes = [ + "h1:6u5Z28ArVnKsadP+ZRQYWPM4kNtTF7OZv7ZLzT2LBDc=", + "zh:2adad39412111d19a5195474d6b95577fc25ccf06d88a90019bee0efba33a1e3", + "zh:51226453a14f95b0d1163cfecafc9cf1a92ce5f66e42e6b4065d83a813836a2c", + "zh:62450fadb56db9c18d50bb8b7728a3d009be608d7ee0d4fe95c85ccb521dff83", + "zh:6f3ad977a9cc4800847c136690b1c0a0fd8437705062163d29dc4e9429598950", + "zh:71ca0a16b735b8d34b7127dd7d1e1e5d1eaac9c9f792e08abde291b5beb947d5", + "zh:7ae9cf4838eea80288305be0a3e69b39ffff86ede7b4319be421f06d32d04fb6", + "zh:93abc2db5ad995cfee014eb7446abc7caedc427e141d375a11993e6e199076b5", + "zh:9560b3424d97da804e98ee86b474b7370afefa09baf350cae7f33afb3f1aa209", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9eb57a9b649c217ac4eeb27af2a1935c18bd9bc8fb1be07434e7de74729eff46", + "zh:b5f32dcbe71ea22c2090eeeaec9af3e098d7b8c3e4491f34ffdfdc6f1c1abf81", + "zh:c9fbd5417f266c773055178e87bb4091df7f0542b72bf5ad0a4ae27045a2b7ca", + "zh:d518b3c52c8a9f79769dbe1b3683d25b4cdc8bfc77a3b3cd9c85f74e6c7383e1", + "zh:db741be21f32404bb87d73d25b1b7fd9b813b00aeb20a130ed8806d44dc26680", + "zh:ed1a8bb4d08653d87265ae534d6fc33bbdabae1608692a1ee364fce03548d36c", + ] +} diff --git a/.terraform/modules/main/modules/codebuild/README.md b/.terraform/modules/main/modules/codebuild/README.md new file mode 100644 index 0000000..23fff37 --- /dev/null +++ b/.terraform/modules/main/modules/codebuild/README.md @@ -0,0 +1,45 @@ + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_codebuild_project.terraform_codebuild_project](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codebuild_project) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [build\_project\_source](#input\_build\_project\_source) | Information about the build output artifact location | `string` | n/a | yes | +| [build\_projects](#input\_build\_projects) | List of Names of the CodeBuild projects to be created | `list(string)` | n/a | yes | +| [builder\_compute\_type](#input\_builder\_compute\_type) | Information about the compute resources the build project will use | `string` | n/a | yes | +| [builder\_image](#input\_builder\_image) | Docker image to use for the build project | `string` | n/a | yes | +| [builder\_image\_pull\_credentials\_type](#input\_builder\_image\_pull\_credentials\_type) | Type of credentials AWS CodeBuild uses to pull images in your build. | `string` | n/a | yes | +| [builder\_type](#input\_builder\_type) | Type of build environment to use for related builds | `string` | n/a | yes | +| [kms\_key\_arn](#input\_kms\_key\_arn) | ARN of KMS key for encryption | `string` | n/a | yes | +| [project\_name](#input\_project\_name) | Unique name for this project | `string` | n/a | yes | +| [role\_arn](#input\_role\_arn) | Codepipeline IAM role arn. | `string` | `""` | no | +| [s3\_bucket\_name](#input\_s3\_bucket\_name) | Name of the S3 bucket used to store the deployment artifacts | `string` | n/a | yes | +| [tags](#input\_tags) | Tags to be applied to the codebuild project | `map(any)` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [arn](#output\_arn) | List of ARNs of the CodeBuild projects | +| [id](#output\_id) | List of IDs of the CodeBuild projects | +| [name](#output\_name) | List of Names of the CodeBuild projects | + \ No newline at end of file diff --git a/.terraform/modules/main/modules/codebuild/main.tf b/.terraform/modules/main/modules/codebuild/main.tf new file mode 100644 index 0000000..1b0a668 --- /dev/null +++ b/.terraform/modules/main/modules/codebuild/main.tf @@ -0,0 +1,129 @@ +# Purpose: Create CodeBuild projects +locals { + buildspecs = { + build = "${path.module}/templates/buildspec_build.yml" + test = "${path.module}/templates/buildspec_test.yml" + } + + # This Terraform code block is creating a map of build projects using a for loop. + # It's iterating over the build_projects variable, which is expected to be a list of + # maps where each map represents a build project. + + # For each project, it checks the project's name: + # If the project's name is "build", it creates a map with the following keys: + # vars: This is a map that merges a predefined map (containing packer_version, mitogen_version, + # and packer_config) with the vars from the current project. + # environment_variables: This is a list that concatenates a predefined list of + # environment variables with the environment_variables from the current project. + # buildspec: This is set to a local value buildspec. + + # If the project's name is "test", it creates a map with the following keys: + # vars: This is set to the vars from the current project. + # environment_variables: This is a list that concatenates a predefined list of environment variables with the environment_variables from the current project. + # buildspec: This is set to "test_buildspec.yml". + + # If the project's name is neither "build" nor "test", it creates a map with the following keys: + # vars: This is set to the vars from the current project. + # environment_variables: This is a list that concatenates a predefined list of + # environment variables with the environment_variables from the current project. + # buildspec: This is set to the buildspec from the current project. + + # The result of this for loop is a map where each key is a project name and each + # value is a map with keys vars, environment_variables, and buildspec. + # This map is assigned to the build_projects local value. + build_projects = { for project in var.build_projects : (project.name) => + (project.name) == "build" ? { + vars = merge({ + packer_version = var.packer_version, + mitogen_version = var.mitogen_version, + packer_config = var.packer_config, + project_name = var.project_name, + environment = var.environment, + }, project.vars), + environment_variables = concat(var.environment_variables, project.environment_variables), + buildspec = lookup(local.buildspecs, project.name) + build_project_source = var.build_project_source + } : (project.name) == "test" ? { + vars = merge({ + project_name = var.project_name, + environment = var.environment, + terraform_version = var.terraform_version + }, project.vars) + environment_variables = concat(var.environment_variables, project.environment_variables), + buildspec = lookup(local.buildspecs, project.name) + build_project_source = var.test_project_source + } : { + vars = project.vars + environment_variables = concat(var.environment_variables, project.environment_variables), + buildspec = project.buildspec + build_project_source = project.project_source + } + } +} + + +resource "aws_codebuild_project" "terraform_codebuild_project" { + for_each = local.build_projects + name = "${var.project_name}-${each.key}" + service_role = var.role_arn + encryption_key = var.kms_key_arn + tags = var.tags + artifacts { + type = var.build_project_source + } + environment { + compute_type = var.builder_compute_type + image = var.builder_image + type = var.builder_type + privileged_mode = true + image_pull_credentials_type = var.builder_image_pull_credentials_type + dynamic "environment_variable" { + for_each = toset(lookup(each.value, "environment_variables", {})) + content { + name = environment_variable.value.name + value = environment_variable.value.value + type = environment_variable.value.type + } + } + } + logs_config { + cloudwatch_logs { + status = "ENABLED" + } + } + source { + type = var.build_project_source + # buildspec is set based on a conditional expression. + # The each.key != "test" condition checks if the current key in the iteration (provided by each.key) is not equal to "test". + # If each.key is not "test", it uses the templatefile function to render a template file specified by each.value.buildspec, + # with the variables specified by each.value.vars. + # If each.key is "test", it uses the templatefile function to render a template file specified by each.value.buildspec, + # with the variables specified by merging each.value.vars and a map containing state set to the value of the state variable. + buildspec = each.key != "test" ? templatefile( + each.value.buildspec, + each.value.vars + ) : templatefile( + each.value.buildspec, + merge( + each.value.vars, + { + state = merge(var.state, { bucket = var.s3_bucket_name }) + } + )) + } + + dynamic "vpc_config" { + for_each = var.vpc_config == null ? [] : ["*"] + content { + security_group_ids = var.vpc_config.security_group_ids + subnets = var.vpc_config.subnets + vpc_id = var.vpc_config.vpc_id + } + } + + lifecycle { + ignore_changes = [ + project_visibility + ] + } +} \ No newline at end of file diff --git a/.terraform/modules/main/modules/codebuild/outputs.tf b/.terraform/modules/main/modules/codebuild/outputs.tf new file mode 100644 index 0000000..af47dab --- /dev/null +++ b/.terraform/modules/main/modules/codebuild/outputs.tf @@ -0,0 +1,21 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +# output "id" { +# value = aws_codebuild_project.terraform_codebuild_project[*].id +# description = "List of IDs of the CodeBuild projects" +# } + +# output "name" { +# value = aws_codebuild_project.terraform_codebuild_project[*].name +# description = "List of Names of the CodeBuild projects" +# } + +# output "arn" { +# value = aws_codebuild_project.terraform_codebuild_project[*].arn +# description = "List of ARNs of the CodeBuild projects" +# } + diff --git a/.terraform/modules/main/modules/codebuild/templates/buildspec_build.yml b/.terraform/modules/main/modules/codebuild/templates/buildspec_build.yml new file mode 100644 index 0000000..6152d11 --- /dev/null +++ b/.terraform/modules/main/modules/codebuild/templates/buildspec_build.yml @@ -0,0 +1,44 @@ +version: 0.2 + +env: + variables: + CODE_SRC_DIR: "." + +phases: + install: + runtime-versions: + python: 3.11 + commands: + - stat $${CODEBUILD_SRC_DIR_SourceAnsibleOutput}/init.sh && bash $${CODEBUILD_SRC_DIR_SourceAnsibleOutput}/init.sh $${CODEBUILD_SRC_DIR_SourceAnsibleOutput} || echo "No init.sh script found" + - apt-get install -y jq curl unzip + - curl -s -qL -o mitogen.tar.gz https://files.pythonhosted.org/packages/source/m/mitogen/mitogen-${mitogen_version}.tar.gz + - mv mitogen.tar.gz /opt; cd /opt; tar vxzf mitogen.tar.gz + - curl -s -qL -o packer.zip https://releases.hashicorp.com/packer/${packer_version}/packer_${packer_version}_linux_amd64.zip + - unzip -o packer.zip + - mv packer /bin + - rm packer.zip + build: + commands: + - echo "Configuring AWS credentials" + - echo "Fetching AWS credentials from ECS metadata service" + - echo "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=$${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}" + - curl -qL -o aws_credentials.json http://169.254.170.2/$${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI} > aws_credentials.json + - aws configure set region $AWS_REGION + - aws configure set aws_access_key_id `jq -r '.AccessKeyId' aws_credentials.json` + - export AWS_ACCESS_KEY_ID=`jq -r '.AccessKeyId' aws_credentials.json` + - aws configure set aws_secret_access_key `jq -r '.SecretAccessKey' aws_credentials.json` + - export AWS_SECRET_ACCESS_KEY=`jq -r '.SecretAccessKey' aws_credentials.json` + - aws configure set aws_session_token `jq -r '.Token' aws_credentials.json` + - export AWS_SESSION_TOKEN=`jq -r '.Token' aws_credentials.json` + - echo "Building HashiCorp Packer template, ${packer_config}" + - cd $${CODEBUILD_SRC_DIR}/$${CODE_SRC_DIR} + - /bin/packer init ${packer_config} + - ln -s $${CODEBUILD_SRC_DIR_SourceAnsibleOutput}/roles + - /bin/packer build -var project_name=${project_name} -var environment=${environment} -var ansible_roles=$${CODEBUILD_SRC_DIR_SourceAnsibleOutput}/roles ${packer_config} + post_build: + commands: + - cd $${CODEBUILD_SRC_DIR}/$${CODE_SRC_DIR} + - test -s ami_id.txt || exit 1 +artifacts: + files: + - '**/*' \ No newline at end of file diff --git a/.terraform/modules/main/modules/codebuild/templates/buildspec_test.yml b/.terraform/modules/main/modules/codebuild/templates/buildspec_test.yml new file mode 100644 index 0000000..15acc6f --- /dev/null +++ b/.terraform/modules/main/modules/codebuild/templates/buildspec_test.yml @@ -0,0 +1,44 @@ +version: 0.2 + +env: + variables: + CODE_SRC_DIR: "." + +phases: + install: + runtime-versions: + python: 3.11 + commands: + - stat $${CODEBUILD_SRC_DIR_SourceAnsibleOutput}/init.sh && bash $${CODEBUILD_SRC_DIR_SourceAnsibleOutput}/init.sh $${CODEBUILD_SRC_DIR_SourceAnsibleOutput} || echo "No init.sh script found" + - apt-get install -y jq curl unzip + - curl -s -qL -o terraform.zip https://releases.hashicorp.com/terraform/${terraform_version}/terraform_${terraform_version}_linux_amd64.zip + - unzip -o terraform.zip + - mv terraform /bin + - rm terraform.zip + build: + commands: + - echo "Configuring AWS credentials" + - echo "Fetching AWS credentials from ECS metadata service" + - echo "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=$${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}" + - curl -qL -o aws_credentials.json http://169.254.170.2/$${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI} > aws_credentials.json + - aws configure set region $AWS_REGION + - aws configure set aws_access_key_id `jq -r '.AccessKeyId' aws_credentials.json` + - export AWS_ACCESS_KEY_ID=`jq -r '.AccessKeyId' aws_credentials.json` + - aws configure set aws_secret_access_key `jq -r '.SecretAccessKey' aws_credentials.json` + - export AWS_SECRET_ACCESS_KEY=`jq -r '.SecretAccessKey' aws_credentials.json` + - aws configure set aws_session_token `jq -r '.Token' aws_credentials.json` + - export AWS_SESSION_TOKEN=`jq -r '.Token' aws_credentials.json` + - echo "Applying Terraform configuration template" + - cd $${CODEBUILD_SRC_DIR_SourceGossOutput} + + - /bin/terraform init -upgrade -backend-config="key=${state.key}" -backend-config="region=${state.region}" -backend-config="bucket=${state.bucket}" -backend-config="dynamodb_table=${state.dynamodb_table}" -backend-config="encrypt=true" + - /bin/terraform apply -var project_name=${project_name} -var environment=${environment} -var goss_directory=$${CODEBUILD_SRC_DIR_SourceGossOutput} -auto-approve + + post_build: + commands: + - cd $${CODEBUILD_SRC_DIR_SourceGossOutput} + - terraform destroy -var project_name=${project_name} -var environment=${environment} -var goss_directory=$${CODEBUILD_SRC_DIR_SourceGossOutput} -auto-approve + - test -f tf_ami_id.txt && aws ec2 deregister-image --image-id `cat tf_ami_id.txt` --region $AWS_REGION || echo "Tests passed, no AMI to deregister" +artifacts: + files: + - '**/*' \ No newline at end of file diff --git a/.terraform/modules/main/modules/codebuild/variables.tf b/.terraform/modules/main/modules/codebuild/variables.tf new file mode 100644 index 0000000..1353d7a --- /dev/null +++ b/.terraform/modules/main/modules/codebuild/variables.tf @@ -0,0 +1,133 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +variable "project_name" { + description = "Unique name for this project" + type = string +} + +variable "role_arn" { + description = "Codepipeline IAM role arn. " + type = string + default = "" +} + +variable "s3_bucket_name" { + description = "Name of the S3 bucket used to store the deployment artifacts" + type = string +} + +variable "tags" { + description = "Tags to be applied to the codebuild project" + type = map(any) +} + +variable "build_projects" { + description = "List of Names of the CodeBuild projects to be created" + type = list(object({ + name = string, + vars = optional(map(string), {}) + environment_variables = optional(list(object({ + name = string + value = string + type = string + })), []) + buildspec = optional(string) + project_source = optional(string) + })) +} + +variable "terraform_version" { + type = string +} + +variable "builder_compute_type" { + description = "Information about the compute resources the build project will use" + type = string +} + +variable "builder_image" { + description = "Docker image to use for the build project" + type = string + default = "happypathway/aws-codebuild-image-pipeline:latest" +} + +variable "builder_type" { + description = "Type of build environment to use for related builds" + type = string +} + +variable "builder_image_pull_credentials_type" { + description = "Type of credentials AWS CodeBuild uses to pull images in your build." + type = string +} + +variable "build_project_source" { + description = "Information about the build output artifact location" + type = string +} + +variable "test_project_source" { + description = "Information about the test output artifact location" + type = string +} + +variable "kms_key_arn" { + description = "ARN of KMS key for encryption" + type = string +} + + +variable "environment_variables" { + type = list(object({ + name = string + value = string + type = string + })) + default = [] +} + +variable "packer_version" { + type = string + description = "Packer CLI Version" + default = "1.10.3" +} + +variable "mitogen_version" { + type = string + description = "Packer CLI Version" + default = "0.3.7" +} + +variable "packer_config" { + type = string + description = "Name of Packer Config in Repo" + default = "build.pkr.hcl" +} + +variable "vpc_config" { + default = null + type = object({ + security_group_ids = list(string) + subnets = list(string) + vpc_id = string + }) +} + +variable "environment" { + type = string + description = "environment to deploy the codebuild project" +} + + +variable "state" { + type = object({ + bucket = string + key = string + region = string + dynamodb_table = string + }) +} \ No newline at end of file diff --git a/.terraform/modules/main/modules/codecommit/README.md b/.terraform/modules/main/modules/codecommit/README.md new file mode 100644 index 0000000..e97a1f5 --- /dev/null +++ b/.terraform/modules/main/modules/codecommit/README.md @@ -0,0 +1,43 @@ + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_codecommit_approval_rule_template.source_repository_approval](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codecommit_approval_rule_template) | resource | +| [aws_codecommit_approval_rule_template_association.source_repository_approval_association](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codecommit_approval_rule_template_association) | resource | +| [aws_codecommit_repository.source_repository](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codecommit_repository) | resource | +| [aws_codecommit_repository.existing_repository](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/codecommit_repository) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [create\_new\_repo](#input\_create\_new\_repo) | Flag for deciding if a new repository needs to be created | `bool` | `false` | no | +| [kms\_key\_arn](#input\_kms\_key\_arn) | Name of the project to be prefixed to create the s3 bucket | `string` | n/a | yes | +| [repo\_approvers\_arn](#input\_repo\_approvers\_arn) | ARN or ARN pattern for the IAM User/Role/Group etc that can be used for approving Pull Requests | `string` | n/a | yes | +| [source\_repository\_branch](#input\_source\_repository\_branch) | Branch of the Source CodeCommit repository used in pipeline | `string` | n/a | yes | +| [source\_repository\_name](#input\_source\_repository\_name) | Name of the Source CodeCommit repository used by the pipeline | `string` | n/a | yes | +| [tags](#input\_tags) | Tags to be attached to the source CodeCommit repository | `map(any)` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [arn](#output\_arn) | LList containing the arn of the CodeCommit repositories | +| [clone\_url\_http](#output\_clone\_url\_http) | List containing the clone url of the CodeCommit repositories | +| [repository\_name](#output\_repository\_name) | List containing the name of the CodeCommit repositories | + \ No newline at end of file diff --git a/.terraform/modules/main/modules/codecommit/data.tf b/.terraform/modules/main/modules/codecommit/data.tf new file mode 100644 index 0000000..f36bef8 --- /dev/null +++ b/.terraform/modules/main/modules/codecommit/data.tf @@ -0,0 +1,11 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +# To be used only in case of an Existing Repository +data "aws_codecommit_repository" "existing_repository" { + count = var.create_new_repo ? 0 : 1 + repository_name = var.source_repository_name +} diff --git a/.terraform/modules/main/modules/codecommit/main.tf b/.terraform/modules/main/modules/codecommit/main.tf new file mode 100644 index 0000000..1150c97 --- /dev/null +++ b/.terraform/modules/main/modules/codecommit/main.tf @@ -0,0 +1,36 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +resource "aws_codecommit_repository" "source_repository" { + count = var.create_new_repo ? 1 : 0 + repository_name = var.source_repository_name + default_branch = var.source_repository_branch + description = "Code Repository for hosting the terraform code and pipeline configuration files" + tags = var.tags +} +resource "aws_codecommit_approval_rule_template" "source_repository_approval" { + count = var.create_new_repo ? 1 : 0 + name = "${var.source_repository_name}-${var.source_repository_branch}-Rule" + description = "Approval rule template for enabling approval process" + + content = < +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_codepipeline.terraform_pipeline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codepipeline) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [codepipeline\_role\_arn](#input\_codepipeline\_role\_arn) | ARN of the codepipeline IAM role | `string` | n/a | yes | +| [kms\_key\_arn](#input\_kms\_key\_arn) | ARN of KMS key for encryption | `string` | n/a | yes | +| [project\_name](#input\_project\_name) | Unique name for this project | `string` | n/a | yes | +| [s3\_bucket\_name](#input\_s3\_bucket\_name) | S3 bucket name to be used for storing the artifacts | `string` | n/a | yes | +| [source\_repo\_branch](#input\_source\_repo\_branch) | Default branch in the Source repo for which CodePipeline needs to be configured | `string` | n/a | yes | +| [source\_repo\_name](#input\_source\_repo\_name) | Source repo name of the CodeCommit repository | `string` | n/a | yes | +| [stages](#input\_stages) | List of Map containing information about the stages of the CodePipeline | `list(map(any))` | n/a | yes | +| [tags](#input\_tags) | Tags to be attached to the CodePipeline | `map(any)` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [arn](#output\_arn) | The arn of the CodePipeline | +| [id](#output\_id) | The id of the CodePipeline | +| [name](#output\_name) | The name of the CodePipeline | + \ No newline at end of file diff --git a/.terraform/modules/main/modules/codepipeline/main.tf b/.terraform/modules/main/modules/codepipeline/main.tf new file mode 100644 index 0000000..a0bada7 --- /dev/null +++ b/.terraform/modules/main/modules/codepipeline/main.tf @@ -0,0 +1,100 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +resource "aws_codepipeline" "terraform_pipeline" { + + name = "${var.project_name}-pipeline" + role_arn = var.codepipeline_role_arn + tags = var.tags + + artifact_store { + location = var.s3_bucket_name + type = "S3" + encryption_key { + id = var.kms_key_arn + type = "KMS" + } + } + + stage { + name = "Source" + + action { + name = "Download-Source" + category = "Source" + owner = "AWS" + version = "1" + provider = "CodeCommit" + namespace = "SourceVariables" + output_artifacts = ["SourceOutput"] + run_order = 1 + + configuration = { + RepositoryName = var.source_repo_name + BranchName = var.source_repo_branch + PollForSourceChanges = "true" + } + } + + action { + name = "Download-Ansible-Roles" + category = "Source" + owner = "AWS" + version = "1" + provider = "CodeCommit" + namespace = "SourceAnsible" + output_artifacts = ["SourceAnsibleOutput"] + run_order = 1 + + configuration = { + RepositoryName = var.ansible_repo.name + BranchName = var.ansible_repo.branch + PollForSourceChanges = "true" + } + } + + action { + name = "Download-Goss-Testing-Suite" + category = "Source" + owner = "AWS" + version = "1" + provider = "CodeCommit" + namespace = "SourceGoss" + output_artifacts = ["SourceGossOutput"] + run_order = 1 + + configuration = { + RepositoryName = var.goss_repo.name + BranchName = var.goss_repo.branch + PollForSourceChanges = "true" + } + } + } + + dynamic "stage" { + for_each = var.stages + + content { + name = "Stage-${stage.value["name"]}" + action { + category = stage.value["category"] + name = "Action-${stage.value["name"]}" + owner = stage.value["owner"] + provider = stage.value["provider"] + input_artifacts = lookup(stage.value, "input_artifacts", "") != "" ? stage.value["input_artifacts"] : null + output_artifacts = lookup(stage.value, "output_artifacts", "") != "" ? stage.value["output_artifacts"] : null + version = "1" + run_order = index(var.stages, stage.value) + 2 + + configuration = { + ProjectName = stage.value["provider"] == "CodeBuild" ? "${var.project_name}-${stage.value["name"]}" : null + PrimarySource = "SourceOutput" + } + } + } + } + +} \ No newline at end of file diff --git a/.terraform/modules/main/modules/codepipeline/outputs.tf b/.terraform/modules/main/modules/codepipeline/outputs.tf new file mode 100644 index 0000000..3bbbf07 --- /dev/null +++ b/.terraform/modules/main/modules/codepipeline/outputs.tf @@ -0,0 +1,22 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +output "id" { + value = aws_codepipeline.terraform_pipeline.id + description = "The id of the CodePipeline" +} + +output "name" { + value = aws_codepipeline.terraform_pipeline.name + description = "The name of the CodePipeline" +} + +output "arn" { + value = aws_codepipeline.terraform_pipeline.arn + description = "The arn of the CodePipeline" +} + + diff --git a/.terraform/modules/main/modules/codepipeline/variables.tf b/.terraform/modules/main/modules/codepipeline/variables.tf new file mode 100644 index 0000000..c2c2b84 --- /dev/null +++ b/.terraform/modules/main/modules/codepipeline/variables.tf @@ -0,0 +1,74 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +variable "project_name" { + description = "Unique name for this project" + type = string +} + +variable "source_repo_name" { + description = "Source repo name of the CodeCommit repository" + type = string +} + +variable "source_repo_branch" { + description = "Default branch in the Source repo for which CodePipeline needs to be configured" + type = string +} + +variable "ansible_repo" { + type = object({ + clone_url_http = string, + arn = string, + name = optional(string, "image-pipeline-ansible-roles") + branch = optional(string, "main") + }) + description = "Source of Ansible Repo" +} + + +variable "goss_repo" { + type = object({ + clone_url_http = string, + arn = string, + name = optional(string, "image-pipeline-ansible-roles") + branch = optional(string, "main") + }) + description = "Source of Ansible Repo" +} + + +variable "s3_bucket_name" { + description = "S3 bucket name to be used for storing the artifacts" + type = string +} + +variable "codepipeline_role_arn" { + description = "ARN of the codepipeline IAM role" + type = string +} + +variable "kms_key_arn" { + description = "ARN of KMS key for encryption" + type = string +} + +variable "tags" { + description = "Tags to be attached to the CodePipeline" + type = map(any) +} + +variable "stages" { + description = "List of Map containing information about the stages of the CodePipeline" + type = list(object({ + name = string, + category = string, + owner = string, + provider = string, + input_artifacts = list(string), + output_artifacts = list(string) + })) +} diff --git a/.terraform/modules/main/modules/iam-role/README.md b/.terraform/modules/main/modules/iam-role/README.md new file mode 100644 index 0000000..345190b --- /dev/null +++ b/.terraform/modules/main/modules/iam-role/README.md @@ -0,0 +1,47 @@ + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_accessanalyzer_analyzer.codepipeline_analyzer](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/accessanalyzer_analyzer) | resource | +| [aws_iam_policy.codepipeline_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_role.codepipeline_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.codepipeline_role_attach](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_iam_role.existing_codepipeline_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_role) | data source | +| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | +| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [codepipeline\_iam\_role\_name](#input\_codepipeline\_iam\_role\_name) | Name of the IAM role to be used by the project | `string` | n/a | yes | +| [create\_new\_role](#input\_create\_new\_role) | Flag for deciding if a new role needs to be created | `bool` | `true` | no | +| [kms\_key\_arn](#input\_kms\_key\_arn) | ARN of KMS key for encryption | `string` | n/a | yes | +| [project\_name](#input\_project\_name) | Unique name for this project | `string` | n/a | yes | +| [s3\_bucket\_arn](#input\_s3\_bucket\_arn) | The ARN of the S3 Bucket | `string` | n/a | yes | +| [source\_repository\_name](#input\_source\_repository\_name) | Name of the Source CodeCommit repository | `string` | n/a | yes | +| [tags](#input\_tags) | Tags to be attached to the IAM Role | `map(any)` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [role\_arn](#output\_role\_arn) | The ARN of the IAM Role | +| [role\_name](#output\_role\_name) | The ARN of the IAM Role | + \ No newline at end of file diff --git a/.terraform/modules/main/modules/iam-role/data.tf b/.terraform/modules/main/modules/iam-role/data.tf new file mode 100644 index 0000000..712b5cd --- /dev/null +++ b/.terraform/modules/main/modules/iam-role/data.tf @@ -0,0 +1,15 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +data "aws_region" "current" {} +data "aws_caller_identity" "current" {} +data "aws_partition" "current" {} + +# To be used only in case of an Existing Repository +data "aws_iam_role" "existing_codepipeline_role" { + count = var.create_new_role ? 0 : 1 + name = var.codepipeline_iam_role_name +} \ No newline at end of file diff --git a/.terraform/modules/main/modules/iam-role/locals.tf b/.terraform/modules/main/modules/iam-role/locals.tf new file mode 100644 index 0000000..ba36726 --- /dev/null +++ b/.terraform/modules/main/modules/iam-role/locals.tf @@ -0,0 +1,3 @@ +locals { + role_arn = var.create_new_role ? aws_iam_role.codepipeline_role[0].arn : data.aws_iam_role.existing_codepipeline_role[0].arn +} \ No newline at end of file diff --git a/.terraform/modules/main/modules/iam-role/main.tf b/.terraform/modules/main/modules/iam-role/main.tf new file mode 100644 index 0000000..619c676 --- /dev/null +++ b/.terraform/modules/main/modules/iam-role/main.tf @@ -0,0 +1,194 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + + +data "aws_iam_policy_document" "codepipeline_assume_role" { + statement { + effect = "Allow" + actions = [ + "sts:AssumeRole" + ] + principals { + type = "Service" + identifiers = ["codepipeline.amazonaws.com"] + } + } + statement { + effect = "Allow" + actions = [ + "sts:AssumeRole" + ] + principals { + type = "Service" + identifiers = ["codebuild.amazonaws.com"] + } + } +} + +resource "aws_iam_role" "codepipeline_role" { + count = var.create_new_role ? 1 : 0 + name = var.codepipeline_iam_role_name + tags = var.tags + assume_role_policy = data.aws_iam_policy_document.codepipeline_assume_role.json + path = "/" +} + + + +data "aws_iam_policy_document" "codepipeline_policy" { + + statement { + effect = "Allow" + actions = [ + "secretsmanager:GetSecretValue" + ] + resources = [var.credentials_secret_arn] + } + + statement { + effect = "Allow" + actions = [ + "s3:*" + ] + resources = [ + "${var.s3_bucket_arn}/*", + "arn:${data.aws_partition.current.partition}:s3:::${var.state.bucket}/*" + ] + } + + statement { + effect = "Allow" + actions = [ + "ssm:*" + ] + resources = [ + "arn:${data.aws_partition.current.partition}:ssm:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:parameter/image-pipeline/${var.environment}/${var.project_name}/*" + ] + } + + statement { + effect = "Allow" + actions = [ + "kms:DescribeKey", + "kms:GenerateDataKey*", + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:Decrypt" + ] + resources = [ + var.kms_key_arn + ] + } + + statement { + effect = "Allow" + actions = [ + "ec2:ImportKeyPair" + ] + resources = [ + "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:key-pair/${var.project_name}-${var.environment}-deployer-key" + ] + } + statement { + effect = "Allow" + actions = [ + "codecommit:GitPull", + "codecommit:GitPush", + "codecommit:GetBranch", + "codecommit:CreateCommit", + "codecommit:ListRepositories", + "codecommit:BatchGetCommits", + "codecommit:BatchGetRepositories", + "codecommit:GetCommit", + "codecommit:GetRepository", + "codecommit:GetUploadArchiveStatus", + "codecommit:ListBranches", + "codecommit:UploadArchive" + ] + resources = [ + "arn:${data.aws_partition.current.partition}:codecommit:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:${var.source_repository_name}", + var.ansible_repo.arn, + var.goss_repo.arn + ] + } + + statement { + effect = "Allow" + actions = [ + "codebuild:BatchGetBuilds", + "codebuild:StartBuild", + "codebuild:BatchGetProjects" + ] + resources = [ + "arn:${data.aws_partition.current.partition}:codebuild:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:project/${var.project_name}*" + ] + } + + statement { + effect = "Allow" + actions = [ + "codebuild:CreateReportGroup", + "codebuild:CreateReport", + "codebuild:UpdateReport", + "codebuild:BatchPutTestCases" + ] + resources = [ + "arn:${data.aws_partition.current.partition}:codebuild:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:report-group/${var.project_name}*" + ] + } + + statement { + effect = "Allow" + actions = [ + "dynamodb:*", + ] + resources = [ + "arn:${data.aws_partition.current.partition}:dynamodb:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:table/${var.state.dynamodb_table}" + ] + } + + statement { + effect = "Allow" + actions = [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ] + resources = [ + "arn:${data.aws_partition.current.partition}:logs:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:log-group:*" + ] + } + + statement { + effect = "Allow" + actions = [ + "ec2:*" + ] + resources = ["*"] + } +} + +# TO-DO : replace all * with resource names / arn +resource "aws_iam_policy" "codepipeline_policy" { + count = var.create_new_role ? 1 : 0 + name = "${var.project_name}-codepipeline-policy" + description = "Policy to allow codepipeline to execute" + tags = var.tags + policy = data.aws_iam_policy_document.codepipeline_policy.json +} + +resource "aws_iam_role_policy_attachment" "codepipeline_role_attach" { + count = var.create_new_role ? 1 : 0 + role = one(aws_iam_role.codepipeline_role).name + policy_arn = one(aws_iam_policy.codepipeline_policy).arn +} + +# aws_iam_policy" "vpc_config" +resource "aws_iam_role_policy_attachment" "codepipeline_vpc_role_attach" { + count = var.vpc_config == null ? 0 : 1 + role = one(aws_iam_role.codepipeline_role).name + policy_arn = one(aws_iam_policy.vpc_config).arn +} diff --git a/.terraform/modules/main/modules/iam-role/outputs.tf b/.terraform/modules/main/modules/iam-role/outputs.tf new file mode 100644 index 0000000..08f76c9 --- /dev/null +++ b/.terraform/modules/main/modules/iam-role/outputs.tf @@ -0,0 +1,15 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +output "role_arn" { + value = local.role_arn + description = "The ARN of the IAM Role" +} + +output "role_name" { + value = var.create_new_role ? aws_iam_role.codepipeline_role[0].name : data.aws_iam_role.existing_codepipeline_role[0].name + description = "The ARN of the IAM Role" +} diff --git a/.terraform/modules/main/modules/iam-role/variables.tf b/.terraform/modules/main/modules/iam-role/variables.tf new file mode 100644 index 0000000..2bf1986 --- /dev/null +++ b/.terraform/modules/main/modules/iam-role/variables.tf @@ -0,0 +1,94 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +variable "source_repository_name" { + type = string + description = "Name of the Source CodeCommit repository" +} + +variable "project_name" { + description = "Unique name for this project" + type = string +} + +variable "environment" { + description = "Environment for the project" + type = string +} + +variable "codepipeline_iam_role_name" { + description = "Name of the IAM role to be used by the project" + type = string +} + +variable "tags" { + description = "Tags to be attached to the IAM Role" + type = map(any) +} + +variable "kms_key_arn" { + description = "ARN of KMS key for encryption" + type = string +} + +variable "s3_bucket_arn" { + description = "The ARN of the S3 Bucket" + type = string +} + +variable "credentials_secret_arn" { + description = "The ARN of the AWS Secrets Manager credentials" +} + +variable "create_new_role" { + type = bool + description = "Flag for deciding if a new role needs to be created" + default = true +} + + + +variable "ansible_repo" { + type = object({ + clone_url_http = string, + arn = string, + name = optional(string, "image-pipeline-ansible-roles") + branch = optional(string, "main") + }) + description = "Source of Ansible Repo" +} + + +variable "goss_repo" { + type = object({ + clone_url_http = string, + arn = string, + name = optional(string, "image-pipeline-ansible-roles") + branch = optional(string, "main") + }) + description = "Source of Ansible Repo" +} + + + + +variable "vpc_config" { + default = null + type = object({ + security_group_ids = list(string) + subnets = list(string) + vpc_id = string + }) +} + +variable "state" { + type = object({ + bucket = string + key = string + region = string + dynamodb_table = string + }) +} \ No newline at end of file diff --git a/.terraform/modules/main/modules/iam-role/vpc_access.tf b/.terraform/modules/main/modules/iam-role/vpc_access.tf new file mode 100644 index 0000000..dda467f --- /dev/null +++ b/.terraform/modules/main/modules/iam-role/vpc_access.tf @@ -0,0 +1,41 @@ +resource "aws_iam_policy" "vpc_config" { + count = var.vpc_config == null ? 0 : 1 + name = "${var.project_name}-vpc-access" + path = "/" + description = "${var.project_name}-vpc-access" + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow" + Action = [ + "ec2:CreateNetworkInterface", + "ec2:DescribeDhcpOptions", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + "ec2:DescribeVpcs" + ] + Resource = "*" + }, + { + Effect = "Allow" + Action = [ + "ec2:CreateNetworkInterfacePermission" + ] + Resource = "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:network-interface/*", + Condition = { + StringEquals = { + "ec2:AuthorizedService" : "codebuild.amazonaws.com" + } + ArnEquals = { + "ec2:Subnet" : [ + for subnet in var.vpc_config.subnets : "arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:subnet/${subnet}" + ] + } + } + } + ] + }) +} diff --git a/.terraform/modules/main/modules/kms/README.md b/.terraform/modules/main/modules/kms/README.md new file mode 100644 index 0000000..72c8708 --- /dev/null +++ b/.terraform/modules/main/modules/kms/README.md @@ -0,0 +1,37 @@ + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_kms_key.encryption_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_iam_policy_document.kms_key_policy_doc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [codepipeline\_role\_arn](#input\_codepipeline\_role\_arn) | ARN of the codepipeline IAM role | `string` | n/a | yes | +| [tags](#input\_tags) | Tags to be attached to the KMS Key | `map(any)` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [arn](#output\_arn) | The ARN of the KMS key | + \ No newline at end of file diff --git a/.terraform/modules/main/modules/kms/data.tf b/.terraform/modules/main/modules/kms/data.tf new file mode 100644 index 0000000..402f47a --- /dev/null +++ b/.terraform/modules/main/modules/kms/data.tf @@ -0,0 +1,13 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +data "aws_caller_identity" "current" {} +data "aws_region" "current" {} + +locals { + arn_parts = split("/", data.aws_caller_identity.current.arn) + approver_role = "${local.arn_parts[0]}/${local.arn_parts[1]}" +} \ No newline at end of file diff --git a/.terraform/modules/main/modules/kms/main.tf b/.terraform/modules/main/modules/kms/main.tf new file mode 100644 index 0000000..44c57d7 --- /dev/null +++ b/.terraform/modules/main/modules/kms/main.tf @@ -0,0 +1,94 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +locals { + account_id = data.aws_caller_identity.current.account_id +} + +resource "aws_kms_key" "encryption_key" { + description = "This key is used to encrypt bucket objects" + deletion_window_in_days = 10 + # policy = data.aws_iam_policy_document.kms_key_policy_doc.json + enable_key_rotation = true + tags = var.tags +} + +data "aws_iam_policy_document" "kms_key_policy_doc" { + statement { + sid = "Enable IAM User Permissions" + effect = "Allow" + actions = ["kms:*"] + #checkov:skip=CKV_AWS_111:Without this statement, KMS key cannot be managed by root + #checkov:skip=CKV_AWS_109:Without this statement, KMS key cannot be managed by root + resources = ["*"] + + principals { + type = "AWS" + identifiers = [ + "arn:${var.partition}:iam::${local.account_id}:root", + data.aws_caller_identity.current.arn + ] + } + } + + statement { + sid = "Allow access for Key Administrators" + effect = "Allow" + actions = ["kms:*"] + resources = ["*"] + + principals { + type = "AWS" + identifiers = [ + var.codepipeline_role_arn + ] + } + } + + statement { + sid = "Allow use of the key" + effect = "Allow" + actions = [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:DescribeKey" + ] + resources = ["*"] + + principals { + type = "AWS" + identifiers = [ + var.codepipeline_role_arn + ] + } + } + + statement { + sid = "Allow attachment of persistent resources" + effect = "Allow" + actions = [ + "kms:CreateGrant", + "kms:ListGrants", + "kms:RevokeGrant" + ] + resources = ["*"] + + principals { + type = "AWS" + identifiers = [ + var.codepipeline_role_arn + ] + } + + condition { + test = "Bool" + variable = "kms:GrantIsForAWSResource" + values = ["true"] + } + } +} diff --git a/.terraform/modules/main/modules/kms/outputs.tf b/.terraform/modules/main/modules/kms/outputs.tf new file mode 100644 index 0000000..09901cc --- /dev/null +++ b/.terraform/modules/main/modules/kms/outputs.tf @@ -0,0 +1,14 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +output "arn" { + value = aws_kms_key.encryption_key.arn + description = "The ARN of the KMS key" +} + +output "key" { + value = aws_kms_key.encryption_key +} \ No newline at end of file diff --git a/.terraform/modules/main/modules/kms/variables.tf b/.terraform/modules/main/modules/kms/variables.tf new file mode 100644 index 0000000..2c43118 --- /dev/null +++ b/.terraform/modules/main/modules/kms/variables.tf @@ -0,0 +1,19 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +variable "tags" { + description = "Tags to be attached to the KMS Key" + type = map(any) +} + +variable "codepipeline_role_arn" { + description = "ARN of the codepipeline IAM role" + type = string +} + +variable "partition" { + default = "aws-us-gov" +} \ No newline at end of file diff --git a/.terraform/modules/main/modules/s3/README.md b/.terraform/modules/main/modules/s3/README.md new file mode 100644 index 0000000..3cf516d --- /dev/null +++ b/.terraform/modules/main/modules/s3/README.md @@ -0,0 +1,58 @@ + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | n/a | +| [aws.replication](#provider\_aws.replication) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_policy.replication_s3_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_role.replication_s3_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.replication_s3_role_attach](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_s3_bucket.codepipeline_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | +| [aws_s3_bucket.replication_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | +| [aws_s3_bucket_acl.codepipeline_bucket_acl](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource | +| [aws_s3_bucket_acl.replication_bucket_acl](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource | +| [aws_s3_bucket_logging.codepipeline_bucket_logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource | +| [aws_s3_bucket_logging.replication_bucket_logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource | +| [aws_s3_bucket_policy.bucket_policy_codepipeline_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource | +| [aws_s3_bucket_policy.bucket_policy_replication_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource | +| [aws_s3_bucket_public_access_block.codepipeline_bucket_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | +| [aws_s3_bucket_public_access_block.replication_bucket_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | +| [aws_s3_bucket_replication_configuration.replication_config](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_replication_configuration) | resource | +| [aws_s3_bucket_server_side_encryption_configuration.codepipeline_bucket_encryption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | +| [aws_s3_bucket_server_side_encryption_configuration.replication_bucket_encryption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | +| [aws_s3_bucket_versioning.codepipeline_bucket_versioning](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | +| [aws_s3_bucket_versioning.replication_bucket_versioning](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | +| [aws_iam_policy_document.bucket_policy_doc_codepipeline_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.bucket_policy_doc_replication_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [codepipeline\_role\_arn](#input\_codepipeline\_role\_arn) | ARN of the codepipeline IAM role | `string` | n/a | yes | +| [kms\_key\_arn](#input\_kms\_key\_arn) | ARN of KMS key for encryption | `string` | n/a | yes | +| [project\_name](#input\_project\_name) | Name of the project to be prefixed to create the s3 bucket | `string` | n/a | yes | +| [tags](#input\_tags) | Tags to be associated with the S3 bucket | `map(any)` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [arn](#output\_arn) | The ARN of the S3 Bucket | +| [bucket](#output\_bucket) | The Name of the S3 Bucket | +| [bucket\_url](#output\_bucket\_url) | The URL of the S3 Bucket | + \ No newline at end of file diff --git a/.terraform/modules/main/modules/s3/main.tf b/.terraform/modules/main/modules/s3/main.tf new file mode 100644 index 0000000..f864a43 --- /dev/null +++ b/.terraform/modules/main/modules/s3/main.tf @@ -0,0 +1,253 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +# resource "aws_iam_role" "replication_s3_role" { +# name = "${var.project_name}-replication-role" + +# assume_role_policy = < ami_id.txt", + "echo 'AMI ID: {{.BuildName}}'" + ] + } +} \ No newline at end of file diff --git a/.terraform/modules/main/sec-group.tf b/.terraform/modules/main/sec-group.tf new file mode 100644 index 0000000..7702e07 --- /dev/null +++ b/.terraform/modules/main/sec-group.tf @@ -0,0 +1,49 @@ +resource "aws_security_group" "packer" { + count = var.vpc_config == null ? 0 : 1 + name = "${var.project_name}-packer-builder" + description = "Packer Network Access" + vpc_id = var.vpc_config.vpc_id + tags = { + Name = "packer_builder" + } +} + +resource "aws_security_group_rule" "sg_rule" { + count = var.vpc_config == null ? 0 : 1 + type = "ingress" + from_port = 0 + to_port = 65535 + protocol = "tcp" + security_group_id = one(aws_security_group.packer).id + source_security_group_id = one(aws_security_group.packer).id +} + +resource "aws_vpc_security_group_egress_rule" "allow_all_traffic_ipv4" { + count = var.vpc_config == null ? 0 : 1 + security_group_id = one(aws_security_group.packer).id + cidr_ipv4 = "0.0.0.0/0" + ip_protocol = "-1" # semantically equivalent to all ports +} + +resource "aws_vpc_security_group_ingress_rule" "allow_all_ssh_ipv4" { + count = var.vpc_config == null ? 0 : 1 + security_group_id = one(aws_security_group.packer).id + cidr_ipv4 = "0.0.0.0/0" + ip_protocol = "-1" +} + +resource "aws_vpc_security_group_egress_rule" "allow_all_traffic_ipv6" { + count = var.vpc_config == null ? 0 : 1 + security_group_id = one(aws_security_group.packer).id + cidr_ipv6 = "::/0" + ip_protocol = "-1" # semantically equivalent to all ports +} + +locals { + vpc_config = var.vpc_config != null ? merge( + var.vpc_config, + { + security_group_ids = concat(var.vpc_config.security_group_ids, [one(aws_security_group.packer).id]) + } + ) : null +} \ No newline at end of file diff --git a/.terraform/modules/main/ssm.tf b/.terraform/modules/main/ssm.tf new file mode 100644 index 0000000..fb5ed1e --- /dev/null +++ b/.terraform/modules/main/ssm.tf @@ -0,0 +1,36 @@ +resource "aws_ssm_parameter" "security_group_id" { + name = "/image-pipeline/${var.environment}/${var.project_name}/security_group_ids" + type = "StringList" + value = join(",", local.vpc_config.security_group_ids) +} + +resource "aws_ssm_parameter" "region" { + name = "/image-pipeline/${var.environment}/${var.project_name}/region" + type = "String" + value = local.vpc_config.region +} + +resource "aws_ssm_parameter" "subnets" { + name = "/image-pipeline/${var.environment}/${var.project_name}/subnets" + type = "StringList" + value = join(",", local.vpc_config.subnets) +} + +resource "aws_ssm_parameter" "vpc_id" { + name = "/image-pipeline/${var.environment}/${var.project_name}/vpc_id" + type = "String" + value = local.vpc_config.vpc_id +} + +resource "aws_ssm_parameter" "ssh_user" { + name = "/image-pipeline/${var.environment}/${var.project_name}/ssh_user" + type = "String" + value = var.ssh_user +} + + +resource "aws_ssm_parameter" "goss_profile" { + name = "/image-pipeline/${var.environment}/${var.project_name}/goss_profile" + type = "String" + value = var.goss_profile +} \ No newline at end of file diff --git a/.terraform/modules/main/tmp_buildspec b/.terraform/modules/main/tmp_buildspec new file mode 100644 index 0000000..e69de29 diff --git a/.terraform/modules/main/variables.tf b/.terraform/modules/main/variables.tf new file mode 100644 index 0000000..13af77d --- /dev/null +++ b/.terraform/modules/main/variables.tf @@ -0,0 +1,226 @@ +#This solution, non-production-ready template describes AWS Codepipeline based CICD Pipeline for terraform code deployment. +#© 2023 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +#This AWS Content is provided subject to the terms of the AWS Customer Agreement available at +#http://aws.amazon.com/agreement or other written agreement between Customer and either +#Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. + +variable "project_name" { + description = "Unique name for this project" + type = string +} + +variable "create_new_repo" { + description = "Whether to create a new repository. Values are true or false. Defaulted to true always." + type = bool + default = true +} + +variable "create_new_role" { + description = "Whether to create a new IAM Role. Values are true or false. Defaulted to true always." + type = bool + default = true +} + +variable "codepipeline_iam_role_name" { + description = "Name of the IAM role to be used by the Codepipeline" + type = string + default = "codepipeline-role" +} + +variable "source_repo_name" { + description = "Source repo name of the CodeCommit repository" + type = string +} + +variable "source_repo_branch" { + description = "Default branch in the Source repo for which CodePipeline needs to be configured" + type = string +} + +# variable "repo_approvers_arn" { +# description = "ARN or ARN pattern for the IAM User/Role/Group that can be used for approving Pull Requests" +# type = string +# } + +variable "environment" { + description = "Environment in which the script is run. Eg: dev, prod, etc" + type = string +} + +variable "stage_input" { + description = "Tags to be attached to the CodePipeline" + type = list(object({ + name = string, + category = string, + owner = string, + provider = string, + input_artifacts = list(string), + output_artifacts = list(string) + })) + default = [ + { + name = "build", + category = "Build", + owner = "AWS", + provider = "CodeBuild", + input_artifacts = ["SourceOutput", "SourceAnsibleOutput"], + output_artifacts = ["BuildOutput"] + }, + { + name = "test", + category = "Build", + owner = "AWS", + provider = "CodeBuild", + input_artifacts = ["SourceOutput", "SourceGossOutput"], + output_artifacts = ["BuildTestOutput"] + }, + ] +} + + +variable "build_projects" { + description = "List of Names of the CodeBuild projects to be created" + type = list(object({ + name = string, + vars = optional(map(string), {}) + environment_variables = optional(list(object({ + name = string + value = string + type = string + })), []) + buildspec = optional(string) + })) + default = [ + { + name = "build" + }, + { + name = "test" + } + ] +} + +variable "builder_compute_type" { + description = "Relative path to the Apply and Destroy build spec file" + type = string + default = "BUILD_GENERAL1_SMALL" +} + +variable "builder_image" { + description = "Docker Image to be used by codebuild" + type = string + default = "aws/codebuild/amazonlinux2-x86_64-standard:3.0" +} + +variable "builder_type" { + description = "Type of codebuild run environment" + type = string + default = "LINUX_CONTAINER" +} + +variable "builder_image_pull_credentials_type" { + description = "Image pull credentials type used by codebuild project" + type = string + default = "CODEBUILD" +} + +variable "build_project_source" { + description = "Source Code Repo for Playbook" + type = string + default = "CODEPIPELINE" +} + +variable "test_project_source" { + description = "Source Code Repo for Goss Testing Suite" + type = string + default = "CODEPIPELINE" +} + +variable "build_environment_variables" { + type = list(object({ + name = string + value = string + type = optional(string, "PLAINTEXT") + })) + default = [] +} + +variable "packer_version" { + type = string + description = "Terraform CLI Version" + default = "1.10.3" +} + +variable "mitogen_version" { + type = string + description = "Mitogen Version" + default = "0.3.7" +} + +variable "packer_config" { + type = string + description = "Name of Packer Config in Repo" + default = "build.pkr.hcl" +} + +variable "build_permissions_iam_doc" { + type = any +} + + +variable "ansible_repo" { + type = object({ + clone_url_http = string, + arn = string, + name = optional(string, "image-pipeline-ansible-roles") + branch = optional(string, "main") + }) + description = "Source of Ansible Repo" +} + + +variable "goss_repo" { + type = object({ + clone_url_http = string, + arn = string, + name = optional(string, "image-pipeline-goss-testing") + branch = optional(string, "main") + }) + description = "Source of Goss Repo" +} + +variable "vpc_config" { + default = null + type = object({ + security_group_ids = list(string) + subnets = list(string) + vpc_id = string + region = string + }) +} + +variable "terraform_version" { + type = string + default = "1.3.10" +} + +variable "state" { + type = object({ + bucket = string + key = string + region = string + dynamodb_table = string + }) +} + +variable "ssh_user" { + type = string + description = "SSH username" + default = "ec2-user" +} + +variable "goss_profile" { + type = string + description = "GOSS Profile to be used for testing" + default = "goss" +} \ No newline at end of file diff --git a/.terraform/modules/main/vpc_config.tf b/.terraform/modules/main/vpc_config.tf new file mode 100644 index 0000000..d35b104 --- /dev/null +++ b/.terraform/modules/main/vpc_config.tf @@ -0,0 +1,13 @@ +resource "aws_vpc_endpoint" "endpoint" { + for_each = var.vpc_config == null ? toset([]) : toset([ + "codecommit", + "git-codecommit", + "s3" + ]) + vpc_id = local.vpc_config.vpc_id + service_name = "com.amazonaws.${data.aws_region.current.name}.${each.value}" + vpc_endpoint_type = "Interface" + + security_group_ids = local.vpc_config.security_group_ids + subnet_ids = local.vpc_config.subnets +} diff --git a/.terraform/modules/modules.json b/.terraform/modules/modules.json new file mode 100644 index 0000000..3debcde --- /dev/null +++ b/.terraform/modules/modules.json @@ -0,0 +1 @@ +{"Modules":[{"Key":"main.codepipeline_kms","Source":"./modules/kms","Dir":".terraform/modules/main/modules/kms"},{"Key":"main.codepipeline_terraform","Source":"./modules/codepipeline","Dir":".terraform/modules/main/modules/codepipeline"},{"Key":"main.s3_artifacts_bucket","Source":"./modules/s3","Dir":".terraform/modules/main/modules/s3"},{"Key":"main","Source":"registry.terraform.io/HappyPathway/image-pipeline/aws","Version":"0.0.155","Dir":".terraform/modules/main"},{"Key":"main.codebuild_terraform","Source":"./modules/codebuild","Dir":".terraform/modules/main/modules/codebuild"},{"Key":"main.codecommit_infrastructure_source_repo","Source":"./modules/codecommit","Dir":".terraform/modules/main/modules/codecommit"},{"Key":"","Source":"","Dir":"."},{"Key":"main.codepipeline_iam_role","Source":"./modules/iam-role","Dir":".terraform/modules/main/modules/iam-role"}]} \ No newline at end of file diff --git a/.terraform/providers/registry.terraform.io/hashicorp/aws/5.54.1/linux_amd64 b/.terraform/providers/registry.terraform.io/hashicorp/aws/5.54.1/linux_amd64 new file mode 120000 index 0000000..f65dd3c --- /dev/null +++ b/.terraform/providers/registry.terraform.io/hashicorp/aws/5.54.1/linux_amd64 @@ -0,0 +1 @@ +/data/terraform/workspaces/arnol377/terraform-plugin-cache/registry.terraform.io/hashicorp/aws/5.54.1/linux_amd64 \ No newline at end of file diff --git a/.terraform/providers/registry.terraform.io/hashicorp/aws/5.55.0/linux_amd64 b/.terraform/providers/registry.terraform.io/hashicorp/aws/5.55.0/linux_amd64 new file mode 120000 index 0000000..1f8fa86 --- /dev/null +++ b/.terraform/providers/registry.terraform.io/hashicorp/aws/5.55.0/linux_amd64 @@ -0,0 +1 @@ +/data/terraform/workspaces/arnol377/terraform-plugin-cache/registry.terraform.io/hashicorp/aws/5.55.0/linux_amd64 \ No newline at end of file diff --git a/.terraform/providers/registry.terraform.io/hashicorp/random/3.6.2/linux_amd64 b/.terraform/providers/registry.terraform.io/hashicorp/random/3.6.2/linux_amd64 new file mode 120000 index 0000000..1ce221e --- /dev/null +++ b/.terraform/providers/registry.terraform.io/hashicorp/random/3.6.2/linux_amd64 @@ -0,0 +1 @@ +/data/terraform/workspaces/arnol377/terraform-plugin-cache/registry.terraform.io/hashicorp/random/3.6.2/linux_amd64 \ No newline at end of file diff --git a/.terraform/terraform.tfstate b/.terraform/terraform.tfstate new file mode 100644 index 0000000..5a17fef --- /dev/null +++ b/.terraform/terraform.tfstate @@ -0,0 +1,52 @@ +{ + "version": 3, + "serial": 1, + "lineage": "0ec7fefb-3146-2c18-c987-572dfd68438e", + "backend": { + "type": "s3", + "config": { + "access_key": null, + "acl": null, + "assume_role_duration_seconds": null, + "assume_role_policy": null, + "assume_role_policy_arns": null, + "assume_role_tags": null, + "assume_role_transitive_tag_keys": null, + "bucket": "inf-tfstate-229685449397", + "dynamodb_endpoint": null, + "dynamodb_table": "tf_remote_state", + "encrypt": null, + "endpoint": null, + "external_id": null, + "force_path_style": null, + "iam_endpoint": null, + "key": "csvd-dev-gov/common/apps/aws-image-pipeline-demo", + "kms_key_id": null, + "max_retries": null, + "profile": null, + "region": "us-gov-east-1", + "role_arn": null, + "secret_key": null, + "session_name": null, + "shared_credentials_file": null, + "skip_credentials_validation": null, + "skip_metadata_api_check": null, + "skip_region_validation": null, + "sse_customer_key": null, + "sts_endpoint": null, + "token": null, + "workspace_key_prefix": null + }, + "hash": 748883980 + }, + "modules": [ + { + "path": [ + "root" + ], + "outputs": {}, + "resources": {}, + "depends_on": [] + } + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..c4195ac --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# aws-image-pipeline +Terraform Workspace for creating and managing AWS Image Pipelines diff --git a/backend.tf b/backend.tf new file mode 100644 index 0000000..7395341 --- /dev/null +++ b/backend.tf @@ -0,0 +1,8 @@ +terraform { + backend "s3" { + bucket = "inf-tfstate-229685449397" + key = "csvd-dev-gov/common/apps/aws-image-pipeline-demo" + region = "us-gov-east-1" + dynamodb_table = "tf_remote_state" + } +} diff --git a/examples/build.pkr.hcl b/examples/build.pkr.hcl new file mode 100644 index 0000000..6e0f9e3 --- /dev/null +++ b/examples/build.pkr.hcl @@ -0,0 +1,37 @@ +packer { + required_plugins { + amazon = { + version = ">= 1.2.8" + source = "github.com/hashicorp/amazon" + } + ansible = { + version = "v1.1.1" + source ="github.com/hashicorp/ansible" + } + } +} + +variable subnet_id {} +variable security_group_id {} + +source "amazon-ebs" "ubuntu" { + ami_name = "learn-packer-linux-aws" + instance_type = "t2.micro" + source_ami = "ami-07e218d88f8f9c4db" + ssh_username = "ec2-user" + subnet_id = var.subnet_id + security_group_ids = [ + var.security_group_id + ] +} + +build { + name = "learn-packer" + sources = [ + "source.amazon-ebs.ubuntu" + ] + provisioner "ansible" { + playbook_file = "./playbook.yml" + roles_path = "./ansible/roles" + } +} diff --git a/examples/playbook.yaml b/examples/playbook.yaml new file mode 100644 index 0000000..f7ccf5b --- /dev/null +++ b/examples/playbook.yaml @@ -0,0 +1,7 @@ +--- +# playbook.yml +- name: 'Provision Image' + hosts: default + become: true + roles: + - hello_world diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..2a37169 --- /dev/null +++ b/main.tf @@ -0,0 +1,115 @@ +locals { + proxy_env_vars = { + HTTP_PROXY = "http://proxy.tco.census.gov:3128" + NO_PROXY = ".census.gov,169.254.169.254,148.129.*,10.*,172.18.*,172.22.*,172.23.*,172.24.*,172.25.*,.eks.amazonaws.com,.s3.amazonaws.com,.amazonaws.com,.gcr.io,.pkg.dev" + HTTPS_PROXY = "http://proxy.tco.census.gov:3128" + } + source_repo = "image-pipeline-hello-world" + project_name = "image-pipeline-demo2" + environment = "dev" + _vpc_config = { + vpc_id = "vpc-00576a396ec570b94" + region = "us-gov-west-1" + security_group_ids = [ + "sg-0d828d223df9834a6" + ] + subnets = [ + # "subnet-0b1992a84536c581b" + "subnet-062189d742937204e" + ] + } + vpc_config = merge( + local._vpc_config, + { + security_group_ids = concat( + local._vpc_config.security_group_ids, + [ + aws_security_group.allow_amznlinux_cdn.id + ]) + } + ) + state_config = { + bucket = aws_s3_bucket.state_bucket.bucket + key = "csvd-dev-gov/common/apps/${local.environment}/${local.project_name}" + region = local.vpc_config.region + dynamodb_table = "tf_remote_state" + } +} + +resource random_uuid random {} +resource aws_s3_bucket state_bucket { + bucket = "inf-test-${random_uuid.random.result}" +} + +data "aws_iam_policy_document" "s3_access" { + statement { + effect = "Allow" + actions = ["s3:*"] + resources = ["*"] + } +} + +resource "aws_security_group" "allow_amznlinux_cdn" { + name = "allow_amznlinux_cdn" + description = "Allow TLS inbound traffic and all outbound traffic" + vpc_id = local._vpc_config.vpc_id + tags = { + Name = "allow_amznlinux_cdn" + } +} + +resource "aws_vpc_security_group_egress_rule" "allow_all_traffic_ipv4" { + security_group_id = aws_security_group.allow_amznlinux_cdn.id + cidr_ipv4 = "0.0.0.0/0" + ip_protocol = "-1" # semantically equivalent to all ports +} + +# referenced_security_group_id +resource "aws_vpc_security_group_ingress_rule" "allow_all_between_self" { + security_group_id = aws_security_group.allow_amznlinux_cdn.id + ip_protocol = "-1" # semantically equivalent to all ports + referenced_security_group_id = aws_security_group.allow_amznlinux_cdn.id +} + +data "aws_codecommit_repository" "ansible" { + repository_name = "image-pipeline-ansible-roles" +} + +data "aws_codecommit_repository" "goss" { + repository_name = "image-pipeline-goss-testing" +} + + +module "main" { + source = "HappyPathway/image-pipeline/aws" + project_name = local.project_name + environment = local.environment + source_repo_name = local.source_repo + source_repo_branch = "main" + builder_image = "aws/codebuild/standard:7.0" + create_new_repo = true + create_new_role = true + build_permissions_iam_doc = data.aws_iam_policy_document.s3_access + build_environment_variables = [ + for proxy_var in keys(local.proxy_env_vars) : + { + name=proxy_var, + value=lookup(local.proxy_env_vars, proxy_var), + type = "PLAINTEXT" + } + ] + ansible_repo = data.aws_codecommit_repository.ansible + goss_repo = data.aws_codecommit_repository.goss + goss_profile = "base-test" + state = local.state_config + vpc_config = local.vpc_config +} + +output iam_arn { + value = module.main.iam_arn +} + +output codebuild_user { + value = module.main.build_user.name +} + diff --git a/playbook.yml b/playbook.yml new file mode 100644 index 0000000..e69de29 diff --git a/terraform.tfstate b/terraform.tfstate new file mode 100644 index 0000000..e69de29 diff --git a/terraform.tfstate.backup b/terraform.tfstate.backup new file mode 100644 index 0000000..c8a2442 --- /dev/null +++ b/terraform.tfstate.backup @@ -0,0 +1,87 @@ +{ + "version": 4, + "terraform_version": "1.3.10", + "serial": 352, + "lineage": "13f4d701-1a18-9f7b-9a0c-fca719ec9492", + "outputs": {}, + "resources": [ + { + "module": "module.main", + "mode": "data", + "type": "aws_caller_identity", + "name": "current", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "account_id": "229685449397", + "arn": "arn:aws-us-gov:sts::229685449397:assumed-role/AWSReservedSSO_inf-admin-t2_4e0c6446aecbe4a0/david.j.arnold.jr@census.gov", + "id": "229685449397", + "user_id": "AROATK6SR2K22ZVVMKHBO:david.j.arnold.jr@census.gov" + }, + "sensitive_attributes": [] + } + ] + }, + { + "module": "module.main", + "mode": "data", + "type": "aws_region", + "name": "current", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "description": "AWS GovCloud (US-West)", + "endpoint": "ec2.us-gov-west-1.amazonaws.com", + "id": "us-gov-west-1", + "name": "us-gov-west-1" + }, + "sensitive_attributes": [] + } + ] + }, + { + "module": "module.main", + "mode": "managed", + "type": "aws_iam_user", + "name": "build_user", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws-us-gov:iam::229685449397:user/tf-pipeline/dev/HelloAMI", + "force_destroy": false, + "id": "HelloAMI", + "name": "HelloAMI", + "path": "/tf-pipeline/dev/", + "permissions_boundary": "", + "tags": { + "Account_ID": "229685449397", + "Environment": "dev", + "Project_Name": "HelloAMI", + "Region": "us-gov-west-1" + }, + "tags_all": { + "Account_ID": "229685449397", + "Environment": "dev", + "Project_Name": "HelloAMI", + "Region": "us-gov-west-1" + }, + "unique_id": "AIDATK6SR2K2R47PBYLRK" + }, + "sensitive_attributes": [], + "private": "bnVsbA==", + "dependencies": [ + "module.main.data.aws_caller_identity.current", + "module.main.data.aws_region.current" + ] + } + ] + } + ], + "check_results": null +}