diff --git a/.github/workflows/infra-plan.yaml b/.github/workflows/infra-plan.yaml new file mode 100644 index 0000000..e86da36 --- /dev/null +++ b/.github/workflows/infra-plan.yaml @@ -0,0 +1,20 @@ +name: Infrastructure-Plan + +on: + workflow_dispatch: + inputs: + environment: + description: "Environment to run tests against" + type: environment + required: true + +env: + TERRAFORM_VERSION: '1.5.7' + +jobs: + + terraform-plan: + runs-on: ubuntu-latest + + envrionment: + name: ${{ github.event.inputs.environment }} diff --git a/.gitignore b/.gitignore index 3e892a6..fee674c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ /.terraform/* terraform.tfvars *.swp +terratest_logs/ +infrastructure/.terraform/* +infrastructure/terraform.tfvars diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl deleted file mode 100644 index ca7e143..0000000 --- a/.terraform.lock.hcl +++ /dev/null @@ -1,151 +0,0 @@ -# This file is maintained automatically by "tofu init". -# Manual edits may be lost in future updates. - -provider "registry.opentofu.org/hashicorp/aws" { - version = "5.60.0" - constraints = ">= 4.33.0, ~> 5.0, >= 5.30.0, >= 5.58.0" - hashes = [ - "h1:E+BGAPXYIDlCXElj6i4l6VVpQ9AYD/+U+2I8opQQIl0=", - "zh:091f2c99d71f3caff7aa3c10accf86195f5b8d2f544da55439cf79019b99a057", - "zh:2f18891755dba03ccb0bdff2cd82a58952c1f207bbf4b0a642399c4c47395ea6", - "zh:36cea297b244e0060e5ae999c7e037f4da0eb09528670aad4965d18fad50da17", - "zh:3f53bea6e00db4d81deb71a865495a82bdc41f441913cd588c713df8409f2d26", - "zh:81455dba777b1445b83a6ee9c460fd59cc8d15de39a4ca1c2c947d7737384218", - "zh:91be3367eca3f84f4e07a96cd2d30492d0566ede3eebcbc697d83cb009ed0e78", - "zh:a302cfc831299c7555a87a88824cd0900d21723ab2042de76c9f10665dcec173", - "zh:b5e6bdf3d0bea884786babfaa4a1f45398477dbb2c7c1077b463caa6147d5ce6", - "zh:d9a71baf6bfc1ebbac9b14164b7a99bed6e61e73b1ee0f36de0bce9570537d04", - "zh:faf17028a25cc7aca490dcee6d88895619723ad81fb4d7904a9f1f6f82e4dff6", - ] -} - -provider "registry.opentofu.org/hashicorp/cloudinit" { - version = "2.3.4" - constraints = ">= 2.0.0" - hashes = [ - "h1:Y0kCXWjX7Oz9kwAKpYFXWRQVYFrOMvZYd/OkIUizLik=", - "h1:hTQwJ3PKuyS0mC/2YoQhm0s2QHLEKxo5w3AfvD/ntJA=", - "zh:316777a45891cb7dd987673fc84e60190f7d416d437287413f97abb3a2374647", - "zh:31a9f544d7cee4f4241898540758dd4b7273b2f4468ac1821fdd2cf9c7f73295", - "zh:48c068b8cafdae710ca68485463e1c514cfd2dc1e7ed744a5e47421f97276574", - "zh:4e2e2bd03ceb53129535285b3699b2dfe6d433e5574329ab4845beb6af82a3ca", - "zh:50aa62cb46475e064a52f1c91012d219b05ac65091b7dfa83fa0cfe2ac16a431", - "zh:87d44fd608fd1c12311604d9941b7d3ecb004f682ee75ce8daf628e5e6e1f084", - "zh:a027e7b0c2146c0ee343829ba2e1f3cab39fc3e366dfc03906190e79f18fc001", - "zh:c92ed7e8119668e5b22916110025714ed967c0bcffcd6660655efb145de2d227", - "zh:c9d49cbaee106f53c4bb42086e959158aa4b1f9682c0ece79db741220bc06e1c", - "zh:e7ecc5c15d257fe320c2a62e2245b5f064e53f370d96ac3ee0ef48b62f16fcbe", - ] -} - -provider "registry.opentofu.org/hashicorp/helm" { - version = "2.14.0" - constraints = "~> 2.14.0" - hashes = [ - "h1:9iakPuHCaTKv7isMpZmD93F0kw/Yd/1bcMI18YPMIuU=", - "h1:ibK3MM61pVjBwBcrro56OLTHwUhhNglvGG9CloLvliI=", - "zh:1c84ca8c274564c46497e89055139c7af64c9e1a8dd4f1cd4c68503ac1322fb8", - "zh:211a763173934d30c2e49c0cc828b1e34a528b0fdec8bf48d2bb3afadd4f9095", - "zh:3dca0b703a2f82d3e283a9e9ca6259a3b9897b217201f3cddf430009a1ca00c9", - "zh:40c5cfd48dcef54e87129e19d31c006c2e3309ee6c09d566139eaf315a59a369", - "zh:6f23c00ca1e2663e2a208a7491aa6dbe2604f00e0af7e23ef9323206e8f2fc81", - "zh:77f8cfc4888600e0d12da137bbdb836de160db168dde7af26c2e44cf00cbf057", - "zh:97b99c945eafa9bafc57c3f628d496356ea30312c3df8dfac499e0f3ff6bf0c9", - "zh:a01cfc53e50d5f722dc2aabd26097a8e4d966d343ffd471034968c2dc7a8819d", - "zh:b69c51e921fe8c91e38f4a82118d0b6b0f47f6c71a76f506fde3642ecbf39911", - "zh:fb8bfc7b8106bef58cc5628c024103f0dd5276d573fe67ac16f343a2b38ecee8", - ] -} - -provider "registry.opentofu.org/hashicorp/kubernetes" { - version = "2.31.0" - constraints = "~> 2.31.0" - hashes = [ - "h1:qqatdCju5nmc5IpGsuK92P0yGqzNbVl4S+Qdg+fBTYU=", - "h1:z2qlqn6WbrjbezwQo4vvlwAgVUGz59klzDU4rlYhYi8=", - "zh:0dd25babf78a88a61dd329b8c18538a295ea63630f1b69575e7898c89307da39", - "zh:3138753e4b2ce6e9ffa5d65d73e9236169ff077c10089c7dc71031a0a139ff6d", - "zh:644f94692dc33de0bb1183c307ae373efbf4ef4cb92654ccc646a5716edf9593", - "zh:6cc630e43193220b1599e3227286cc4e3ca195910e8c56b6bacb50c5b5176dbf", - "zh:764173875e77aa482da4dca9fec5f77c455d028848edfc394aa7dac5dfed6afd", - "zh:7b1d380362d50ffbb3697483036ae351b0571e93b33754255cde6968e62b839f", - "zh:a1d93ca3d8d1ecdd3b69242d16ff21c91b34e2e98f02a3b2d02c908aeb45189b", - "zh:b471d0ab56dbf19c95fba68d2ef127bdb353be96a2be4c4a3dcd4d0db4b4180a", - "zh:d610f725ded4acd3d31a240472bb283aa5e657ed020395bdefea18d094b8c2bf", - "zh:d7f3ddd636ad5af6049922f212feb24830b7158410819c32073bf81c359cd2fa", - ] -} - -provider "registry.opentofu.org/hashicorp/local" { - version = "2.5.1" - hashes = [ - "h1:Ur9dG6cz1z2yHLkkWmpCwL1naYYBB6nvnOdGJBADAG0=", - "zh:031c2c2070672b7e78e0aa15560839278dc57fe7cf1e58a617ac13c67b31d5fb", - "zh:1ef64ea4f8382cd538a76f3d319f405d18130dc3280f1c16d6aaa52a188ecaa4", - "zh:422ce45691b2f384dbd4596fdc8209d95cb43d85a82aaa0173089d38976d6e96", - "zh:7415fbd8da72d9363ba55dd8115837714f9534f5a9a518ec42268c2da1b9ed2f", - "zh:92aa22d071339c8ef595f18a9f9245c287266c80689f5746b26e10eaed04d542", - "zh:9cd0d99f5d3be835d6336c19c4057af6274e193e677ecf6370e5b0de12b4aafe", - "zh:a8c1525b389be5809a97f02aa7126e491ba518f97f57ed3095a3992f2134bb8f", - "zh:b336fa75f72643154b07c09b3968e417a41293358a54fe03efc0db715c5451e6", - "zh:c66529133599a419123ad2e42874afbd9aba82bd1de2b15cc68d2a1e665d4c8e", - "zh:c7568f75ba6cb7c3660b69eaab8b0e4278533bd9a7a4c33ee6590cc7e69743ea", - ] -} - -provider "registry.opentofu.org/hashicorp/null" { - version = "3.2.2" - constraints = ">= 3.0.0" - hashes = [ - "h1:P8+KlqxeTE3fNqzngzTxfwXFJaGl2Csw7lYJtFff508=", - "h1:xN1tSeF/rUBfaddk/AVqk4i65z/MMM9uVZWd2cWCCH0=", - "zh:00e5877d19fb1c1d8c4b3536334a46a5c86f57146fd115c7b7b4b5d2bf2de86d", - "zh:1755c2999e73e4d73f9de670c145c9a0dc5a373802799dff06a0e9c161354163", - "zh:2b29d706353bc9c4edda6a2946af3322abe94372ffb421d81fa176f1e57e33be", - "zh:34f65259c6d2bd51582b6da536e782b181b23725782b181193b965f519fbbacd", - "zh:370f6eb744475926a1fa7464d82d46ad83c2e1148b4b21681b4cec4d75b97969", - "zh:5950bdb23b4fcc6431562d7eba3dea37844aa4220c4da2eb898ae3e4d1b64ec4", - "zh:8f3d5c8d4b9d497fec36953a227f80c76d37fc8431b683a23fb1c42b9cccbf8a", - "zh:8f6eb5e65c047bf490ad3891efecefc488503b65898d4ee106f474697ba257d7", - "zh:a7040eed688316fe00379574c72bb8c47dbe2638b038bb705647cbf224de8f72", - "zh:e561f28df04d9e51b75f33004b7767a53c45ad96e3375d86181ba1363bffbc77", - ] -} - -provider "registry.opentofu.org/hashicorp/time" { - version = "0.12.0" - constraints = ">= 0.9.0" - hashes = [ - "h1:D47BKVFyfA5FiRq92ihD7wb2FlaARXoZpySqUbtXNLE=", - "h1:Om7xF0GgRkBsAjKis3RAFXQJKmHgnO04C+PEScF/xTM=", - "zh:01b7ac8203eb7ed712a356215e44f8851b96ddcfdf63b13ff9f870f799667059", - "zh:06c4420bdb964209eb119f1740575df7b8ac44a3b5d71631dae2962a155f58b7", - "zh:2534d1d04ca934e25426ab5bb0b29a57a95c676f70b154bfb382d58bf1e6f6c9", - "zh:340de6c71a1090f13ab5c429ca2134c12189e8b86c2b104859e82eb30eea9772", - "zh:561a2780f7fb1b0a9092c59c4eb3e3d8c3ec9cecddc9214ae92fdc941c3bd2e7", - "zh:65b1a982617375123bc3a1dcd44d61264cabac6b3d83378e7079ee0655ec6679", - "zh:9ae9f6c9609c5ed9e35a702068629ef5adfb131f957a571fc39ce0127c782ca4", - "zh:ad7f066c5db340683cb5a3a29ced3a2ece13c5b84c46d6b3d30815444a6c78ee", - "zh:f532d2c33c2303a970e9ee813e37d208eb65321aec489da14786b7f04ea66105", - "zh:fb269e2425a4b996fef79665eaeec8f40a388bf7ac7bf8ce2c108fb83c4b10ca", - ] -} - -provider "registry.opentofu.org/hashicorp/tls" { - version = "4.0.5" - constraints = ">= 3.0.0" - hashes = [ - "h1:ILGm1+RP2+eIDc+YQ+xWgNX7Dcb9cD9OuvJHqUxtjmE=", - "h1:zEH0OgSkeXDqNWzmOUWDczrUwyyujAHvnbW79qdxVMI=", - "zh:05a7dc3ac92005485714f87541ad6d0d478988b478c5774227a7d39b01660050", - "zh:547e0def44080456169bf77c21037aa6dc9e7f3e644a8f6a2c5fc3e6c15cf560", - "zh:6842b03d050ae1a4f1aaed2a2b1ca707eae84ae45ae492e4bb57c3d48c26e1f1", - "zh:6ced0a9eaaba12377f3a9b08df2fd9b83ae3cb357f859eb6aecf24852f718d9a", - "zh:766bcdf71a7501da73d4805d05764dcb7c848619fa7c04b3b9bd514e5ce9e4aa", - "zh:84cc8617ce0b9a3071472863f43152812e5e8544802653f636c866ef96f1ed34", - "zh:b1939e0d44c89315173b78228c1cf8660a6924604e75ced7b89e45196ce4f45e", - "zh:ced317916e13326766427790b1d8946c4151c4f3b0efd8f720a3bc24abe065fa", - "zh:ec9ff3412cf84ba81ca88328b62c17842b803ef406ae19152c13860b356b259c", - "zh:ff064f0071e98702e542e1ce00c0465b7cd186782fe9ccab8b8830cac0f10dd4", - ] -} diff --git a/README.md b/README.md index a699635..58cd142 100644 --- a/README.md +++ b/README.md @@ -1,99 +1,16 @@ -# Bootstrapped TofuGrunt Kubernetes +# Terraform scripts for RDS, ECR, and App Runner -A system that creates an entire EKS cluster from scratch in Kubernetes. -Automatically creates the remote state backend -### Setup +## Instructions -- Ensure you have OpenTofu installed with a version of at least 1.7 [here](https://opentofu.org/docs/intro/install/) +1. Ensure you are authenticated to your AWS account -- Ensure you have Terragrunt installed (if using a Debian-based Linux, use the method with taking the binary from the Github Release file instead of using Homebrew) [here](https://davidbegin.github.io/terragrunt/) +2. Clone the Repository -- Ensure your AWS Access credentials are properly configured +3. Go to the infrastructure folder (`cd infrastructure`) -### Steps to create the infrastructure +4. Run `tofu apply` -Clone the repo +5. Check the configuration and confirm the infrastructure before typing 'yes' -Run `terragrunt init` if this is a clean account with no state history. - -Run `terragrunt plan` to see what the current infrastructure will likely build - -Run `terragrunt apply` to attempt to create that infrastructure - -If you get an error saying something already exists, change the name of that thing in the code. - -### Steps to destroy the infrastructure - -Run `terragrunt destroy` to destroy the infrastructure. - -If the destruction does not work, attempt to delete the RDS instance manually in AWS and then try again. - - - -# Terraform-docs for the repo - -## Requirements - -| Name | Version | -|------|---------| -| [OpenTofu](#requirement\_opentofu) | ~> 1.7 | -| [aws](#requirement\_aws) | ~> 5.0 | -| [kubernetes](#requirement\_kubernetes) | ~> 2.31.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | ~> 5.0 | - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [eks](#module\_eks) | terraform-aws-modules/eks/aws | ~> 20.0 | -| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | 5.9.0 | - -## Resources - -| Name | Type | -|------|------| -| [aws_db_instance.mysql](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance) | resource | -| [aws_db_subnet_group.mysql-group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_subnet_group) | resource | -| [aws_iam_role.eks_cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role.eks_node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role_policy_attachment.eks_cluster_AmazonEKSClusterPolicy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.eks_node_AmazonEC2ContainerRegistryReadOnly](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.eks_node_AmazonEKSWorkerNodePolicy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.eks_node_AmazonEKS_CNI_Policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_secretsmanager_secret.db_credentials](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret) | resource | -| [aws_secretsmanager_secret_version.db_credentials_version](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_version) | resource | -| [aws_security_group.db_sg](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | -| [aws_security_group.eks_cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [cluster\_name](#input\_cluster\_name) | EKS cluster name | `string` | `"cm-cluster"` | no | -| [cluster\_version](#input\_cluster\_version) | EKS cluster version | `number` | `"1.30"` | no | -| [db\_name](#input\_db\_name) | The database name | `string` | n/a | yes | -| [db\_password](#input\_db\_password) | The database password | `string` | n/a | yes | -| [db\_username](#input\_db\_username) | The database username | `string` | n/a | yes | -| [desired\_capacity](#input\_desired\_capacity) | Desired number of worker nodes | `number` | `3` | no | -| [instance\_type](#input\_instance\_type) | EC2 instance type for worker nodes | `list` |
[
"t2.small"
]
| no | -| [max\_node\_count](#input\_max\_node\_count) | Maximum number of worker nodes | `number` | `4` | no | -| [min\_node\_count](#input\_min\_node\_count) | Minimum number of worker nodes | `number` | `2` | no | -| [region](#input\_region) | n/a | `string` | `"us-east-1"` | no | -| [vpc\_cidr](#input\_vpc\_cidr) | CIDR block for the VPC | `string` | `"10.0.0.0/16"` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| [cluster\_endpoint](#output\_cluster\_endpoint) | n/a | -| [cluster\_name](#output\_cluster\_name) | n/a | -| [cluster\_security\_group\_id](#output\_cluster\_security\_group\_id) | n/a | -| [db\_instance\_endpoint](#output\_db\_instance\_endpoint) | n/a | -| [region](#output\_region) | n/a | - +6. The process will fail with errors. If the errors are all related to AppRunner, then the process has succeeded. It is now necessary to push a container image to the ECR repository with the correct database url attached. diff --git a/features/ecr_repositories.feature b/features/ecr_repositories.feature new file mode 100644 index 0000000..0977205 --- /dev/null +++ b/features/ecr_repositories.feature @@ -0,0 +1,7 @@ +Feature: Create AWS ECR Repositories for each application and environment + +Scenario: Create ECR Repositories + Given I have the necessary IAM permissions + When I apply the OpenTofu configuration + Then ECR repositories should be created + And the repositories' URLs should be output diff --git a/infrastructure/.terraform.lock.hcl b/infrastructure/.terraform.lock.hcl new file mode 100644 index 0000000..362c410 --- /dev/null +++ b/infrastructure/.terraform.lock.hcl @@ -0,0 +1,37 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/hashicorp/aws" { + version = "5.62.0" + constraints = "5.62.0" + hashes = [ + "h1:7wg7/a/7B5UKRNpgl4f6xEK4O7A425ZC7xv5ynAOdno=", + "zh:2cb519ce7f3cbcb88b2e93dd3b3424ad85a347fc0e7429661945da5df8a20fda", + "zh:2fc7ed911cceaa1652d1f4090eaa91e8463aba86873910bccf16601260379886", + "zh:395b32d157adeb92571a0efd230c73bbee01744782a50356fb16e8946bd63ffb", + "zh:43303d36af40a568cd40bd54dc9e8430e18c4a4d78682b459dca8c755c717a0c", + "zh:65b2c6e955deeeffb9d9cd4ed97e8c532a453ba690d0e3d88c740f9036bccc4d", + "zh:a9d09dc9daf33b16894ed7d192ceb4c402261da58cded503a3ffa1dd2373e3fb", + "zh:c5e9f8bc4397c2075b6dc62458be51b93322517affd760c161633d56b0b9a334", + "zh:db0921c091402179edd549f8aa4f12dce18aab09d4302e800c67d6ec6ff88a86", + "zh:e7d13f9c0891446d03c29e4fcd60de633f71bbf1bc9786fca47a0ee356ac979a", + "zh:f128a725dbdbd31b9ed8ea478782152339c9fab4d635485763c8da2a477fe3f6", + ] +} + +provider "registry.opentofu.org/hashicorp/null" { + version = "3.2.2" + hashes = [ + "h1:P8+KlqxeTE3fNqzngzTxfwXFJaGl2Csw7lYJtFff508=", + "zh:00e5877d19fb1c1d8c4b3536334a46a5c86f57146fd115c7b7b4b5d2bf2de86d", + "zh:1755c2999e73e4d73f9de670c145c9a0dc5a373802799dff06a0e9c161354163", + "zh:2b29d706353bc9c4edda6a2946af3322abe94372ffb421d81fa176f1e57e33be", + "zh:34f65259c6d2bd51582b6da536e782b181b23725782b181193b965f519fbbacd", + "zh:370f6eb744475926a1fa7464d82d46ad83c2e1148b4b21681b4cec4d75b97969", + "zh:5950bdb23b4fcc6431562d7eba3dea37844aa4220c4da2eb898ae3e4d1b64ec4", + "zh:8f3d5c8d4b9d497fec36953a227f80c76d37fc8431b683a23fb1c42b9cccbf8a", + "zh:8f6eb5e65c047bf490ad3891efecefc488503b65898d4ee106f474697ba257d7", + "zh:a7040eed688316fe00379574c72bb8c47dbe2638b038bb705647cbf224de8f72", + "zh:e561f28df04d9e51b75f33004b7767a53c45ad96e3375d86181ba1363bffbc77", + ] +} diff --git a/infrastructure/01_provider.tf b/infrastructure/01_provider.tf index 0978330..4f9d71e 100644 --- a/infrastructure/01_provider.tf +++ b/infrastructure/01_provider.tf @@ -1,42 +1,9 @@ -provider "aws" { - region = var.primary_region -} - -provider "kubernetes" { - host = data.aws_eks_cluster.cluster.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority[0].data) - token = data.aws_eks_cluster_auth.cluster.token - load_config_file = false -} - -provider "helm" { - kubernetes { - host = data.aws_eks_cluster.main.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.main.certificate_authority[0].data) - token = data.aws_eks_cluster_auth.main.token - } -} - -provider "random" {} - terraform { - required_version = "~> 1.7" required_providers { aws = { source = "hashicorp/aws" - version = "~> 5.17" - } - kubernetes = { - source = "hashicorp/kubernetes" - version = "~> 2.31.0" - } - helm = { - source = "hashicorp/helm" - version = "~> 2.14.0" - } - random = { - source = "hashicorp/random" - version = "3.6.2" + version = "5.62.0" } } } + diff --git a/infrastructure/02_applicationlist.tf b/infrastructure/02_applicationlist.tf new file mode 100644 index 0000000..1fe5c57 --- /dev/null +++ b/infrastructure/02_applicationlist.tf @@ -0,0 +1,22 @@ +variable "applications" { + type = list(string) + default = ["announcements"] +} + +variable "environments" { + type = list(string) + default = ["dev", "qa", "prod"] +} + +locals { + app_env_combinations = [ + for app in var.applications : [ + for env in var.environments : { + app = app + env = env + } + ] + ] + + app_env_list = flatten(local.app_env_combinations) +} diff --git a/infrastructure/02_ecr.tf b/infrastructure/02_ecr.tf deleted file mode 100644 index 710fcff..0000000 --- a/infrastructure/02_ecr.tf +++ /dev/null @@ -1,53 +0,0 @@ -locals { - repository_list = ["announcements"] - repositories = { for name in local.repository_list : name => name } -} - -resource "aws_ecr_repository" "main" { - for_each = local.repositories - - name = "ecr-${var.application_name}-${var.environment_name}-${each.key}" - image_tag_mutability = "MUTABLE" - force_delete = true - - tags = { - application = var.application_name - environment = var.envrionment_name - } -} - -resource "aws_iam_group" "ecr_image_pushers" { - name = "${var.application_name}-${var.environment_name}-ecr-image-pushers" -} - -resource "aws_iam_group_policy" "ecr_image_pushers" { - for_each = local.repositories - name = "${var.application_name}-${var.environment_name}-${each.key}-ecr-image-push-policy" - group = aws_iam_group.ecr_image_pushers.name - policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - { - Effect = "Allow", - Action = [ - "ecr:GetDownloadUrlForLayer", - "ecr:BatchGetImage", - "ecr:BatchCheckLayerAvailability", - "ecr:PutImage", - "ecr:InitiateLayerUpload", - "ecr:UploadLayerPart", - "ecr:CompleteLayerUpload" - ], - Resource = aws_ecr_repository.main[each.key].arn - } - ] - }) -} - -resource "aws_iam_group_membership" "ecr_image_pushers" { - name = "${var.application_name}-${var.environment_name}-ecr-image-push-membership" - users = var.ecr_image_pushers - group = aws_iam_group.ecr_image_pushers.name -} - - diff --git a/infrastructure/03_ecr.tf b/infrastructure/03_ecr.tf new file mode 100644 index 0000000..141a907 --- /dev/null +++ b/infrastructure/03_ecr.tf @@ -0,0 +1,5 @@ +resource "aws_ecr_repository" "app_repos" { + for_each = { for combo in local.app_env_list : "${combo.app}-${combo.env}" => combo } + + name = "${each.value.app}-${each.value.env}" +} diff --git a/infrastructure/03_vpc.tf b/infrastructure/03_vpc.tf deleted file mode 100644 index ec80dd4..0000000 --- a/infrastructure/03_vpc.tf +++ /dev/null @@ -1,63 +0,0 @@ -resource "aws_vpc" "main" { - cidr_block = var.vpc_cidr_block - tags = { - Name = "${var.application_name}-${var.environment_name}-network" - application = var.application_name - environment = var.environment_name - } -} - -resource "aws_internet_gateway" "main" { - vpc_id = aws_vpc.main.id - - depends_on = [aws_vpc.main] -} - -resource "aws_resourcegroups_group" "main" { - name = "${var.application_name}-${var.environment_name}" - resource_query { - query = jsonencode( - { - ResourceTypeFilters = [ - "AWS::AllSupported" - ] - TagFilters = [ - { - Key = "application" - Values = [var.application_name] - }, - { - Key = "application" - Values = [var.application_name] - } - ] - } - ) - } -} - -data "aws_availability_zones" "available" { - state = "available" -} - -resource "random_shuffle" "az" { - input = data.aws_availability_zones.available.anmes - result_count = var.az_count -} - -locals { - azs_random = random_shuffle.az.result - - public_subnets = { for k, v in local.azs_random : - k => { - cidr_block = cidrsubnet(var.vpc_cidr_block, var.cidr_split_bits, k) - availability_zone = v - } - } - private_subnets = { for k, v in local.azs_random : - k => { - cidr_block = cidrsubnet(var.vpc_cidr_block, var.cidr_split_bits, k + var.az_count) - availability_zone = v - } - } -} diff --git a/infrastructure/04_iam.tf b/infrastructure/04_iam.tf deleted file mode 100644 index 0cb1cd0..0000000 --- a/infrastructure/04_iam.tf +++ /dev/null @@ -1,21 +0,0 @@ -data "aws_iam_policy_document" "container_cluster_assume_role" { - statement { - effect = "Allow" - principals { - type = "Service" - identifiers = ["eks.amazonaws.com"] - } - actions = ["sts:AssumeRole"] - } -} - -resource "aws_iam_role" "container_cluster" { - name = "eks-${var.application_name}-${var.environment_name}-cluster-role" - assume_role_policy = data.aws_iam_policy_document.container_cluster_assume_role.json -} - -resource "aws_iam_role_policy_attachment" "eks_cluster_policy" { - policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy" - role = aws_iam_role.container_cluster.name -} - diff --git a/infrastructure/04_rds.tf b/infrastructure/04_rds.tf new file mode 100644 index 0000000..4b764f4 --- /dev/null +++ b/infrastructure/04_rds.tf @@ -0,0 +1,75 @@ +variable "db_password" { + type = string + sensitive = true + description = "The password for the RDS instance" +} + + +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_subnet" "main" { + vpc_id = aws_vpc.main.id + cidr_block = "10.0.1.0/24" + availability_zone = "us-east-1c" +} + +resource "aws_subnet" "alternative" { + vpc_id = aws_vpc.main.id + cidr_block = "10.0.2.0/24" + availability_zone = "us-east-1b" +} + +resource "aws_db_subnet_group" "default" { + name = "default-subnet-group" + subnet_ids = [aws_subnet.main.id, aws_subnet.alternative.id] +} + +resource "aws_security_group" "rds_sg" { + vpc_id = aws_vpc.main.id + + ingress { + from_port = 3306 + to_port = 3306 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] # Replace with a more secure range + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } +} + +resource "aws_db_instance" "default" { + identifier = "shared-rds-instance" + allocated_storage = 20 + engine = "mysql" + instance_class = "db.t3.micro" + username = "admin" + password = var.db_password + parameter_group_name = "default.mysql8.0" + skip_final_snapshot = true + publicly_accessible = true + + vpc_security_group_ids = [aws_security_group.rds_sg.id] + db_subnet_group_name = aws_db_subnet_group.default.name + +} + + +resource "null_resource" "create_databases" { + for_each = { for combo in local.app_env_list : "${combo.app}-${combo.env}" => combo } + + provisioner "local-exec" { + command = < combo } + + service_name = "${each.value.app}-${each.value.env}-service" + + source_configuration { + image_repository { + image_identifier = "654654512735.dkr.ecr.us-east-1.amazonaws.com/${each.value.app}-${each.value.env}:latest" + image_repository_type = "ECR" + } + + authentication_configuration { + access_role_arn = aws_iam_role.apprunner_role.arn + } + } + + instance_configuration { + cpu = "1024" + memory = "2048" + } + + auto_scaling_configuration_arn = aws_apprunner_auto_scaling_configuration_version.app_scaling.arn + + tags = { + Environment = each.value.env + Application = each.value.app + } + + depends_on = [ + aws_ecr_repository.app_repos + ] +} + +resource "aws_iam_role" "apprunner_role" { + name = "apprunner-access-role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Principal = { + Service = "build.apprunner.amazonaws.com" + }, + Action = "sts:AssumeRole" + }, + ] + }) +} + + +resource "aws_iam_policy" "ecr_access_policy" { + name = "apprunner-ecr-access-policy" + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Action = [ + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage", + "ecr:BatchCheckLayerAvailability" + ], + Resource = "*" + }, + { + Effect = "Allow", + Action = "ecr:GetAuthorizationToken", + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "apprunner_ecr_policy_attach" { + role = aws_iam_role.apprunner_role.name + policy_arn = aws_iam_policy.ecr_access_policy.arn +} + +resource "aws_apprunner_auto_scaling_configuration_version" "app_scaling" { + auto_scaling_configuration_name = "app-scaling-config" + + max_concurrency = 100 + max_size = 3 + min_size = 1 +} + diff --git a/infrastructure/05_network.tf b/infrastructure/05_network.tf deleted file mode 100644 index 45576ed..0000000 --- a/infrastructure/05_network.tf +++ /dev/null @@ -1,72 +0,0 @@ -resource "aws_subnet" "frontend" { - - for_each = local.public_subnets - - vpc_id = aws_vpc.main.id - availability_zone = each.value.availability_zone - cidr_block = each.value.cidr_block - map_public_ip_on_launch = true - -} - -resource "aws_route_table" "frontend" { - vpc_id = aws_vpc.main.id - - route { - cidr_block = "0.0.0.0/0" - gateway_id = aws_internet_gateway.main.id - } - - depends_on = [ - aws_vpc.main, - aws_internet_gateway.main - ] -} - -resource "aws_route_table_association" "frontend" { - - for_each = aws_subnet.frontend - - subnet_id = each.value.id - route_table_id = aws_route_table.frontend.id - -} - -resource "aws_eip" "nat" { - for_each = local.private_subnets -} - -resource "aws_nat_gateway" "nat" { - - for_each = local.private_subnets - - allocation_id = aws_eip.nat[each.key].id - subnet_id = aws_subnet.backend[each.key].id - - depends_on = [ - aws_internet_gateway.main, - aws_eip.nat[each.key] - ] - -} - -resource "aws_route_table" "backend" { - - for_each = local.private_subnets - - vpc_id = aws_vpc.main.id - - route { - cidr_block = "0.0.0.0/0" - nat_gateway_id = aws_nat_gateway.nat[each.key].id - } -} - -resource "aws_route_table_association" "backend" { - - for_each = local.private_subnets - - subnet_id = aws_subnet.backend[each.key].id - route_table_id = aws_route_table.backend[each.key].id - -} diff --git a/infrastructure/06_securitygroups.tf b/infrastructure/06_securitygroups.tf deleted file mode 100644 index fc8d10d..0000000 --- a/infrastructure/06_securitygroups.tf +++ /dev/null @@ -1,34 +0,0 @@ -resource "aws_security_group" "eks_cluster" { - name = "eks-cluster-sg" - vpc_id = module.vpc.vpc_id - description = "Security group for EKS cluster control plane" - - ingress { - description = "Allow all traffic from worker nodes" - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = [var.vpc_cidr] - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } -} - -resource "aws_security_group" "db_sg" { - name_prefix = "db-sg-" - vpc_id = module.vpc.vpc_id - - ingress { - from_port = 3306 - to_port = 3306 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - } -} - - diff --git a/infrastructure/07_rds.tf b/infrastructure/07_rds.tf deleted file mode 100644 index bdae5be..0000000 --- a/infrastructure/07_rds.tf +++ /dev/null @@ -1,25 +0,0 @@ -resource "aws_db_instance" "mysql" { - - engine = "mysql" - engine_version = "8.0" - instance_class = "db.t3.micro" - allocated_storage = 20 - storage_type = "gp2" - db_name = var.db_name - username = var.db_username - password = var.db_password - port = "3306" - - db_subnet_group_name = aws_db_subnet_group.mysql-group.name - vpc_security_group_ids = [aws_security_group.db_sg.id] - publicly_accessible = true - - skip_final_snapshot = true -} - -resource "aws_db_subnet_group" "mysql-group" { - name = "db_subnet_group_${local.cleanstamp}" - subnet_ids = module.vpc.public_subnets -} - - diff --git a/infrastructure/98_variables.tf b/infrastructure/98_variables.tf deleted file mode 100644 index b7c3cc3..0000000 --- a/infrastructure/98_variables.tf +++ /dev/null @@ -1,33 +0,0 @@ -variable "primary_region" { - type = string - description = "Primary region for account activity" - default = "us-east-1" -} - -variable "vpc_cidr_block" { - type = string - description = "CIDR block for the VPC" - default = "10.0.0.0/16" -} - -variable "az_count" { - type = number - description = "Count for availability zones" - default = 1 -} - -variable "db_name" { - description = "The database name" - type = string -} - -variable "db_username" { - description = "The database username" - type = string -} - -variable "db_password" { - description = "The database password" - type = string -} - diff --git a/infrastructure/99_output.tf b/infrastructure/99_output.tf deleted file mode 100644 index 823f4d9..0000000 --- a/infrastructure/99_output.tf +++ /dev/null @@ -1,27 +0,0 @@ -output "cluster_name" { - value = var.cluster_name -} - -output "cluster_endpoint" { - value = module.eks.cluster_endpoint -} - -output "cluster_security_group_id" { - value = module.eks.cluster_security_group_id -} - -output "region" { - value = var.region -} - -output "db_instance_endpoint" { - value = aws_db_instance.mysql.endpoint -} - -output "eks_connect" { - value = "aws eks --region us-east-1 update-kubeconfig --name ${module.eks.cluster_name}" -} - -output "argocd_initial_admin_secret" { - value = "kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath=\"{.data.password}\" | base64 -d" -} diff --git a/infrastructure/terraform.tfstate b/infrastructure/terraform.tfstate new file mode 100644 index 0000000..c484e17 --- /dev/null +++ b/infrastructure/terraform.tfstate @@ -0,0 +1,9 @@ +{ + "version": 4, + "terraform_version": "1.7.2", + "serial": 11, + "lineage": "54543b65-0785-73d9-c782-458033890453", + "outputs": {}, + "resources": [], + "check_results": null +} diff --git a/infrastructure/terraform.tfstate.backup b/infrastructure/terraform.tfstate.backup new file mode 100644 index 0000000..1fe36c7 --- /dev/null +++ b/infrastructure/terraform.tfstate.backup @@ -0,0 +1,613 @@ +{ + "version": 4, + "terraform_version": "1.7.2", + "serial": 10, + "lineage": "54543b65-0785-73d9-c782-458033890453", + "outputs": {}, + "resources": [ + { + "mode": "managed", + "type": "aws_apprunner_auto_scaling_configuration_version", + "name": "app_scaling", + "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws:apprunner:us-east-1:654654512735:autoscalingconfiguration/app-scaling-config/1/698d3cef159a4e53a2bf2ee136979a68", + "auto_scaling_configuration_name": "app-scaling-config", + "auto_scaling_configuration_revision": 1, + "has_associated_service": false, + "id": "arn:aws:apprunner:us-east-1:654654512735:autoscalingconfiguration/app-scaling-config/1/698d3cef159a4e53a2bf2ee136979a68", + "is_default": false, + "latest": true, + "max_concurrency": 100, + "max_size": 3, + "min_size": 1, + "status": "active", + "tags": {}, + "tags_all": {} + }, + "sensitive_attributes": [], + "private": "bnVsbA==" + } + ] + }, + { + "mode": "managed", + "type": "aws_apprunner_service", + "name": "app_services", + "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", + "instances": [ + { + "index_key": "announcements-dev", + "status": "tainted", + "schema_version": 0, + "attributes": { + "arn": null, + "auto_scaling_configuration_arn": "arn:aws:apprunner:us-east-1:654654512735:autoscalingconfiguration/app-scaling-config/1/698d3cef159a4e53a2bf2ee136979a68", + "encryption_configuration": [], + "health_check_configuration": null, + "id": "arn:aws:apprunner:us-east-1:654654512735:service/announcements-dev-service/4436750d31e942b485b8dc466ba582c9", + "instance_configuration": [ + { + "cpu": "1024", + "instance_role_arn": "", + "memory": "2048" + } + ], + "network_configuration": null, + "observability_configuration": [], + "service_id": null, + "service_name": "announcements-dev-service", + "service_url": null, + "source_configuration": [ + { + "authentication_configuration": [ + { + "access_role_arn": "arn:aws:iam::654654512735:role/apprunner-access-role", + "connection_arn": "" + } + ], + "auto_deployments_enabled": true, + "code_repository": [], + "image_repository": [ + { + "image_configuration": [], + "image_identifier": "654654512735.dkr.ecr.us-east-1.amazonaws.com/announcements-dev:latest", + "image_repository_type": "ECR" + } + ] + } + ], + "status": null, + "tags": { + "Application": "announcements", + "Environment": "dev" + }, + "tags_all": { + "Application": "announcements", + "Environment": "dev" + } + }, + "sensitive_attributes": [], + "private": "bnVsbA==", + "dependencies": [ + "aws_apprunner_auto_scaling_configuration_version.app_scaling", + "aws_ecr_repository.app_repos", + "aws_iam_role.apprunner_role" + ] + }, + { + "index_key": "announcements-prod", + "status": "tainted", + "schema_version": 0, + "attributes": { + "arn": null, + "auto_scaling_configuration_arn": "arn:aws:apprunner:us-east-1:654654512735:autoscalingconfiguration/app-scaling-config/1/698d3cef159a4e53a2bf2ee136979a68", + "encryption_configuration": [], + "health_check_configuration": null, + "id": "arn:aws:apprunner:us-east-1:654654512735:service/announcements-prod-service/00f46166691142e0ad04186aca90f63a", + "instance_configuration": [ + { + "cpu": "1024", + "instance_role_arn": "", + "memory": "2048" + } + ], + "network_configuration": null, + "observability_configuration": [], + "service_id": null, + "service_name": "announcements-prod-service", + "service_url": null, + "source_configuration": [ + { + "authentication_configuration": [ + { + "access_role_arn": "arn:aws:iam::654654512735:role/apprunner-access-role", + "connection_arn": "" + } + ], + "auto_deployments_enabled": true, + "code_repository": [], + "image_repository": [ + { + "image_configuration": [], + "image_identifier": "654654512735.dkr.ecr.us-east-1.amazonaws.com/announcements-prod:latest", + "image_repository_type": "ECR" + } + ] + } + ], + "status": null, + "tags": { + "Application": "announcements", + "Environment": "prod" + }, + "tags_all": { + "Application": "announcements", + "Environment": "prod" + } + }, + "sensitive_attributes": [], + "private": "bnVsbA==", + "dependencies": [ + "aws_apprunner_auto_scaling_configuration_version.app_scaling", + "aws_ecr_repository.app_repos", + "aws_iam_role.apprunner_role" + ] + }, + { + "index_key": "announcements-qa", + "status": "tainted", + "schema_version": 0, + "attributes": { + "arn": null, + "auto_scaling_configuration_arn": "arn:aws:apprunner:us-east-1:654654512735:autoscalingconfiguration/app-scaling-config/1/698d3cef159a4e53a2bf2ee136979a68", + "encryption_configuration": [], + "health_check_configuration": null, + "id": "arn:aws:apprunner:us-east-1:654654512735:service/announcements-qa-service/07d17a2103b048d9828dd1f3e48fef33", + "instance_configuration": [ + { + "cpu": "1024", + "instance_role_arn": "", + "memory": "2048" + } + ], + "network_configuration": null, + "observability_configuration": [], + "service_id": null, + "service_name": "announcements-qa-service", + "service_url": null, + "source_configuration": [ + { + "authentication_configuration": [ + { + "access_role_arn": "arn:aws:iam::654654512735:role/apprunner-access-role", + "connection_arn": "" + } + ], + "auto_deployments_enabled": true, + "code_repository": [], + "image_repository": [ + { + "image_configuration": [], + "image_identifier": "654654512735.dkr.ecr.us-east-1.amazonaws.com/announcements-qa:latest", + "image_repository_type": "ECR" + } + ] + } + ], + "status": null, + "tags": { + "Application": "announcements", + "Environment": "qa" + }, + "tags_all": { + "Application": "announcements", + "Environment": "qa" + } + }, + "sensitive_attributes": [], + "private": "bnVsbA==", + "dependencies": [ + "aws_apprunner_auto_scaling_configuration_version.app_scaling", + "aws_ecr_repository.app_repos", + "aws_iam_role.apprunner_role" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_db_subnet_group", + "name": "default", + "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws:rds:us-east-1:654654512735:subgrp:default-subnet-group", + "description": "Managed by Terraform", + "id": "default-subnet-group", + "name": "default-subnet-group", + "name_prefix": "", + "subnet_ids": [ + "subnet-0695d3dd445173149", + "subnet-0a3fd13b052e1ea06" + ], + "supported_network_types": [ + "IPV4" + ], + "tags": null, + "tags_all": {}, + "vpc_id": "vpc-0703324b0286401ca" + }, + "sensitive_attributes": [], + "private": "bnVsbA==", + "dependencies": [ + "aws_subnet.alternative", + "aws_subnet.main", + "aws_vpc.main" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_ecr_repository", + "name": "app_repos", + "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", + "instances": [ + { + "index_key": "announcements-dev", + "schema_version": 0, + "attributes": { + "arn": "arn:aws:ecr:us-east-1:654654512735:repository/announcements-dev", + "encryption_configuration": [ + { + "encryption_type": "AES256", + "kms_key": "" + } + ], + "force_delete": null, + "id": "announcements-dev", + "image_scanning_configuration": [ + { + "scan_on_push": false + } + ], + "image_tag_mutability": "MUTABLE", + "name": "announcements-dev", + "registry_id": "654654512735", + "repository_url": "654654512735.dkr.ecr.us-east-1.amazonaws.com/announcements-dev", + "tags": {}, + "tags_all": {}, + "timeouts": null + }, + "sensitive_attributes": [], + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxMjAwMDAwMDAwMDAwfX0=" + }, + { + "index_key": "announcements-prod", + "schema_version": 0, + "attributes": { + "arn": "arn:aws:ecr:us-east-1:654654512735:repository/announcements-prod", + "encryption_configuration": [ + { + "encryption_type": "AES256", + "kms_key": "" + } + ], + "force_delete": null, + "id": "announcements-prod", + "image_scanning_configuration": [ + { + "scan_on_push": false + } + ], + "image_tag_mutability": "MUTABLE", + "name": "announcements-prod", + "registry_id": "654654512735", + "repository_url": "654654512735.dkr.ecr.us-east-1.amazonaws.com/announcements-prod", + "tags": {}, + "tags_all": {}, + "timeouts": null + }, + "sensitive_attributes": [], + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxMjAwMDAwMDAwMDAwfX0=" + }, + { + "index_key": "announcements-qa", + "schema_version": 0, + "attributes": { + "arn": "arn:aws:ecr:us-east-1:654654512735:repository/announcements-qa", + "encryption_configuration": [ + { + "encryption_type": "AES256", + "kms_key": "" + } + ], + "force_delete": null, + "id": "announcements-qa", + "image_scanning_configuration": [ + { + "scan_on_push": false + } + ], + "image_tag_mutability": "MUTABLE", + "name": "announcements-qa", + "registry_id": "654654512735", + "repository_url": "654654512735.dkr.ecr.us-east-1.amazonaws.com/announcements-qa", + "tags": {}, + "tags_all": {}, + "timeouts": null + }, + "sensitive_attributes": [], + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxMjAwMDAwMDAwMDAwfX0=" + } + ] + }, + { + "mode": "managed", + "type": "aws_iam_policy", + "name": "ecr_access_policy", + "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws:iam::654654512735:policy/apprunner-ecr-access-policy", + "attachment_count": 1, + "description": "", + "id": "arn:aws:iam::654654512735:policy/apprunner-ecr-access-policy", + "name": "apprunner-ecr-access-policy", + "name_prefix": "", + "path": "/", + "policy": "{\"Statement\":[{\"Action\":[\"ecr:GetDownloadUrlForLayer\",\"ecr:BatchGetImage\",\"ecr:BatchCheckLayerAvailability\"],\"Effect\":\"Allow\",\"Resource\":\"*\"},{\"Action\":\"ecr:GetAuthorizationToken\",\"Effect\":\"Allow\",\"Resource\":\"*\"}],\"Version\":\"2012-10-17\"}", + "policy_id": "ANPAZQ3DTDZPYUO66RVZD", + "tags": {}, + "tags_all": {} + }, + "sensitive_attributes": [], + "private": "bnVsbA==" + } + ] + }, + { + "mode": "managed", + "type": "aws_iam_role", + "name": "apprunner_role", + "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws:iam::654654512735:role/apprunner-access-role", + "assume_role_policy": "{\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"build.apprunner.amazonaws.com\"}}],\"Version\":\"2012-10-17\"}", + "create_date": "2024-08-15T17:54:50Z", + "description": "", + "force_detach_policies": false, + "id": "apprunner-access-role", + "inline_policy": [], + "managed_policy_arns": [ + "arn:aws:iam::654654512735:policy/apprunner-ecr-access-policy" + ], + "max_session_duration": 3600, + "name": "apprunner-access-role", + "name_prefix": "", + "path": "/", + "permissions_boundary": "", + "tags": {}, + "tags_all": {}, + "unique_id": "AROAZQ3DTDZPQRT6NSTHC" + }, + "sensitive_attributes": [], + "private": "bnVsbA==" + } + ] + }, + { + "mode": "managed", + "type": "aws_iam_role_policy_attachment", + "name": "apprunner_ecr_policy_attach", + "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "apprunner-access-role-20240815175450650000000001", + "policy_arn": "arn:aws:iam::654654512735:policy/apprunner-ecr-access-policy", + "role": "apprunner-access-role" + }, + "sensitive_attributes": [], + "private": "bnVsbA==", + "dependencies": [ + "aws_iam_policy.ecr_access_policy", + "aws_iam_role.apprunner_role" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_security_group", + "name": "rds_sg", + "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-east-1:654654512735:security-group/sg-0b7d1f9deff6649db", + "description": "Managed by Terraform", + "egress": [ + { + "cidr_blocks": [ + "0.0.0.0/0" + ], + "description": "", + "from_port": 0, + "ipv6_cidr_blocks": [], + "prefix_list_ids": [], + "protocol": "-1", + "security_groups": [], + "self": false, + "to_port": 0 + } + ], + "id": "sg-0b7d1f9deff6649db", + "ingress": [ + { + "cidr_blocks": [ + "0.0.0.0/0" + ], + "description": "", + "from_port": 3306, + "ipv6_cidr_blocks": [], + "prefix_list_ids": [], + "protocol": "tcp", + "security_groups": [], + "self": false, + "to_port": 3306 + } + ], + "name": "terraform-20240815175451484000000002", + "name_prefix": "terraform-", + "owner_id": "654654512735", + "revoke_rules_on_delete": false, + "tags": {}, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0703324b0286401ca" + }, + "sensitive_attributes": [], + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6OTAwMDAwMDAwMDAwfSwic2NoZW1hX3ZlcnNpb24iOiIxIn0=", + "dependencies": [ + "aws_vpc.main" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_subnet", + "name": "alternative", + "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-east-1:654654512735:subnet/subnet-0695d3dd445173149", + "assign_ipv6_address_on_creation": false, + "availability_zone": "us-east-1b", + "availability_zone_id": "use1-az2", + "cidr_block": "10.0.2.0/24", + "customer_owned_ipv4_pool": "", + "enable_dns64": false, + "enable_lni_at_device_index": 0, + "enable_resource_name_dns_a_record_on_launch": false, + "enable_resource_name_dns_aaaa_record_on_launch": false, + "id": "subnet-0695d3dd445173149", + "ipv6_cidr_block": "", + "ipv6_cidr_block_association_id": "", + "ipv6_native": false, + "map_customer_owned_ip_on_launch": false, + "map_public_ip_on_launch": false, + "outpost_arn": "", + "owner_id": "654654512735", + "private_dns_hostname_type_on_launch": "ip-name", + "tags": null, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0703324b0286401ca" + }, + "sensitive_attributes": [], + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMSJ9", + "dependencies": [ + "aws_vpc.main" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_subnet", + "name": "main", + "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-east-1:654654512735:subnet/subnet-0a3fd13b052e1ea06", + "assign_ipv6_address_on_creation": false, + "availability_zone": "us-east-1c", + "availability_zone_id": "use1-az4", + "cidr_block": "10.0.1.0/24", + "customer_owned_ipv4_pool": "", + "enable_dns64": false, + "enable_lni_at_device_index": 0, + "enable_resource_name_dns_a_record_on_launch": false, + "enable_resource_name_dns_aaaa_record_on_launch": false, + "id": "subnet-0a3fd13b052e1ea06", + "ipv6_cidr_block": "", + "ipv6_cidr_block_association_id": "", + "ipv6_native": false, + "map_customer_owned_ip_on_launch": false, + "map_public_ip_on_launch": false, + "outpost_arn": "", + "owner_id": "654654512735", + "private_dns_hostname_type_on_launch": "ip-name", + "tags": {}, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0703324b0286401ca" + }, + "sensitive_attributes": [], + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMSJ9", + "dependencies": [ + "aws_vpc.main" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_vpc", + "name": "main", + "provider": "provider[\"registry.opentofu.org/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-east-1:654654512735:vpc/vpc-0703324b0286401ca", + "assign_generated_ipv6_cidr_block": false, + "cidr_block": "10.0.0.0/16", + "default_network_acl_id": "acl-0dd2bdb92530fedd6", + "default_route_table_id": "rtb-00455086c858b9518", + "default_security_group_id": "sg-09cf7c3814ef1aec9", + "dhcp_options_id": "dopt-0448c905aa54596ac", + "enable_dns_hostnames": false, + "enable_dns_support": true, + "enable_network_address_usage_metrics": false, + "id": "vpc-0703324b0286401ca", + "instance_tenancy": "default", + "ipv4_ipam_pool_id": null, + "ipv4_netmask_length": null, + "ipv6_association_id": "", + "ipv6_cidr_block": "", + "ipv6_cidr_block_network_border_group": "", + "ipv6_ipam_pool_id": "", + "ipv6_netmask_length": 0, + "main_route_table_id": "rtb-00455086c858b9518", + "owner_id": "654654512735", + "tags": {}, + "tags_all": {} + }, + "sensitive_attributes": [], + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ==" + } + ] + } + ], + "check_results": null +} diff --git a/infrastructure/terragrunt.hcl b/infrastructure/terragrunt.hcl deleted file mode 100644 index 34ae804..0000000 --- a/infrastructure/terragrunt.hcl +++ /dev/null @@ -1,15 +0,0 @@ -remote_state { - backend = "s3" - generate = { - path = "backend.tf" - if_exists = "overwrite_terragrunt" - } - config = { - bucket = "cm-state-${get_aws_account_id()}" - key = "${path_relative_to_include()}/terraform.tfstate" - region = "us-east-1" - encrypt = true - dynamodb_table = "cm-state-lock-table" - } -} - diff --git a/kubernetes/06_eks.tf b/kubernetes/06_eks.tf deleted file mode 100644 index 4aac95f..0000000 --- a/kubernetes/06_eks.tf +++ /dev/null @@ -1,174 +0,0 @@ -locals { - cluster_name = "eks-${var.application_name}-${var.environment_name}" - cluster_subnet_ids = [for subnet in values(aws_subnet.backend) : subnet.id] -} - -data "aws_eks_cluster" "cluster" { - name = var.eks_cluster_name -} - -resource "aws_cloudwatch_log_group" "container_cluster" { - name = "/aws/eks/${local.cluster_name}/cluster" - retention_in_days = 7 -} - - -resource "aws_eks_cluster" "main" { - name = local.cluster_name - role_arn = aws_iam_role.container_cluster.arn - enabled_cluster_log_types = ["api", "audit"] - - vpc_config { - - security_group_ids = [ - aws_security_group.cluster.id, - aws_security_group.cluster_nodes.id - ] - - subnet_ids = local.cluster_subnet_ids - endpoint_public_access = true - endpoint_private_access = true - } - - depends_on = [ - aws_iam_role_policy_attachment.eks_cluster_policy, - aws_iam_role_policy_attachemetn.eks_vpc_controller_policy, - aws_cloudwatch_log_group.container_cluster, - aws_ecr_repository.main.* - ] - - tags = { - application = var.applications_name - environment = var.envrionment_name - } -} - -resource "aws_eks_node_group" "main" { - - cluster_name = aws_eks_cluster.main.name - node_group_name = "ng-user" - node_role_arn = aws_iam_role.container_node_group.arn - subnet_ids = local.cluster_subnet_ids - - scaling_config { - desired_size = 3 - min_size = 1 - max_size = 4 - } - - update_config { - max_unavailable = 1 - } - - ami_type = var.node_image_type - instance_types = [var.node_size] - - depends_on = [ - aws_iam_role_policy_attachment.eks_worker_node_policy, - aws_iam_role_policy_attachment.eks_cni_policy, - aws_iam_role_policy_attachment.eks_ecr_policy, - aws_iam_role_policy_attachment.eks_cloudwatch_policy - ] -} - -data "tls_certificate" "container_cluster_oidc" { - url = aws_eks_cluster.main.identity[0].oidc[0].issuer -} - -resource "aws_iam_openid_connect_provider" "container_cluster_oidc" { - client_id_list = ["sts.amazonaws.com"] - thumbprint_list = [data.tls_certificate.container_cluster_oidc.certificates[0].sha1_fingerprint] - url = data.tls_certificate.container_cluster_oidc.url -} - -data "aws_iam_policy_document" "workload_identity_assume_role_policy" { - statement { - actions = ["sts:AssumeRoleWithWebIdentity"] - effect = "Allow" - condition { - test = "StringEquals" - variable = "${replace(aws_iam_openid_connect_provider.container_cluster_oidc.url, "https://", "")}:sub" - values = ["system:serviceaccount:${var.k8s_namespace}:${var.k8s_service_account_name}"] - } - principals { - identifiers = [aws_iam_openid_connect_provider.container_cluster_oidc.arn] - type = "Federated" - } - } -} - -resource "kubernetes_namespace" "main" { - metadata { - name = var.namespace - labels = { - name = var.namespace - } - } -} - -resource "kubernetes_service_account" "workload_identity" { - for_each = {for k, v in var.namespaces: k=>v} - metadata { - name = "${each.value[0]}_service_account" - namespace = each.value[0] - annotations = { - "eks.amazonaws.com/role-arn" = var.workload_identity_role - } - } -} - -resource "helm_release" "csi_secrets_store" { - name = "csi-secrets-store" - repository = "https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts" - namespace = "kube-system" - set { - name = "syncSecret.enabled" - value = "true" - } -} - -resource "helm_release" "aws_secrets_provider" { - name = "secrets-provider-aws" - repository = "https://aws.github.io/secrets-store-csi-driver-provider-aws" - chart = "secrets-store-csi-driver-provider-aws" - namespace = "kube-system" -} - -resource "kubernetes_manifest" "secret_provider_class" { - manifest = { - apiVersion = "secrets-store.csi.x-k8s.io/v1" - kind = "SecretProviderClass" - - metadata = { - name = "${var.application_name}-${var.environment_name}-secret-provider-class" - namespace = var.namespace - } - - spec = { - provider = "aws" - parameters = { - objects = yamlencode([ - { - objectName = "connection-string" - objectType = "secretsmanager" - objectVersionLabel = "AWSCURRENT" - } - ]) - } - secretObjects = [ - { - data = [ - { - key = "connection-string" - objectName = "connection-string" - } - ] - secretName = "connection-string" - type = "Opaque" - } - ] - } - } -} - - diff --git a/kubernetes/07_secrets.tf b/kubernetes/07_secrets.tf deleted file mode 100644 index f2ee63a..0000000 --- a/kubernetes/07_secrets.tf +++ /dev/null @@ -1,42 +0,0 @@ -data "aws_iam_policy_document" "workload_identity_policy" { - statement { - effect = "Allow" - actions = [ - "secretsmanager:GetSecretValue", - "secretsmanager:DescribeSecret", - ] - resources = [ - "arn:aws:secretmanager:${var.primary_region}:${data.aws_caller_identity.current.account_id}:secret:*", - ] - } -} - -resource "aws_secretsmanager_secret" "db_credentials" { - name = "${var.application_name}-${var.environment_name}-connection-string" - description = "Database connection string" -} - -resource "aws_secretsmanager_secret_version" "db_credentials_version" { - secret_id = aws_secretsmanager_secret.db_credentials.id - secret_string = random_password.db_credentials.result -} - -resource "helm_release" "csi_secrets_store" { - name = "csi-secrets-store" - repository = "https://kubernetes-sigs.github.io/secrets-stroe-csi-driver/charts" - chart = "secrets-store-csi-driver" - namespace = "kube-system" - - set { - name = "syncSecret.enabled" - value = "true" - } -} - -resource "helm_release" "aws_secrets_provider" { - name = "secrets-provider-aws" - repository = "https://aws.github.io/secrets-store-csi-driver-provider-aws" - chart = "secrets-store-csi-driver-provider-aws" - namespace = "kube-system" -} - diff --git a/kubernetes/09_deployment.tf b/kubernetes/09_deployment.tf deleted file mode 100644 index a71538b..0000000 --- a/kubernetes/09_deployment.tf +++ /dev/null @@ -1,84 +0,0 @@ -data "aws_caller_identity" "current" {} - -locals { - account_id = data.aws_caller_identity.current.account_id - container_registry = "${local.account_id}.dkr.ecr.${var.primary_region}.amazonaws.com/" -} - -resource "kubernetes_deployment" "web_app" { - - metadata { - name = "${var.application_name}-${var.environment_name}" - namespace = var.namespace - } - - spec { - replicas = 1 - selector { - match_labels = { - app = "${var.application_name}-${var.environment_name}" - } - } - - template { - metadata { - labels = { - app = "${var.application_name}-${var.environment_name}" - - } - } - spec { - service_account_name = kubernetes_service_account.workload_identity.metadata[0].name - container { - image = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.primary_region}.amazonaws.com/${var.web_app_image.name}:${var.web_app_image.version}" - name = var.application_name - port { - containerPort = 8000 - } - env_from { - config_map_ref { - name = kubernetes_config_map.web_app.metadata.0.name - } - } - } - } - } - } - - timeouts { - create = "5m" - update = "5m" - delete = "5m" - } -} - -resource "kubernetes_service" "web_app" { - metadata { - name = "${var.application_name}-service" - namespace = var.namespace - } - - spec { - type = "ClusterIP" - port { - port = 80 - target_port = 8000 - } - - selector = { - app = var.application_name - } - } -} - -resource "kubernetes_config_map" "web_app" { - metadata { - name = "${var.application_name}-config" - namespace = var.namespace - } - - data = { - BackendEndpoint = "" - } -} - diff --git a/kubernetes/10_ingress.tf b/kubernetes/10_ingress.tf deleted file mode 100644 index a95a728..0000000 --- a/kubernetes/10_ingress.tf +++ /dev/null @@ -1,54 +0,0 @@ -resource "helm_release" "ingress" { - name = "ingress" - repository = "https://chart.bitnami.com/bitnami" - chart = "nginx-ingress-controller" - - create_namespace = true - namespace = "ingress-nginx" - - set { - name = "service.type" - value = "LoadBalancer" - } - - set { - name = "service.annotation" - value = "service.beta.kubernetes.io/aws-load-balancer-type: nlb" - } -} - -resource "kubernetes_ingress_v1" "ingress" { - metadata { - name = "${var.application_name}-ingress" - namespace = var.namespace - annotations = { - "kubernetes.io/ingress.class" = "nginx" - } - } - - spec { - rule { - http { - path { - path = "/" - path_type = "Prefix" - - backend { - service { - name = kubernetes_service.web_app.metadata[0].name - port { - number = 80 - } - } - } - } - } - } - } - - depends_on = [ - kubernetes_service.web_app, - helm_release.ingress - ] -} - diff --git a/kubernetes/98_variables.tf b/kubernetes/98_variables.tf deleted file mode 100644 index 24a8610..0000000 --- a/kubernetes/98_variables.tf +++ /dev/null @@ -1,79 +0,0 @@ -variable "web_app_name" { - type = string - description = "Name of web application" -} - -variable "primary_region" { - type = string - description = "Primary region for account activity" - default = "us-east-1" -} - -variable "vpc_cidr_block" { - type = string - description = "CIDR block for the VPC" - default = "10.0.0.0/16" -} - -variable "az_count" { - type = number - description = "Count for availability zones" - default = 1 -} - -variable "cluster_name" { - description = "EKS cluster name" - type = string - default = "cm-appfolio" -} - -variable "cluster_version" { - type = number - description = "EKS cluster version" - default = "1.29" -} - -variable "desired_capacity" { - type = number - description = "Desired number of worker nodes" - default = 3 -} - -variable "min_node_count" { - description = "Minimum number of worker nodes" - type = number - default = 2 -} - -variable "max_node_count" { - description = "Maximum number of worker nodes" - type = number - default = 4 -} - -variable "instance_type" { - description = "EC2 instance type for worker nodes" - default = ["t2.small"] - type = list(any) -} - -variable "db_name" { - description = "The database name" - type = string -} - -variable "db_username" { - description = "The database username" - type = string -} - -variable "db_password" { - description = "The database password" - type = string -} - -variable "namespaces" { - description = "Cluster namespaces" - type = list(string) - default = ["dev", "qa"] -} diff --git a/kubernetes/99_output.tf b/kubernetes/99_output.tf deleted file mode 100644 index 823f4d9..0000000 --- a/kubernetes/99_output.tf +++ /dev/null @@ -1,27 +0,0 @@ -output "cluster_name" { - value = var.cluster_name -} - -output "cluster_endpoint" { - value = module.eks.cluster_endpoint -} - -output "cluster_security_group_id" { - value = module.eks.cluster_security_group_id -} - -output "region" { - value = var.region -} - -output "db_instance_endpoint" { - value = aws_db_instance.mysql.endpoint -} - -output "eks_connect" { - value = "aws eks --region us-east-1 update-kubeconfig --name ${module.eks.cluster_name}" -} - -output "argocd_initial_admin_secret" { - value = "kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath=\"{.data.password}\" | base64 -d" -} diff --git a/tests/ecr_test.go b/tests/ecr_test.go new file mode 100644 index 0000000..dfe244f --- /dev/null +++ b/tests/ecr_test.go @@ -0,0 +1,22 @@ +package tests + +import ( + "testing" + "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/stretchr/testify/assert" +) + +func TestECRRepositories(t *testing.T) { + terraformOptions := &terraform.Options{ + TerraformDir: "../infrastructure" + } + + // Ensure the code is cleaned up at the end of the test + defer terraform.Destroy(t, terraformOptions) + + // Run Terraform Init and Apply + terraform.InitAndApply(t, terraformOptions) + + repoUrl := terraform.Output(t, terraformOptions, "repository_url") + assert.Contains(t, repoUrl, "amazonaws.com") +}