{"id":31667982,"url":"https://github.com/chrispsheehan/aws-serverless-github-deploy","last_synced_at":"2026-04-16T18:03:41.886Z","repository":{"id":316260643,"uuid":"1049633587","full_name":"chrispsheehan/aws-serverless-github-deploy","owner":"chrispsheehan","description":"Terraform + GitHub Actions for AWS serverless: Lambda + API Gateway with CodeDeploy blue/green (all-at-once/canary/linear) and provisioned concurrency (none/fixed/autoscaled), shipped via OIDC + Just recipes.","archived":false,"fork":false,"pushed_at":"2026-04-10T12:08:54.000Z","size":460,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-10T12:17:08.445Z","etag":null,"topics":["apigateway","aws-serverless","cicd","codedeploy","lamda","terraform","terragrunt"],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chrispsheehan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-03T09:12:36.000Z","updated_at":"2026-03-31T11:07:13.000Z","dependencies_parsed_at":"2025-09-23T16:30:47.313Z","dependency_job_id":null,"html_url":"https://github.com/chrispsheehan/aws-serverless-github-deploy","commit_stats":null,"previous_names":["chrispsheehan/aws-serverless-github-deploy"],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/chrispsheehan/aws-serverless-github-deploy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrispsheehan%2Faws-serverless-github-deploy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrispsheehan%2Faws-serverless-github-deploy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrispsheehan%2Faws-serverless-github-deploy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrispsheehan%2Faws-serverless-github-deploy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chrispsheehan","download_url":"https://codeload.github.com/chrispsheehan/aws-serverless-github-deploy/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrispsheehan%2Faws-serverless-github-deploy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31749763,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-13T09:16:15.125Z","status":"ssl_error","status_checked_at":"2026-04-13T09:16:05.023Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["apigateway","aws-serverless","cicd","codedeploy","lamda","terraform","terragrunt"],"created_at":"2025-10-08T00:50:13.263Z","updated_at":"2026-04-13T11:00:56.370Z","avatar_url":"https://github.com/chrispsheehan.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# aws-serverless-github-deploy\n\n**Terraform + GitHub Actions for AWS serverless deployments.**  \nLambda + ECS with CodeDeploy rollouts, plus provisioned concurrency controls for Lambda — driven by clean module variables and `just` recipes.\n\n---\n\n## 🚀 setup roles for ci\n\n```sh\njust tg ci aws/oidc apply\njust tg dev aws/oidc apply\njust tg prod aws/oidc apply\n```\n\nThe `ci` OIDC role is intentionally narrower than the `dev` and `prod` roles. In this repo it is limited to build-artifact management, including the shared code bucket, IAM interactions needed by the existing CI flow, and publishing container images to ECR. It is not the repo's broad deployment role.\n\n## 🧱 prerequisite network\n\nThe AWS account must already have the landing-zone or StackSet network in place before deploying this repo.\n\n- the Terraform in this repo reads the VPC and subnets with `data` sources rather than creating them\n- the expected VPC and subnets must therefore already exist\n- the private subnets must be tagged so the module lookups can find them, for example with names matching `*private*`\n\nIf those shared network resources do not exist yet, the infra applies in this repo will fail during data lookup.\n\nThe repo `network` module also owns the shared internal ALB and shared HTTP API Gateway surface used by ECS services:\n\n- HTTP API\n- default API stage\n- VPC link\n- internal ALB and target groups\n\nThe `api` module is Lambda-specific and plugs the Lambda integration and root routes into that shared API.\n\nTerragrunt also provides a shared default ECR repository name to ECS task modules:\n\n- shared artifact base: `dev -\u003e \u003caccount\u003e-\u003cregion\u003e-\u003cproject\u003e-dev`, otherwise `\u003caccount\u003e-\u003cregion\u003e-\u003cproject\u003e-ci`\n- default ECR repository: `\u003cartifact_base\u003e-ecs-worker`\n- override it in `infra/live/\u003cenvironment\u003e/environment_vars.hcl` only if the repository naming diverges from that convention\n- the concrete ECS worker task wrapper defaults `local_tunnel = false` and `xray_enabled = false` unless you explicitly set them\n\nThe reusable deploy workflows follow the same split: `prod` deploy wrappers read shared artifact resources from `ci`, but still apply `prod` infrastructure stacks using the repo's directory-derived service and lambda matrices.\n\nFor code-only release deploys, pass explicit release versions for each runtime you want to roll out. In particular, ECS code-only deploys should provide an `ecs_version` rather than relying on a Lambda-version fallback.\n\n## 🛠️ local plan some infra\n\nGiven a terragrunt file is found at `infra/live/dev/aws/api/terragrunt.hcl`\n\n```sh\njust tg dev aws/api plan\n```\n\n## ⚙️ types of lambda provisioned concurrency\n\n```hcl\nmodule \"lambda_example\" {\n  source = \"../lambda\"\n  ...\n  provisioned_config = var.your_provisioned_config\n}\n```\n\n#### ✅ [default] No provisioned lambdas\n- use case: background processes\n- we can handle an initial lag while lambda warms up/boots\n```hcl\nprovisioned_config = {\n    fixed                = 0\n    reserved_concurrency = 2 # only allow 2 concurrent executions THIS ALSO SERVES AS A LIMIT TO AVOID THROTTLING\n}\n```\n\n#### 🔒 X number of provisioned lambdas\n- use case: high predictable usage\n- we never want lag due to warm up and can predict traffic\n```hcl\nprovisioned_config = {\n    fixed                = 10\n    reserved_concurrency = 50\n}\n```\n\n#### 📈 Scale provisioning when usage exceeds % tolerance \n- use case: react to traffic i.e. api backend\n- limit the cost with autoscale.max\n- ensure minimal concurrency (no cold starts) with autoscale.min\n- set tolerance to amount of used concurrent executions. Below will trigger when 70% are used and add more to meet demands.\n- set cool down seconds to reasonable time before you would like the system to react.\n```hcl\nprovisioned_config = {\n    auto_scale = {\n        max               = 3,\n        min               = 1,\n        trigger_percent   = 70\n        cool_down_seconds = 60\n    }\n}\n```\n- before scaling the lambda alias will match the minmum value\n![a](docs/lambda-config-before.png)\n- when the trigger percent is exceeded the lambda moves into `In progress (1/2)` state as an additional provisioned lambda is added.\n![a](docs/lamba-scaling-up.png)\n- after scaling the lambda alias will show an additional provisioned lambda\n![a](docs/lambda-config-after.png)\n\n\n## 🚦 types of lambda deploy\n\n```hcl\nmodule \"lambda_example\" {\n  source = \"../_shared/lambda\"\n  ...\n  deployment_config = var.your_deployment_config\n}\n```\n\n#### ⚡ [default] All at once (fastest):\n\n- use case: background processes\n```hcl\ndeployment_config = {\n    strategy = \"all_at_once\"\n}\n```\n\n#### 🐤 canary deployment:\n\n- use case: api or service serving traffic\n- incrementally rolls out new version to 10% of lambdas and rolls back if errors detected. If not goes to 100%.\n- waits to make a decision on health after 1 minute\n```hcl\ndeployment_config = {\n    strategy         = \"canary\"\n    percentage       = 10\n    interval_minutes = 1\n}\n```\n\n#### 📶 linear deployment:\n\n- use case: api or service serving traffic\n- checks for lambda health on 10% of lambdas and rolls back if errors detected\n- rolls out changes on increments of 1 minute\n```hcl\ndeployment_config = {\n    strategy         = \"linear\"\n    percentage       = 10\n    interval_minutes = 1\n}\n```\n\n## 🚦 types of ecs deploy\n\n```hcl\nmodule \"service_example\" {\n  source = \"../_shared/service\"\n  ...\n  deployment_strategy = var.your_deployment_strategy\n}\n```\n\n#### ⚡ [default] All at once:\n\n- use case: internal services, queue workers, low-risk changes\n- for load-balanced ECS services this uses CodeDeploy and shifts traffic in one step\n```hcl\ndeployment_strategy = \"all_at_once\"\n```\n\n#### 🐤 canary deployment:\n\n- use case: HTTP services behind the load balancer\n- shifts 10% of traffic for 5 minutes before moving to 100%\n```hcl\ndeployment_strategy = \"canary\"\n```\n\n#### 📶 linear deployment:\n\n- use case: steady rollout with smaller blast radius\n- shifts traffic 10% every minute until complete\n```hcl\ndeployment_strategy = \"linear\"\n```\n\n#### 🟦🟩 blue/green deployment:\n\n- use case: explicit blue/green semantics while still using the default ECS all-at-once traffic switch\n- currently maps to the ECS CodeDeploy all-at-once config\n```hcl\ndeployment_strategy = \"blue_green\"\n```\n\n- ECS CodeDeploy is only created for load-balanced ECS services in `_shared/service`\n- internal ECS services without load balancer integration should use native ECS rolling updates instead\n- the shared ECS service resource ignores `task_definition` drift so later infra applies do not revert the live task revision after either a rolling deploy or a CodeDeploy rollout\n- the deployment workflow:\n  - applies the new `task_*` revision\n  - if the service has CodeDeploy resources, reads `codedeploy_app_name` and `codedeploy_deployment_group_name` from `service_*`\n  - renders [`appspec-ecs.yml`](appspec-ecs.yml)\n  - uploads the AppSpec to the code bucket\n  - runs `just ecs-deploy`\n  - otherwise updates the ECS service to the new task definition with a native rolling deploy\n\n## 🔥↩️ deployment roll-back\n\n- use cloudwatch metrics and alarms to automatically roll-back a deployment\n- create a [cloudwatch_metric_alarm](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) resource and pass in as per below\n\n```hcl\nmodule \"lambda_example\" {\n  source = \"../_shared/lambda\"\n  ...\n  codedeploy_alarm_names = [\n    local.api_5xx_alarm_name\n  ]\n}\n```\n- the ECS shared service module accepts the same `codedeploy_alarm_names` input\n- if the alarm triggers during a deployment you will see the below in the CI\n\n```\n📦 Running: lambda-deploy\n🚀 Deployment started: d-40UUQH3DF\nAttempt 1: Deployment status is InProgress\nAttempt 2: Deployment status is InProgress\nAttempt 3: Deployment status is InProgress\nAttempt 4: Deployment status is InProgress\nAttempt 5: Deployment status is Stopped\n❌ Deployment d-40UUQH3DF failed or was stopped.\n-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n|                                                                                                                    GetDeployment                                                                                                                    |\n+--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n|  ErrorCode   |  ALARM_ACTIVE                                                                                                                                                                                                                        |\n|  ErrorMessage|  One or more alarms have been activated according to the Amazon CloudWatch metrics you selected, and the affected deployments have been stopped. Activated alarms: \u003cdev-aws-serverless-github-deploy-api-api-v2-5xx-rate-critical\u003e   |\n|  Status      |  Stopped                                                                                                                                                                                                                             |\n+--------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\nerror: Recipe `lambda-deploy` failed with exit code 1\nError: Process completed with exit code 1.\n\n```\n\n## 🚢 deployment strategies\n\n- Infrastructure and feature code deployments (via codedeploy) are completely decoupled.\n- Initial infrastructure deployments deploys `infra/modules/aws/_shared/lambda/bootstrap/index.py` which serves as a place-holder.\n- Initial ECS infrastructure deployments can use a bootstrap task, while the deploy workflow later registers a real `task_*` revision and promotes it via CodeDeploy.\n- The code deploy app and group are also deployed, which is the mechanism used to deploy the real builds.\n- Subsequent re-runs of the infrastructure deployments will not update the code.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrispsheehan%2Faws-serverless-github-deploy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrispsheehan%2Faws-serverless-github-deploy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrispsheehan%2Faws-serverless-github-deploy/lists"}