Skip to content

Commit

Permalink
feat!: Allow custom ECR image, rename use_ecr_image variable, and cle…
Browse files Browse the repository at this point in the history
…anup a few things
  • Loading branch information
baolsen committed Dec 10, 2024
1 parent 995d6cc commit d85ec1e
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 111 deletions.
File renamed without changes.
105 changes: 36 additions & 69 deletions README.md

Large diffs are not rendered by default.

46 changes: 46 additions & 0 deletions docs/GITHUB-AUTH-SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Setting up authentication ot GitHub

## 1. Decide on how to authenticate to GitHub

Here we focus on setting up a personal access token to authenticate to GitHub.
OAuth is also supported but not implemented / documented here.

<!-- TODO OAuth -->

### GitHub access token

Note that CodeBuild only supports 1 GitHub token to be configured for all CodeBuild projects in the same AWS Account and Region.

Therefore, when using multiple CodeBuild projects, you can configure the token once per region (not once per project).

There are a few approaches that you could take, choose one from the below.

#### 2. Create your GitHub token

Create a [GitHub personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token).
Make sure that the fine grained token has [these](https://docs.aws.amazon.com/codebuild/latest/userguide/access-tokens-github.html#access-tokens-github-prereqs) permissions.


#### 2a. Add the token to CodeBuild separately.

You would add the token as [documented here](https://docs.aws.amazon.com/codebuild/latest/userguide/access-tokens-github.html)
This is recommended if you do not want to maintain the token in Terraform.

#### 2b. Provide the token as an input Terraform variable

Note that although the variable is sensitive, the value will still be stored in Terraform state.

#### 2c. Use AWS Parameter Store

<!-- TODO there seems to be a SecretsManager option in the link above - worth investigating -->

Add the token to AWS Systems Manager Parameter Store, and configure this module to read it.
The module will add the token to Codebuild for you.

This is recommended if you have only a single project.

### Adding your token to Parameter Store (Optional)

Add it to AWS Systems Manager Parameter Store with the `SecureString` type.

[![Parameter Store configuration](https://github.com/cloudandthings/terraform-aws-github-runners/blob/main/docs/images/ssm.png)](https://github.com/cloudandthings/terraform-aws-github-runners/blob/main/docs/images/ssm.png )
2 changes: 1 addition & 1 deletion examples/advanced/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ module "github_runner" {
################################
security_group_ids = [aws_security_group.this.id]
use_ecr_image = true
create_ecr_repository = true
cloudwatch_logs_group_name = "/some/log/group"
}
```
Expand Down
2 changes: 1 addition & 1 deletion examples/advanced/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,6 @@ module "github_runner" {
################################

security_group_ids = [aws_security_group.this.id]
use_ecr_image = true
create_ecr_repository = true
cloudwatch_logs_group_name = "/some/log/group"
}
26 changes: 20 additions & 6 deletions iam.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
################################################################################
# Cloudwatch permissions
################################################################################
data "aws_iam_policy_document" "cloudwatch_required" {
# Cloudwatch permissions
statement {
sid = "AllowCreateLogGroup"
effect = "Allow"
Expand All @@ -26,9 +28,11 @@ resource "aws_iam_role_policy" "cloudwatch_required" {
policy = data.aws_iam_policy_document.cloudwatch_required.json
}

################################################################################
# VPC permissions
################################################################################
data "aws_iam_policy_document" "networking_required" {
count = local.has_vpc_config ? 1 : 0
# VPC permissions
statement {
sid = "AllowNetworkingDescribe"
effect = "Allow"
Expand Down Expand Up @@ -74,8 +78,10 @@ resource "aws_iam_role_policy" "networking_required" {
policy = data.aws_iam_policy_document.networking_required[0].json
}

################################################################################
# S3 permissions
################################################################################
data "aws_iam_policy_document" "s3_required" {
# S3 permissions
count = local.has_s3_log_bucket ? 1 : 0
statement {
effect = "Allow"
Expand All @@ -94,9 +100,11 @@ resource "aws_iam_role_policy" "s3_required" {
policy = data.aws_iam_policy_document.s3_required[0].json
}

################################################################################
# ECR permissions
################################################################################
data "aws_iam_policy_document" "ecr_required" {
count = var.use_ecr_image ? 1 : 0
# ECR permissions
count = local.use_ecr_repository ? 1 : 0
statement {
effect = "Allow"
actions = [
Expand All @@ -120,7 +128,7 @@ data "aws_iam_policy_document" "ecr_required" {
}

resource "aws_iam_role_policy" "ecr_required" {
count = var.use_ecr_image ? 1 : 0
count = local.use_ecr_repository ? 1 : 0
name = "${var.name}-ecr"
role = local.create_iam_role ? aws_iam_role.this[0].name : var.iam_role_name
policy = data.aws_iam_policy_document.ecr_required[count.index].json
Expand All @@ -140,13 +148,19 @@ data "aws_iam_policy_document" "assume_role" {
}
}

################################################################################
# Create role
################################################################################
resource "aws_iam_role" "this" {
count = local.create_iam_role ? 1 : 0
name = var.name
assume_role_policy = data.aws_iam_policy_document.assume_role[0].json
permissions_boundary = var.iam_role_permissions_boundary == null ? null : var.iam_role_permissions_boundary
}

################################################################################
# Custom permissions
################################################################################
resource "aws_iam_role_policy_attachment" "additional" {
for_each = var.iam_role_policies

Expand Down
6 changes: 0 additions & 6 deletions locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ locals {

subnet_arns = [for subnet_id in var.subnet_ids : "arn:aws:ec2:${local.aws_region}:${local.aws_account_id}:subnet/${subnet_id}"]

security_group_ids = (
length(var.security_group_ids) == 0
? try([aws_security_group.codebuild[0].id], [])
: concat(try([aws_security_group.codebuild[0].id], []), var.security_group_ids)
)

create_iam_role = var.iam_role_name == null

cloudwatch_logs_group_arn = (
Expand Down
71 changes: 57 additions & 14 deletions main.tf
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
################################################################################
# CodeBuild
################################################################################

resource "aws_codebuild_project" "this" {
name = var.name
description = var.description
build_timeout = var.build_timeout
service_role = local.create_iam_role ? aws_iam_role.this[0].arn : "arn:aws:iam::${local.aws_account_id}:role/${var.iam_role_name}"
service_role = (
local.create_iam_role
? aws_iam_role.this[0].arn
: "arn:aws:iam::${local.aws_account_id}:role/${var.iam_role_name}"
)

artifacts {
type = "NO_ARTIFACTS"
}

environment {
type = var.environment_type
compute_type = var.environment_compute_type
image = var.use_ecr_image ? "${aws_ecr_repository.this[0].repository_url}:latest" : var.environment_image
image_pull_credentials_type = var.use_ecr_image ? "SERVICE_ROLE" : "CODEBUILD"
type = var.environment_type
compute_type = var.environment_compute_type
image = (
local.use_ecr_repository
? data.aws_ecr_image.latest[0].image_uri
: var.environment_image
)
image_pull_credentials_type = local.use_ecr_repository ? "SERVICE_ROLE" : "CODEBUILD"
# privileged_mode = true
}

Expand Down Expand Up @@ -81,19 +93,35 @@ resource "aws_codebuild_webhook" "this" {
}
}

################################################################################
# Security Group
################################################################################

locals {
create_security_group = local.has_vpc_config && length(var.security_group_ids) == 0
security_group_name = coalesce(var.security_group_name, var.name)

security_group_ids = concat(
local.create_security_group
? [aws_security_group.codebuild[0].id]
: [],
var.security_group_ids
)
}

resource "aws_security_group" "codebuild" {
#checkov:skip=CKV2_AWS_5:access logging not required
count = local.has_vpc_config ? 1 : 0
count = local.create_security_group ? 1 : 0
vpc_id = var.vpc_id
name = var.name
name = local.security_group_name
description = "Security group for CodeBuild project ${var.name}"
tags = {
Name = var.name
Name = local.security_group_name
}
}

resource "aws_vpc_security_group_egress_rule" "codebuild" {
count = local.has_vpc_config ? 1 : 0
count = local.create_security_group ? 1 : 0
security_group_id = aws_security_group.codebuild[count.index].id

cidr_ipv4 = "0.0.0.0/0"
Expand All @@ -102,7 +130,7 @@ resource "aws_vpc_security_group_egress_rule" "codebuild" {
}

resource "aws_vpc_security_group_ingress_rule" "codebuild" {
count = local.has_vpc_config ? 1 : 0
count = local.create_security_group ? 1 : 0
security_group_id = aws_security_group.codebuild[count.index].id

cidr_ipv4 = "0.0.0.0/0"
Expand All @@ -112,12 +140,20 @@ resource "aws_vpc_security_group_ingress_rule" "codebuild" {
description = "Allow HTTPS traffic from ALL"
}

# TODO
################################################################################
# ECS Repository
################################################################################

locals {
use_ecr_repository = var.create_ecr_repository || var.ecr_repository_name
ecr_repository_name = coalesce(var.ecr_repository_name, var.name)
}

resource "aws_ecr_repository" "this" {
#checkov:skip=CKV_AWS_136:encryption not required
#checkov:skip=CKV_AWS_51:latest tag used by codebuild so tag needs to be overwritten
count = var.use_ecr_image ? 1 : 0
name = var.name
count = var.create_ecr_repository ? 1 : 0
name = local.ecr_repository_name

image_tag_mutability = "IMMUTABLE"

Expand All @@ -132,8 +168,15 @@ resource "aws_ecr_repository" "this" {
}
}

data "aws_ecr_image" "latest" {
count = local.use_ecr_repository ? 1 : 0

repository_name = local.ecr_repository_name
most_recent = true
}

resource "aws_ecr_lifecycle_policy" "policy" {
count = var.use_ecr_image ? 1 : 0
count = var.create_ecr_repository ? 1 : 0
repository = aws_ecr_repository.this[0].name

policy = <<-EOF
Expand Down
40 changes: 26 additions & 14 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,19 @@ variable "description" {
variable "environment_type" {
type = string
default = "LINUX_CONTAINER"
description = "Type of build environment to use for related builds. Valid values: LINUX_CONTAINER, LINUX_GPU_CONTAINER, WINDOWS_CONTAINER (deprecated), WINDOWS_SERVER_2019_CONTAINER, ARM_CONTAINER, LINUX_LAMBDA_CONTAINER, ARM_LAMBDA_CONTAINER"
description = "Type of build environment to use for related builds. Valid values: `LINUX_CONTAINER`, `LINUX_GPU_CONTAINER`, `WINDOWS_CONTAINER` (deprecated), `WINDOWS_SERVER_2019_CONTAINER`, `ARM_CONTAINER`, `LINUX_LAMBDA_CONTAINER`, `ARM_LAMBDA_CONTAINER`"
}

variable "environment_compute_type" {
type = string
default = "BUILD_GENERAL1_SMALL"
description = " Information about the compute resources the build project will use. Valid values: BUILD_GENERAL1_SMALL, BUILD_GENERAL1_MEDIUM, BUILD_GENERAL1_LARGE, BUILD_GENERAL1_2XLARGE, BUILD_LAMBDA_1GB, BUILD_LAMBDA_2GB, BUILD_LAMBDA_4GB, BUILD_LAMBDA_8GB, BUILD_LAMBDA_10GB. BUILD_GENERAL1_SMALL is only valid if type is set to LINUX_CONTAINER. When type is set to LINUX_GPU_CONTAINER, compute_type must be BUILD_GENERAL1_LARGE. When type is set to LINUX_LAMBDA_CONTAINER or ARM_LAMBDA_CONTAINER, compute_type must be BUILD_LAMBDA_XGB"
description = " Information about the compute resources the build project will use. Valid values: `BUILD_GENERAL1_SMALL`, `BUILD_GENERAL1_MEDIUM`, `BUILD_GENERAL1_LARGE`, `BUILD_GENERAL1_2XLARGE`, `BUILD_LAMBDA_1GB`, `BUILD_LAMBDA_2GB`, `BUILD_LAMBDA_4GB`, `BUILD_LAMBDA_8GB`, `BUILD_LAMBDA_10GB`. `BUILD_GENERAL1_SMALL` is only valid if type is set to `LINUX_CONTAINER`. When type is set to `LINUX_GPU_CONTAINER`, compute_type must be `BUILD_GENERAL1_LARGE`. When type is set to `LINUX_LAMBDA_CONTAINER` or `ARM_LAMBDA_CONTAINER`, compute_type must be `BUILD_LAMBDA_XGB`"
}

variable "environment_image" {
type = string
default = "aws/codebuild/amazonlinux2-x86_64-standard:5.0"
description = "Docker image to use for this build project. Valid values include Docker images provided by CodeBuild (e.g aws/codebuild/amazonlinux2-x86_64-standard:4.0), Docker Hub images (e.g., hashicorp/terraform:latest). If use_ecr_image is set to true, this value will be ignored and the ECR image location will be used."
description = "Docker image to use for this build project. Valid values include Docker images provided by CodeBuild (e.g `aws/codebuild/amazonlinux2-x86_64-standard:4.0`), Docker Hub images (e.g., `hashicorp/terraform:latest`). If `use_ecr_image` is set to true, this value will be ignored and the ECR image location will be used."
}

# logs
Expand All @@ -60,13 +60,13 @@ variable "create_cloudwatch_log_group" {
}

variable "cloudwatch_logs_group_name" {
description = "Name of the log group used by the codebuild project. If blank then a default is used."
description = "Name of the log group used by the CodeBuild project. If not specified then a default is used."
type = string
default = null
}

variable "cloudwatch_logs_stream_name" {
description = "Name of the log stream used by the codebuild project. If blank then a default is used."
description = "Name of the log stream used by the CodeBuild project. If not specified then a default is used."
type = string
default = null
}
Expand All @@ -78,7 +78,7 @@ variable "cloudwatch_log_group_retention_in_days" {
}

variable "s3_logs_bucket_name" {
description = "Name of the S3 bucket to store logs in. If null then logging to S3 will be disabled."
description = "Name of the S3 bucket to store logs in. If not specified then logging to S3 will be disabled."
type = string
default = null
}
Expand All @@ -91,25 +91,31 @@ variable "s3_logs_bucket_prefix" {
# vpc
variable "vpc_id" {
type = string
description = "The VPC ID for AWS Codebuild to launch ephemeral instances in."
description = "The VPC ID for AWS CodeBuild to launch ephemeral instances in."
default = null
}

variable "subnet_ids" {
type = list(string)
description = "The list of Subnet IDs for AWS Codebuild to launch ephemeral EC2 instances in."
description = "The list of Subnet IDs for AWS CodeBuild to launch ephemeral EC2 instances in."
default = []
}

variable "security_group_name" {
description = "Name to use on created Security Group. Defaults to `name`"
type = string
default = null
}

variable "security_group_ids" {
type = list(string)
description = "The list of Security Group IDs for AWS Codebuild to launch ephemeral EC2 instances in."
description = "The list of Security Group IDs for AWS CodeBuild to launch ephemeral EC2 instances in."
default = []
}

# IAM
variable "iam_role_name" {
description = "Name of the IAM role to be used, if one is not given a role will be created"
description = "Name of the IAM role to be used. If not specified then a role will be created"
type = string
default = null
}
Expand All @@ -128,13 +134,13 @@ variable "iam_role_permissions_boundary" {

# GitHub
variable "github_personal_access_token" {
description = "The GitHub personal access token to use for accessing the repository"
description = "The GitHub personal access token to use for accessing the repository. If not specified then GitHub auth must be configured separately."
type = string
default = null
}

variable "github_personal_access_token_ssm_parameter" {
description = "The GitHub personal access token to use for accessing the repository"
description = "The GitHub personal access token to use for accessing the repository. If not specified then GitHub auth must be configured separately."
type = string
default = null
}
Expand All @@ -147,8 +153,14 @@ variable "kms_key_id" {
}

# Custom image
variable "use_ecr_image" {
description = "Determines whether the build image will be pulled from ECR, if set to true an ECR repository will be created and an image needs to be pushed to it before running the build project"
variable "create_ecr_repository" {
description = "If set to true then an ECR repository will be created, and an image needs to be pushed to it before running the build project"
type = string
default = false
}

variable "ecr_repository_name" {
description = "Name of the ECR repository to create or use. If not specified and `create_ecr_repository` is true, then a default is used."
type = string
default = null
}

0 comments on commit d85ec1e

Please sign in to comment.