{"id":19396126,"url":"https://github.com/ruanbekker/aws-terraform-cicd-java-springboot","last_synced_at":"2025-07-29T19:40:20.961Z","repository":{"id":41462634,"uuid":"342588754","full_name":"ruanbekker/aws-terraform-cicd-java-springboot","owner":"ruanbekker","description":"Terraform: AWS CICD with CodePipeline, CodeBuild and ECS and a Springboot App","archived":false,"fork":false,"pushed_at":"2022-11-21T07:18:37.000Z","size":389,"stargazers_count":27,"open_issues_count":0,"forks_count":23,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-07-19T14:40:40.225Z","etag":null,"topics":["aws","ecs","java","spring-boot","terraform"],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ruanbekker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-02-26T13:45:51.000Z","updated_at":"2024-11-16T08:28:32.000Z","dependencies_parsed_at":"2023-01-21T08:43:35.462Z","dependency_job_id":null,"html_url":"https://github.com/ruanbekker/aws-terraform-cicd-java-springboot","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ruanbekker/aws-terraform-cicd-java-springboot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Faws-terraform-cicd-java-springboot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Faws-terraform-cicd-java-springboot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Faws-terraform-cicd-java-springboot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Faws-terraform-cicd-java-springboot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ruanbekker","download_url":"https://codeload.github.com/ruanbekker/aws-terraform-cicd-java-springboot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruanbekker%2Faws-terraform-cicd-java-springboot/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266312110,"owners_count":23909744,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-21T11:47:31.412Z","response_time":64,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","ecs","java","spring-boot","terraform"],"created_at":"2024-11-10T10:33:45.976Z","updated_at":"2025-07-21T13:34:20.758Z","avatar_url":"https://github.com/ruanbekker.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# aws-terraform-cicd-java-springboot\nTerraform: AWS CICD with CodePipeline, CodeBuild, ECS and a Springboot App\n\nThe [all-in-one](https://github.com/ruanbekker/aws-terraform-cicd-java-springboot/tree/all-in-one) branch has the application code, application infrastructure and pipeline infrastructure in one repository.\n\n## Description\n\nThis is a demo on how to use Terraform to deploy your AWS Infrastructure for your Java Springboot application to run as a container on ECS.\n\nYou will be able to boot your application locally using docker-compose as well as building the following infrastructure on AWS for this application:\n\n- ALB, Target Groups, 80 and 443 Target Group Listeners, with Listener Configurations\n- ACM Certificates, ACM Certificate Validation and Route53 Configuration\n- CI/CD Pipeline with CodePipeline, CodeBuild and Deployment to ECS with EC2 as target\n- Github Webhook (Pipeline will trigger on the main branch but configurable in `variables.tf`)\n- ECR Repository\n- ECS Container Instance with Userdata\n- ECS Cluster, ECS Service and ECS Task Definition with variables\n- S3 Buckets for CodePipeline and CodeBuild Cache\n- RDS MySQL Instance\n- SSM Parameters for RDS Password, Hostname etc, which we will place into the Task Definition as well\n- IAM Roles, Policies and Security Groups\n\nWhen I tested, terraform took `4m 24s` to deploy the infrastructure and when I made a commit to the `main` branch the pipeline took about 5 minutes to deploy.\n\n## Deploy Local\n\nBoot our application with docker-compose:\n\n```\n$ docker-compose up --build\n```\n\n## Test the Application Locally\n\nMake a request to view all cars:\n\n```\n$ curl http://localhost:8080/api/cars\n[]\n```\n\nCreate one car:\n\n```\n$ curl -H \"Content-Type: application/json\" http://localhost:8080/api/cars -d '{\"make\":\"bmw\", \"model\": \"m3\"}'\n{\"id\":3,\"make\":\"bmw\",\"model\":\"m3\",\"createdAt\":\"2021-03-01T14:12:07.624+00:00\",\"updatedAt\":\"2021-03-01T14:12:07.624+00:00\"}\n```\n\nView all cars again:\n\n```\n$ curl http://localhost:8080/api/cars\n[{\"id\":3,\"make\":\"bmw\",\"model\":\"m3\",\"createdAt\":\"2021-03-01T14:12:08.000+00:00\",\"updatedAt\":\"2021-03-01T14:12:08.000+00:00\"}]\n```\n\nView a specific car:\n\n```\n$ curl http://localhost:8080/api/cars/3\n{\"id\":3,\"make\":\"bmw\",\"model\":\"m3\",\"createdAt\":\"2021-03-01T14:12:08.000+00:00\",\"updatedAt\":\"2021-03-01T14:12:08.000+00:00\"}\n```\n\nDelete a car:\n\n```\n$ curl -XDELETE http://localhost:8080/api/cars/3\n```\n\nView application status:\n\n```\n$ curl -s http://localhost:8080/status | jq .\n{\n  \"status\": \"UP\",\n  \"components\": {\n    \"db\": {\n      \"status\": \"UP\",\n      \"details\": {\n        \"database\": \"MySQL\",\n        \"validationQuery\": \"isValid()\"\n      }\n    },\n    \"diskSpace\": {\n      \"status\": \"UP\",\n      \"details\": {\n        \"total\": 62725623808,\n        \"free\": 2183278592,\n        \"threshold\": 10485760,\n        \"exists\": true\n      }\n    },\n    \"ping\": {\n      \"status\": \"UP\"\n    }\n  }\n}\n```\n\nOr the database status individually:\n\n```\n$ curl -s http://localhost:8080/status/db\n{\"status\":\"UP\",\"details\":{\"database\":\"MySQL\",\"validationQuery\":\"isValid()\"}}\n```\n\n## Installing Terraform\n\nFor Mac:\n\n```\n$ wget https://releases.hashicorp.com/terraform/0.14.7/terraform_0.14.7_darwin_amd64.zip\n$ unzip terraform_0.14.7_darwin_amd64.zip\n$ mv ./terraform /usr/local/bin/terraform\n$ rm -rf terraform_0.14.7_darwin_amd64.zip\n```\n\nView the version:\n\n```\n$ terraform -version\nTerraform v0.14.7\n```\n\n## Assumptions for AWS\n\nFor AWS, I have the current existing resources, which I will reference in terraform with the `data` source:\n\n### vpc\n- vpc with the name \"main\", which is my non default-vpc\n\n### subnets\n- 3 public subnets with tags Tier:public\n- 3 private subnets with tags Tier:private\n\n### nat gateway\n- nat gw with eip for private range and added to my private routing table 0.0.0.0/0 to natgw\n\n### rds\n- subnet group with the name \"private\" which is linked to my private subnets\n\n### route53\n- existing hosted zone\n\n### codestar connections \n- codestar connection linked to my github account:\n- https://eu-west-1.console.aws.amazon.com/codesuite/settings/connections\n- the connection id is defined in: var.codestar_connection_id = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n\n## Required Environment Variables\n\nGithub Personal Access Token:\n\nHead over to https://github.com/settings/tokens/new and create a token with the following scopes:\n- `admin:repo_hook`\n\nSet the environment variable as:\n\n```\n$ export TF_VAR_github_token=${your-github-pat}\n```\n\nwhich will be referenced in `infra/aws/eu-west-1/production/locals.tf`:\n\n```\n# locals.tf\nlocals {\n  github_token = var.github_token\n}\n```\n\nOther variables that needs replacement resides in `infra/aws/eu-west-1/production/variables.tf`:\n\n```\nvariable \"aws_region\" {}\nvariable \"codebuild_docker_image\" {}\nvariable \"codebuild_security_group_name\" {}\nvariable \"codepipeline_build_stage_name\" {}\nvariable \"codepipeline_deploy_stage_name\" {}\nvariable \"codepipeline_source_stage_name\" {}\nvariable \"codestar_connection_id\" {}\nvariable \"container_desired_count\" {}\nvariable \"container_port\" {}\nvariable \"container_reserved_task_memory\" {}\nvariable \"ecs_cluster_name\" {}\nvariable \"ecs_container_instance_type\" {}\nvariable \"ecs_tg_healthcheck_endpoint\" {}\nvariable \"environment_name\" {}\nvariable \"github_branch\" {}\nvariable \"github_repo_name\" {}\nvariable \"github_token\" {}\nvariable \"github_username\" {}\nvariable \"host_port\" {}\nvariable \"platform_type\" {}\nvariable \"rds_admin_username\" {}\nvariable \"rds_instance_type\" {}\nvariable \"rds_subnet_group_name\" {}\nvariable \"route53_hosted_zone\" {}\nvariable \"route53_record_set\" {}\nvariable \"service_hostname\" {}\nvariable \"service_name\" {}\nvariable \"service_name_short\" {}\nvariable \"ssh_keypair_name\" {}\nvariable \"vpc_name\" {}\n```\n\nAlso ensure your configuration matches your setup in:\n- `infra/aws/eu-west-1/production/providers.tf`\n- `infra/aws/eu-west-1/production/terraform-state.tf`\n\n## Notes\n\nI am using the admin credentials for the application to use to authenticate against rds (for this demo), but you can use something like ansible and the local-exec provisioner to provision a rds username and password like [here](https://github.com/ruanbekker/terraformfiles/blob/master/aws-cicd-ecs-codepipeline/existing-vpc-ecs-rds-new-dbuser-ansible-ssm/infra/rds.tf#L11-L40).\n\nI am also using `String` as the type for SSM, if you save secret information, you should be using `SecureString` and encrypt it with KMS, but for the demo I won't be doing that.\n\n\n## Deploy Infrastructure to AWS\n\nValidate:\n\n```\n$ terraform validate\nSuccess! The configuration is valid.\n```\n\nVariables isn't supported for backend, see [this issue](https://github.com/hashicorp/terraform/issues/13022#issuecomment-294262392), to use variables, you can [look at this example](https://github.com/ruanbekker/terraformfiles/tree/master/s3-backend-with-variables):\n\nInitialize:\n\n```\n$ terraform init -input=false\n```\n\nPlan:\n\n```\n$ terraform plan\n...\n  # aws_acm_certificate.cert will be created\n  # aws_acm_certificate_validation.validate will be created\n  # aws_alb.ecs will be created\n  # aws_alb_listener.http will be created\n  # aws_alb_listener.https will be created\n  # aws_alb_target_group.service_tg will be created\n  # aws_cloudwatch_log_group.ecs will be created\n  # aws_codebuild_project.build will be created\n  # aws_codepipeline.pipeline will be created\n  # aws_codepipeline_webhook.webhook will be created\n  # aws_codestarconnections_connection.github will be created\n  # aws_db_instance.prod will be created\n  # aws_ecr_repository.repo will be created\n  # aws_ecs_cluster.prod will be created\n  # aws_ecs_service.service will be created\n  # aws_ecs_task_definition.service will be created\n  # aws_iam_instance_profile.ecs_instance will be created\n  # aws_iam_role.codebuild_role will be created\n  # aws_iam_role.codepipeline_role will be created\n  # aws_iam_role.ecs_instance_role will be created\n  # aws_iam_role.ecs_task_role will be created\n  # aws_iam_role_policy.codebuild_policy will be created\n  # aws_iam_role_policy.codepipeline_policy will be created\n  # aws_iam_role_policy.ecs_instance_policy will be created\n  # aws_iam_role_policy.ecs_task_policy will be created\n  # aws_instance.ec2 will be created\n  # aws_lb_listener_rule.forward_to_tg will be created\n  # aws_route53_record.record[\"rbkr.xyz\"] will be created\n  # aws_route53_record.www will be created\n  # aws_s3_bucket.codepipeline_artifact_store will be created\n  # aws_security_group.alb will be created\n  # aws_security_group.codebuild will be created\n  # aws_security_group.ecs_instance will be created\n  # aws_security_group.rds_instance will be created\n  # aws_security_group_rule.alb_egress will be created\n  # aws_security_group_rule.container_port will be created\n  # aws_security_group_rule.ec2_egress will be created\n  # aws_security_group_rule.http will be created\n  # aws_security_group_rule.https will be created\n  # aws_security_group_rule.mysql will be created\n  # aws_security_group_rule.ssh will be created\n  # aws_ssm_parameter.database_host will be created\n  # aws_ssm_parameter.database_name will be created\n  # aws_ssm_parameter.database_password will be created\n  # aws_ssm_parameter.database_port will be created\n  # aws_ssm_parameter.database_user will be created\n  # github_repository_webhook.webhook will be created\n  # random_password.db_admin_password will be created\n  # random_shuffle.subnets will be created\n  # random_string.secret will be created\nPlan: 50 to add, 0 to change, 0 to destroy.\n```\n\nApply:\n\n```\n$ terraform apply -input=false -auto-approve\nApply complete! Resources: 55 added, 0 changed, 0 destroyed.\nReleasing state lock. This may take a few moments...\n\nOutputs:\n\naccount_id = \"xxxxxxxxxxxx\"\nalb_dns = \"ecs-prod-alb-xxxxxxxxxx.eu-west-1.elb.amazonaws.com\"\ncaller_arn = \"arn:aws:iam::xxxxxxxxxxxx:user/x\"\ncaller_user = \"AXXXXXXXXXXXXXXXXXXXXXX\"\ndb_address = \"ecs-prod-rds-instance.xxxxxxxxxxxx.eu-west-1.rds.amazonaws.com\"\nenvironment_name = \"prod\"\nservice_hostname = \"www.rbkr.xyz\"\n\n~/aws-terraform-cicd-java-springboot/infra/aws/eu-west-1/production main* 4m 24s\n```\n\nNow that your infrastructure is built, we can trigger our repo to start the pipeline:\n\n```\n$ git commit --allow-empty --message \"trigger pipeline\"\n$ git push origin main\n```\n\n## A Tour through our Infra\n\nWe can see our Pipeline when you navigate to CodePipeline:\n\n![](docs/screenshots/codepipeline-overview.png)\n\nWhen you select the pipeline to see our stages:\n\n![](docs/screenshots/codepipeline-detail-view.png)\n\nWe can view our ECS Cluster:\n\n![](docs/screenshots/ecs-cluster-view.png)\n\nOur task:\n\n![](docs/screenshots/ecs-task-view.png)\n\nAnd also check that our ACM Certificates was validated (but terraform did that already):\n\n![](docs/screenshots/acm-certificates.png)\n\n## Test the Application on AWS\n\nMake a request to view all the cars:\n\n```\n❯ curl -i https://www.rbkr.xyz/api/cars                                                                        \nHTTP/2 200 \ndate: Wed, 03 Mar 2021 15:29:41 GMT\ncontent-type: application/json\n\n[]\n```\n\nCreate a car:\n\n```\n❯ curl -i -H \"Content-Type: application/json\" -XPOST https://www.rbkr.xyz/api/cars -d '{\"make\": \"bmw\", \"model\": \"m3\"}'\nHTTP/2 200 \ndate: Wed, 03 Mar 2021 15:29:33 GMT\ncontent-type: application/json\n\n{\"id\":1,\"make\":\"bmw\",\"model\":\"m3\",\"createdAt\":\"2021-03-03T15:29:33.707+00:00\",\"updatedAt\":\"2021-03-03T15:29:33.707+00:00\"}\n```\n\nView all the cars:\n\n```\n❯ curl -i https://www.rbkr.xyz/api/cars                                                                        \nHTTP/2 200 \ndate: Wed, 03 Mar 2021 15:29:41 GMT\ncontent-type: application/json\n\n[{\"id\":1,\"make\":\"bmw\",\"model\":\"m3\",\"createdAt\":\"2021-03-03T15:29:34.000+00:00\",\"updatedAt\":\"2021-03-03T15:29:34.000+00:00\"}]\n```\n\nView the application status:\n\n```\n❯ curl -s https://www.rbkr.xyz/status | jq .\n{\n  \"status\": \"UP\",\n  \"components\": {\n    \"db\": {\n      \"status\": \"UP\",\n      \"details\": {\n        \"database\": \"MySQL\",\n        \"validationQuery\": \"isValid()\"\n      }\n    },\n    \"diskSpace\": {\n      \"status\": \"UP\",\n      \"details\": {\n        \"total\": 10501771264,\n        \"free\": 9604685824,\n        \"threshold\": 10485760,\n        \"exists\": true\n      }\n    },\n    \"ping\": {\n      \"status\": \"UP\"\n    }\n  }\n}\n```\n\n## Destroy Infrastructure on AWS:\n\nDestroy:\n\n```\n$ terraform destroy -auto-approve\nDestroy complete! Resources: 55 destroyed.\nReleasing state lock. This may take a few moments...\n```\n\n## Destroy Application running Locally:\n\n```\n$ docker-compose down\n```\n\n## Resources \n\n### AWS Resources\n\n- [Difference between Task Role and Execution Role](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_TaskDefinition.html)\n\n### Terraform Resources\n\n- [Shunit Tests for User Data](https://alexharv074.github.io/2020/01/31/unit-testing-a-terraform-user_data-script-with-shunit2.html)\n\n### Java Resources\n- [spring-testing-separate-data-source](https://www.baeldung.com/spring-testing-separate-data-source) and [github](https://github.com/eugenp/tutorials/tree/master/persistence-modules/spring-boot-persistence)\n- [testing-with-configuration-classes-and-profiles](https://spring.io/blog/2011/06/21/spring-3-1-m2-testing-with-configuration-classes-and-profiles)\n- [hibernate-ddl-auto-example](https://www.onlinetutorialspoint.com/hibernate/hbm2ddl-auto-example-hibernate-xml-config.html)\n- [cleaning-up-spring-boot-integration-tests-logs](https://ricardolsmendes.medium.com/cleaning-up-spring-boot-integration-tests-logs-5b2d0a5f29bc)\n- [docker-caching-strategies](https://testdriven.io/blog/faster-ci-builds-with-docker-cache/)\n\n## Credit\n\nHuge thanks to [Cobus Bernard](https://github.com/cobusbernard/aws-containers-for-beginners) for his webinar back in 2019, and for sharing his terraform source code, as I learned a LOT from him, and this example is based off his terraform structure.\n\nAlso great thanks to [callicoder](https://www.callicoder.com/spring-boot-rest-api-tutorial-with-mysql-jpa-hibernate/) for the rest api example which this example is based off.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruanbekker%2Faws-terraform-cicd-java-springboot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fruanbekker%2Faws-terraform-cicd-java-springboot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruanbekker%2Faws-terraform-cicd-java-springboot/lists"}