Skip to content

MGhaith/Nodejs-Service-Deployment

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

55 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Node.js Service Deployment

Deploy a Node.js Service to a remote server using GitHub Actions

Project structure

├── .github
│   └── workflows
│       └── deploy_service.yml   # Github Actions CI/CD workflow   
├── ansible                      # Ansible playbooks and inventory
├── node_app                     # Node.js application code
└── terraform                    # Terraform configuration files    

Deployment

Prerequisites

Setup

1. Clone the project's GitHub repository.

Clone the repository:

git clone https://github.com/MGhaith/nodejs-service-deployment.git
cd nodejs-service-deployment

2. Create a Github Repository

You need this repository to store the project code, trigger the deployment workflow, and store secrets.

  1. Create a new repository on GitHub for the project.
  2. Generate an SSH key pair, if you don't one already.
    ssh-keygen -t rsa -P "" -f ~/.ssh/id_rsa
    
  3. Create a new secret in the repository settings (Settings > Secrets and variables > Actions > New repository secret).
    • Name: SSH_PRIVATE_KEY
    • Value: Your private SSH key (contents of ~/.ssh/id_rsa)
    • Name: SSH_PUBLIC_KEY
    • Value: Your public SSH key (contents of ~/.ssh/id_rsa.pub)

3. Create S3 bucket and DynamoDB table for Terraform state

  1. Log in to the AWS Management Console.
  2. Navigate to the S3 service and create a new S3 bucket.
    • Bucket name: node-app-terraform-state-<Your AWS Account ID> (Replace <Your AWS Account ID> with your AWS Account ID)
    • Region: us-east-1
    • Enable versioning
    • Enable public access block
    • Create bucket
  3. Navigate to the S3 service and create a new DynamoDB table for state locking
    • Table name: node-app-terraform-locks
    • Partition key: LockID (String)
    • Create table
  4. update terraform\backend.tf with your bucket name and DynamoDB table name.
    terraform {
      required_version = ">= 1.13.0"
    
      backend "s3" {
        bucket         = "node-app-terraform-state-<Your AWS Account ID>" # Change this
        key            = "global/terraform.tfstate"                     
        region         = "us-east-1"                                    
        dynamodb_table = "node-app-terraform-locks" # And this 
        encrypt        = true                                           
      }
    }

4. Create IAM Role for OIDC

  1. Log in to the AWS Management Console.

  2. Navigate to the IAM service.

  3. Create a new Web identity role

    • Trusted entity type: Web identity
    • Identity provider: token.actions.githubusercontent.com
    • Audience: sts.amazonaws.com
    • GitHub organization: Your Github Username or Your Github Organization
    • GitHub repository: Your repository name
  4. In the new role you created add the following inline policy:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "EC2FullAccess",
                "Effect": "Allow",
                "Action": "ec2:*",
                "Resource": "*"
            },
            {
                "Sid": "STSGetCallerIdentity",
                "Effect": "Allow",
                "Action": "sts:GetCallerIdentity",
                "Resource": "*"
            },
            {
                "Sid": "TerraformS3Backend",
                "Effect": "Allow",
                "Action": [
                    "s3:GetObject",
                    "s3:PutObject",
                    "s3:DeleteObject",
                    "s3:ListBucket"
                ],
                "Resource": [
                    "<Your S3 Bucket Name ARN>",
                    "<Your S3 Bucket Name ARN>/*"
                ]
            },
            {
                "Sid": "TerraformDynamoDBLock",
                "Effect": "Allow",
                "Action": [
                    "dynamodb:GetItem",
                    "dynamodb:PutItem",
                    "dynamodb:DeleteItem",
                    "dynamodb:UpdateItem"
                ],
                "Resource": "<Your DynamoDB Table ARN>"
            }
        ]
    }

    Note: Replace <Your S3 Bucket Name ARN> and <Your DynamoDB Table ARN> with your State Bucket ARN and DynamoDB Table ARN created for Terraform remote state (./terraform/backend.tf).

  5. Copy the Role ARN, we will need it later.

5. Update project files.

  1. In .github\workflows\deploy_service.yml, change the role-to-assume value to your role ARN.

    - name: Configure AWS credentials via OIDC (Terraform)
      uses: aws-actions/configure-aws-credentials@v4
      with:
        role-to-assume: arn:aws:iam::<Account-ID>:role/<Role-Name> # Change this to your role ARN
        aws-region: us-east-1
  2. In ansible\roles\app\tasks\main.yml, update the repo URL to point to your new repository.

    - name: Clone repo into tmp
      git:
        # Change this to your repository URL
        repo: "https://github.com/yourusername/yourrepository.git"
        dest: /tmp/node-service-repo
        version: main

6. Push changes to trigger deployment.

  1. Commit and push your changes to the main branch of the repository you created.
    git add .
    git commit -m "Deploy Node.js service"
    git push origin main
    
  2. Check the Actions tab in your repository to monitor the deployment progress.

Infrastructure Destruction

To destroy the infrastructure created by this project, follow these steps:

  1. Navigate to the project directory.
  2. Ensure you have Terraform installed and configured with AWS credentials.
  3. Run the following command to destroy the resources:
    cd terraform
    terraform destroy
    
  4. Enter you public key content from ~/.ssh/id_rsa.pub and confirm the destruction when prompted.

Node.js Application

The Node.js service is a minimal Express app with one endpoint:

GET /
Response: "Hello, world!"
  • For local testing:
    cd node_app
    npm install
    node index.js

Note: You can modify the Node.js application code according to your preferences. However, when making significant changes to the application structure or dependencies, make sure to review and update the ansible\roles\app\tasks\main.yml file accordingly to ensure proper deployment.

Github Actions Workflow

The GitHub Actions workflow (deploy_service.yml) automates the deployment process through three main jobs:

1. Terraform Job

  • Triggered on push to main branch (excluding README and git files)
  • Sets up Terraform and AWS credentials via OIDC
  • Deploys EC2 instance using Terraform configurations
  • Outputs the server IP for use in subsequent jobs

2. Ansible Job

  • Runs after successful Terraform deployment
  • Configures SSH access using repository secrets
  • Generates Ansible inventory with server IP and private key secret
  • Runs Ansible playbook to install and configure the Node.js service

3. Cleanup Job

  • Only runs if previous jobs fail
  • Destroys AWS resources using Terraform
  • Ensures no orphaned resources remain in case of deployment failure

The workflow uses repository secrets (SSH_PRIVATE_KEY and SSH_PUBLIC_KEY) for secure access and AWS OIDC for authentication. It follows IaC principles by using Terraform for infrastructure provisioning and Ansible for configuration management.

Secrets & Security Notes

  • The SSH private key and public key must be kept as Github secret, and never commit it to the repo.
  • The ANSIBLE_HOST_KEY_CHECKING=False in .github\workflows\deploy_service.yml is used in CI for ephemeral runners for the ansible job.

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Deploy a Node.js Service to a remote server using GitHub Actions

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published