Skip to main content

Command Palette

Search for a command to run...

AWSCloudAction: Automate Your CI/CD Workflows Deploying GitHub Actions Runners on AWS with Terraform

Published
AWSCloudAction: Automate Your CI/CD Workflows Deploying GitHub Actions Runners on AWS with Terraform
R

I'm Rudraksh Laddha โ€” a DevOps engineer and emerging full-stack developer, passionate about building scalable, reliable systems that solve real-world problems.

With a solid foundation in cloud infrastructure automation using tools like Kubernetes, Docker, Terraform, and AWS, I thrive in environments where efficiency, resilience, and automation are key.

But my journey doesn't stop at infrastructure. I'm actively expanding into full-stack development, building dynamic applications using React, Node.js, and MongoDB. Whether it's designing cloud-native CI/CD pipelines or developing intuitive user interfaces, I enjoy creating end-to-end solutions โ€” from server to screen.

Right now, I'm: ๐Ÿงฉ Building full-stack applications that merge DevOps reliability with engaging frontend experiences ๐Ÿ› ๏ธ Contributing to open-source projects, learning through collaboration and real-world scenarios ๐Ÿš€ Growing Virendana Ui, my own UI library focused on expressive, clean design systems ๐Ÿš€ Growing Learn Virendana, where I share my personalized learning journey โ€” from beginner to experienced ๐ŸŽฎ Developing side projects like 2048 Rush, blending product thinking with scalable infrastructure My long-term goal? To bridge DevOps and development โ€” building products that are not just functional and fast, but also resilient, beautiful, and ready for scale.

In the fast-paced world of software development, Continuous Integration and Continuous Deployment (CI/CD) have become crucial for delivering high-quality software quickly. GitHub Actions is a powerful tool that enables developers to automate workflows directly within their GitHub repositories. This article will guide you through automating the deployment of GitHub Actions runners on AWS using Terraform, an Infrastructure as Code (IaC) tool.

Why Deploy Your Own GitHub Actions Runners?

While GitHub offers hosted runners, there are several benefits to deploying your own:

  • Customization: Tailor the environment to suit your project requirements.

  • Cost Efficiency: Reduce costs, especially for frequent builds.

  • Performance Control: Ensure your runners meet performance standards for your workflows.

Project Architecture

The architecture of our project consists of several key components:

  1. AWS EC2 Instance: The GitHub Actions runner will be hosted on an EC2 instance.

  2. IAM Role: An IAM role with permissions for the runner to execute jobs and access AWS resources.

  3. Security Group: A security group to control access to the EC2 instance.

  4. CloudWatch: Monitoring and logging capabilities to track the runnerโ€™s performance.

Screenshot from 2024-10-07 13-32-26.png

Prerequisites

Before you begin, ensure you have the following:

  • An AWS account.

  • Terraform installed on your local machine.

  • A GitHub account and a repository to configure the Actions runner.

  • A GitHub Personal Access Token with the repo and workflow scopes.

  • An SSH key pair for accessing the EC2 instance.

Step-by-Step Implementation

Step 1: Setting Up the Terraform Project

Create a directory for your Terraform project:

mkdir github-actions-runner-aws
cd github-actions-runner-aws

Create the following necessary files:

github-actions-runner-aws/
โ”œโ”€โ”€ main.tf
โ”œโ”€โ”€ variables.tf
โ”œโ”€โ”€ iam.tf
โ”œโ”€โ”€ ec2.tf
โ”œโ”€โ”€ security_group.tf
โ””โ”€โ”€ cloudwatch.tf

Step 2: Define the Terraform Configuration

main.tf

This file is the main entry point for your Terraform configuration, tying together all resources.

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
  required_version = ">= 1.0"
}

provider "aws" {
  region = var.region
}

module "iam_role" {
  source = "./iam.tf"
}

module "ec2_instance" {
  source     = "./ec2.tf"
  iam_role   = module.iam_role.iam_role_name
}

module "security_group" {
  source = "./security_group.tf"
}

module "cloudwatch" {
  source      = "./cloudwatch.tf"
  instance_id = module.ec2_instance.instance_id
}
  • terraform block: Specifies the required provider (AWS) and version.

  • provider block: Configures the AWS provider with the specified region.

  • Modules: Includes IAM role, EC2 instance, security group, and CloudWatch configurations.

variables.tf

This file defines the required variables for your configuration.

variable "region" {
  description = "The AWS region to deploy the resources"
  type        = string
  default     = "us-east-1"
}

variable "instance_type" {
  description = "EC2 instance type"
  type        = string
  default     = "t2.micro"
}

variable "github_token" {
  description = "GitHub personal access token for authentication"
  type        = string
}

variable "runner_name" {
  description = "Name of the GitHub Actions runner"
  type        = string
  default     = "my-runner"
}
  • region: Defines the AWS region for deployment.

  • instance_type: Specifies the EC2 instance type.

  • github_token: Token for GitHub authentication.

  • runner_name: Name of the GitHub Actions runner.

iam.tf

This file creates an IAM role with the necessary policies for your GitHub Actions runner.

resource "aws_iam_role" "github_runner_role" {
  name = "github-runner-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Principal = {
          Service = "ec2.amazonaws.com"
        }
        Effect = "Allow"
        Sid    = ""
      },
    ]
  })
}

resource "aws_iam_policy" "github_runner_policy" {
  name        = "github-runner-policy"
  description = "Policy for GitHub runner to access CloudWatch logs"
  policy      = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "logs:*",
        ]
        Resource = "*"
      },
    ]
  })
}

resource "aws_iam_role_policy_attachment" "attach_policy" {
  policy_arn = aws_iam_policy.github_runner_policy.arn
  role       = aws_iam_role.github_runner_role.name
}
  • IAM Role: Allows EC2 to assume the role.

  • IAM Policy: Grants access to CloudWatch logs.

  • Role Policy Attachment: Attaches the policy to the role.

ec2.tf

This file defines the EC2 instance that will host the GitHub Actions runner.

resource "aws_instance" "github_runner" {
  ami           = "ami-0c55b159cbfafe1f0" # Update this with the latest Amazon Linux 2 AMI
  instance_type = var.instance_type
  key_name      = var.key_name
  subnet_id     = aws_subnet.public_subnet.id

  security_groups = [
    aws_security_group.github_runner_sg.name
  ]

  iam_instance_profile = aws_iam_role.github_runner_role.name

  tags = {
    Name = var.runner_name
  }

  provisioner "remote-exec" {
    inline = [
      "cd /home/ec2-user && mkdir actions-runner && cd actions-runner",
      "curl -o actions-runner-linux-x64-2.283.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.283.0/actions-runner-linux-x64-2.283.0.tar.gz",
      "tar xzf ./actions-runner-linux-x64-2.283.0.tar.gz",
      "./bin/installdependencies.sh",
      "./config.sh --url https://github.com/YOUR_USERNAME/YOUR_REPOSITORY --token ${var.github_token}",
      "sudo ./svc.sh install",
      "sudo ./svc.sh start",
    ]
  }
}
  • AMI: Specify the Amazon Machine Image for the EC2 instance.

  • Instance Type: Set the instance type (e.g., t2.micro).

  • Security Groups: Associates the instance with the security group.

  • IAM Role: Attaches the IAM role to the EC2 instance.

  • Provisioner: Runs commands to set up the GitHub Actions runner on the instance.

security_group.tf

This file configures a security group for your EC2 instance.

resource "aws_security_group" "github_runner_sg" {
  name        = "github-runner-sg"
  description = "Allow SSH and HTTP traffic"
  vpc_id      = aws_vpc.my_vpc.id

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] # Replace with your IP range for security
  }

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
  • Security Group: Defines rules for inbound and outbound traffic.

  • Ingress Rules: Allow SSH (port 22) and HTTP (port 80) traffic.

  • Egress Rules: Allow all outbound traffic.

cloudwatch.tf

This file configures CloudWatch for monitoring and logging.

resource "aws_cloudwatch_log_group" "github_runner_logs" {
  name              = "/aws/ec2/github-runner"
  retention_in_days = 14
}

resource "aws_cloudwatch_metric_alarm" "high_cpu_alarm" {
  alarm_name          = "HighCPUUsage"
  alarm_description   = "This alarm triggers when CPU usage exceeds 80%"
  namespace           = "AWS/EC2"
  metric_name         = "CPUUtilization"
  dimensions          = {
    InstanceId = aws_instance.github_runner.id
  }
  statistic           = "Average"
  period              = 300
  evaluation

_periods  = 1
  threshold           = 80
  comparison_operator = "GreaterThanThreshold"

  alarm_actions = ["arn:aws:sns:us-east-1:123456789012:MySNSTopic"] # Replace with your SNS topic ARN
}
  • CloudWatch Log Group: Creates a log group for the runner logs.

  • CloudWatch Alarm: Monitors CPU usage and triggers if it exceeds a defined threshold.

Step 3: Initialize and Apply the Terraform Configuration

Run the following commands to deploy the infrastructure:

# Initialize Terraform
terraform init

# Plan the deployment
terraform plan -var "github_token=YOUR_GITHUB_TOKEN"

# Apply the configuration
terraform apply -var "github_token=YOUR_GITHUB_TOKEN"

Step 4: Configure Your GitHub Repository

After the EC2 instance is deployed, go to your GitHub repository settings:

  1. Navigate to Settings > Actions > Runners.

  2. Click on Add Runner.

  3. Follow the instructions provided in the runner configuration to register your newly created runner.

Monitoring and Logging

Consider implementing monitoring and logging for your runners. Utilize CloudWatch to track metrics and logs. Set up alarms to notify you of performance issues or errors.

Conclusion

By deploying your own GitHub Actions runners on AWS using Terraform, you gain enhanced control over your CI/CD processes. This setup allows you to optimize costs, customize your build environments, and maintain performance standards. As software development continues to evolve, having the ability to deploy and manage your own CI/CD infrastructure will give you a significant advantage.

By leveraging Terraform for infrastructure management, you ensure that your deployments are reproducible, scalable, and easily maintainable. Whether you are working on small projects or large-scale applications, deploying your own GitHub Actions runners can enhance your development workflow significantly.


More from this blog

R

Rudraksh.tech

67 posts