Github To Aws

Github To Aws

Reading time1 min
#cloud#devops#aws#githubactions#cicd#serverless

Streamlined Deployment: Direct GitHub to AWS with Native CI/CD

Coordinating code delivery from repo to runtime is a daily reality for engineering teams. Manual interventions slow everything down, regress errors, and rarely satisfy compliance or security requirements. Most “easy” CI/CD paths quickly devolve into layers of tangled scripts and third-party runners.

But—direct integration between GitHub Actions and AWS-native tooling can replace that entire stack. No self-hosted runners. No brittle glue code. No duplicate credential sprawl.


Pipeline Objectives

  • Reduce cognitive overhead: Integrate only what’s needed; eliminate extraneous tools.
  • Enforce release velocity: Each change merged to the release branch is deployed within minutes.
  • Centralize audit and security: All credentials isolated in GitHub Secrets and federated with AWS IAM roles—no long-lived admin keys.
  • End-to-end traceability: Every deployment, build, and infrastructure change fully logged and searchable.

Baseline Architecture

Most modern apps—Node.js APIs, SPA frontends, containerized services—fit the same deployment architecture:

[GitHub Repo] --> [GitHub Actions Workflows] --> [AWS Deployment Target]
                                         |
                      [GitHub Secrets] + [AWS IAM Role/Policy]

Target services include:

  • Elastic Beanstalk: Managed legacy and monolith workloads
  • ECS/EKS: Orchestrated Docker deployments
  • S3 + CloudFront: Static assets and frontends
  • CloudFormation/CDK/Terraform: Infrastructure versioning

Example: Node.js API Deployment to Elastic Beanstalk

Intermediate scripting and IAM configuration are where most teams go wrong. Assume a production Node.js API needs continuous deployment.

IAM Setup (minimum required)

  • AWS policy must grant these Beanstalk and S3 permissions:
{
  "Version": "2012-10-17",
  "Statement": [
    {"Effect": "Allow", "Action": [
      "elasticbeanstalk:*",
      "s3:PutObject",
      "s3:GetObject"
    ], "Resource": "*"}
  ]
}
  • Restrict further in production: attach user to a single S3 bucket and relevant Beanstalk apps only.

  • Issue an access key, and store three values in GitHub repository Secrets:

    • AWS_ACCESS_KEY_ID
    • AWS_SECRET_ACCESS_KEY
    • AWS_REGION

Note: Federation via OIDC is supported, but access keys suffice for workflows that only deploy.

.github/workflows/deploy.yml

This workflow automates build, test, package, upload, and deployment actions on each commit to main:

name: Deploy Node.js API to Elastic Beanstalk

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Pull code
        uses: actions/checkout@v3

      - name: AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Setup Node.js v16
        uses: actions/setup-node@v3
        with:
          node-version: '16' # Pin exact version for reproducibility

      - run: npm ci

      - name: Unit tests
        run: npm test

      - name: Package bundle (exclude dependencies for smaller zips)
        run: zip -r app.zip . -x "node_modules/*"

      - name: Upload to S3
        run: aws s3 cp app.zip s3://$EB_APP_NAME/$GITHUB_SHA.zip

      - name: Register Beanstalk App Version
        run: |
          aws elasticbeanstalk create-application-version \
            --application-name $EB_APP_NAME \
            --version-label $GITHUB_SHA \
            --source-bundle S3Bucket=$EB_APP_NAME,S3Key=$GITHUB_SHA.zip

      - name: Deploy to Beanstalk
        run: |
          aws elasticbeanstalk update-environment \
            --environment-name $EB_ENV_NAME \
            --version-label $GITHUB_SHA
        env:
          EB_APP_NAME: my-node-api
          EB_ENV_NAME: my-node-api-env

Known Issue: The Beanstalk CLI occasionally fails with “InvalidParameterValue” if old deployments are not cleaned up. Consider adding pruning scripts to clear unreferenced versions.


Infrastructure Changes via CloudFormation/CDK

Deployment isn’t just about code. Frequently, schema migrations, S3 bucket changes, or security group rules must be versioned safely alongside the app. Add a job for infra updates:

- name: Install AWS CDK
  run: npm install -g aws-cdk@2.132.0

- name: Deploy Infra
  run: |
    cdk synth
    cdk deploy --require-approval never

This ensures every stack change is atomic with application code—rollback with cdk destroy if any post-deploy checks fail.

Gotcha: CloudFormation stack updates can hang indefinitely on resource drift; always check CloudWatch for rollback events if the job stalls past 10–15 minutes.


Observability and Release Hygiene

  • Monitor deployments: Use GitHub Actions logs for CI state; CloudWatch for runtime exceptions.
  • Tag deployments with commit SHA or PR number. If you see Application update failed at step 'PostDeployment', search logs by this SHA.
  • Rotate IAM credentials quarterly; enable CloudTrail for all modification events.
  • Avoid dependency pin drift—lock Node.js and npm to fixed minor releases (node@16.20.x) via actions/setup-node inputs.

Trade-Offs and Alternatives

  • OIDC for Temporary Credentials: GitHub Actions now supports OIDC-based auth directly with AWS STS—preferred for organizations scaling to dozens of repos (see AWS documentation).
  • Serverless / Lambda: If minimizing infrastructure is the absolute goal, swap Beanstalk for Lambda and automate via AWS SAM CLI or the aws-actions/aws-sam-cli action.
  • Terraform: More advanced infra as code teams often switch from CloudFormation/CDK to Terraform for multi-cloud, at the cost of integrating a third-party state backend.

Final Tip

Test the full deployment process—including rollback—using non-production branches before ever touching main. Late-stage surprises (credential scoping, S3 bucket ACLs, locked-down IAM roles) are best caught with a complete dry run.


Reference Table: GitHub Actions to AWS Service Integrations

AWS ServiceUse CaseTypical Action/Plugin
Elastic BeanstalkMonolith API appsaws-cli, Beanstalk deploy scripts
ECS (Fargate/EC2)Containers/Microservicesdocker + aws-actions/amazon-ecs
S3 + CloudFrontSPA/static hostingaws s3 cp, CloudFront invalidation
CDK/CloudFormationIaC/infra provisionaws-cdk, aws-cli
Lambda/SAMServerless workloadsaws-sam-cli, aws-cli

Advanced deployments still have edge cases to address: cold starts, network boundary issues, provisioning race conditions. But for most teams, a tight GitHub-AWS CICD pipeline eliminates entire categories of failure and delay. Start lean, iterate for your environment, and rely on mature platform integrations—the multi-tool bloat is optional.