This guide covers AWS account setup, IAM configuration, and GitHub environment configuration for repository administrators. For day-to-day CI/CD and merge workflows, see the Maintainers Guide.
The Build (CodeBuild) workflow runs the full project build (mise run build) on AWS CodeBuild. It is triggered manually via workflow_dispatch and gated by a protected GitHub environment that requires non-self-approval.
The workflow runs on a GitHub-hosted runner, authenticates to AWS via OIDC, and invokes CodeBuild's StartBuild API using the aws-codebuild-run-build action. CloudWatch logs are streamed back to the GitHub Actions console. No webhooks are involved.
workflow_dispatch
│
▼
GitHub Environment gate ("codebuild")
- requires non-self-approval
│
▼
GitHub-hosted runner (ubuntu-latest)
│
├─► configure-aws-credentials (OIDC)
│ role-to-assume: AWS_CODEBUILD_ROLE_ARN
│
└─► aws-codebuild-run-build (StartBuild API)
project: CODEBUILD_PROJECT_NAME
buildspec: inline (mise install → mise run build)
logs: streamed via CloudWatch
Complete these steps in order.
If your account already has the GitHub Actions OIDC provider (token.actions.githubusercontent.com), skip to step 2.
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1The thumbprint is a formality — AWS validates via the OIDC discovery document. GitHub rotates certificates independently.
CodeBuild needs a GitHub Personal Access Token (PAT) to clone the repository source. A fine-grained token is recommended over a classic token because it supports least-privilege permissions and can be scoped to a single repository.
PAT creation is not scriptable. GitHub does not expose a REST API or CLI endpoint to create tokens. You must use the GitHub web UI.
Create the fine-grained token:
- Go to github.com > Settings > Developer settings > Personal access tokens > Fine-grained tokens
- Click Generate new token
- Configure the token:
- Token name:
codebuild-agent-plugins(or similar) - Expiration: choose an appropriate lifetime (default 30 days, max 1 year)
- Resource owner: select the organization that owns the repository (e.g.,
awslabs) - Repository access: select Only select repositories and choose
awslabs/agent-plugins - Permissions > Repository permissions:
- Contents: Read-only
- All other permissions can remain at No access
- Token name:
- Click Generate token and copy the value immediately — it will not be shown again
A classic token would require the
reposcope, which grants full read/write access to all private repositories — far broader than what CodeBuild needs for this workflow.
Import the token into CodeBuild:
aws codebuild import-source-credentials \
--server-type GITHUB \
--auth-type PERSONAL_ACCESS_TOKEN \
--token <GITHUB_PAT>Verify the import:
aws codebuild list-source-credentialsYou should see an entry with serverType: GITHUB and authType: PERSONAL_ACCESS_TOKEN.
Prerequisite: complete step 2 to import GitHub credentials before creating the project.
Deploy the CloudFormation stack below. It creates:
- A CodeBuild project with GitHub source and no buildspec (the workflow passes an inline override)
- A service role with least-privilege permissions for CloudWatch Logs and S3 artifacts
- An S3 bucket for build artifacts (SARIF reports from security scanners)
- A CloudWatch log group with 90-day retention (matches GitHub Actions log retention)
CloudFormation template:
AWSTemplateFormatVersion: '2010-09-09'
Description: >-
CodeBuild project for the Build (CodeBuild) GitHub Actions workflow.
The workflow passes an inline buildspec override, so no buildspec file
is configured on the project.
Parameters:
ProjectName:
Type: String
Default: agent-plugins-build
Description: >-
CodeBuild project name. Must match the CODEBUILD_PROJECT_NAME
GitHub Actions variable.
GitHubRepoUrl:
Type: String
Default: https://github.com/awslabs/agent-plugins
Description: HTTPS URL of the GitHub repository.
ComputeType:
Type: String
Default: BUILD_GENERAL1_LARGE
AllowedValues:
- BUILD_GENERAL1_SMALL
- BUILD_GENERAL1_MEDIUM
- BUILD_GENERAL1_LARGE
Description: Compute resources for the build environment.
Image:
Type: String
Default: aws/codebuild/amazonlinux-x86_64-standard:5.0
Description: Docker image for the build environment.
LogRetentionDays:
Type: Number
Default: 90
AllowedValues: [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365]
Description: Number of days to retain CloudWatch build logs.
Resources:
ArtifactBucket:
# checkov:skip=CKV_AWS_18:Access logging disproportionate for temporary CI artifacts with lifecycle expiration
# checkov:skip=CKV_AWS_21:Versioning unnecessary for ephemeral build artifacts with lifecycle expiration
Type: AWS::S3::Bucket
Metadata:
cfn_nag:
rules_to_suppress:
- id: W35
reason: >-
Access logging is unnecessary for a CI artifact bucket with
lifecycle expiration. It would require a separate logging
bucket, disproportionate for temporary SARIF reports.
Properties:
BucketName: !Sub '${ProjectName}-artifacts-${AWS::AccountId}'
LifecycleConfiguration:
Rules:
- Id: ExpireArtifacts
Status: Enabled
ExpirationInDays: !Ref LogRetentionDays
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: aws:kms
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
ArtifactBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref ArtifactBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: DenyNonSSLAccess
Effect: Deny
Principal: '*'
Action: 's3:*'
Resource:
- !GetAtt ArtifactBucket.Arn
- !Sub '${ArtifactBucket.Arn}/*'
Condition:
Bool:
aws:SecureTransport: 'false'
LogGroup:
# checkov:skip=CKV_AWS_158:CloudWatch Logs encrypted by AWS-managed keys by default; CMK adds unnecessary cost for CI logs
Type: AWS::Logs::LogGroup
Metadata:
cfn_nag:
rules_to_suppress:
- id: W84
reason: >-
CloudWatch Logs are encrypted at rest with AWS-managed keys
by default. A customer-managed KMS key adds cost and
operational overhead with no benefit for CI build logs.
Properties:
LogGroupName: !Sub '/aws/codebuild/${ProjectName}'
RetentionInDays: !Ref LogRetentionDays
ServiceRole:
Type: AWS::IAM::Role
Metadata:
cfn_nag:
rules_to_suppress:
- id: W11
reason: >-
The KMS Resource: '*' is scoped by kms:ViaService condition
to S3 only. The AWS-managed KMS key ARN cannot be predicted
at deploy time, so a wildcard is the standard pattern.
- id: W28
reason: >-
Explicit naming (${ProjectName}-service-role) is intentional
for discoverability and cross-stack references. The role is
not expected to require replacement.
Properties:
RoleName: !Sub '${ProjectName}-service-role'
Description: !Sub 'Service role for CodeBuild project ${ProjectName}.'
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: CloudWatchLogs
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AllowLogs
Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource: !Sub >-
arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${ProjectName}:*
- PolicyName: S3Artifacts
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AllowArtifactUpload
Effect: Allow
Action:
- s3:PutObject
- s3:GetBucketAcl
- s3:GetBucketLocation
Resource:
- !GetAtt ArtifactBucket.Arn
- !Sub '${ArtifactBucket.Arn}/*'
- Sid: AllowKMSForArtifacts
Effect: Allow
Action:
- kms:Decrypt
- kms:GenerateDataKey
Resource: '*'
Condition:
StringEquals:
kms:ViaService: !Sub 's3.${AWS::Region}.amazonaws.com'
Project:
Type: AWS::CodeBuild::Project
Metadata:
cfn_nag:
rules_to_suppress:
- id: W32
reason: >-
Artifacts use EncryptionDisabled: false, which encrypts with
the AWS-managed key. A customer-managed KMS key adds cost
and complexity with no benefit for CI build artifacts.
Properties:
Name: !Ref ProjectName
Description: >-
Runs the agent-plugins build. Invoked by the Build (CodeBuild)
GitHub Actions workflow via aws-codebuild-run-build.
BadgeEnabled: true
ServiceRole: !GetAtt ServiceRole.Arn
Source:
Type: GITHUB
Location: !Ref GitHubRepoUrl
BuildSpec: '{}'
Artifacts:
Type: S3
Location: !Ref ArtifactBucket
NamespaceType: BUILD_ID
Packaging: ZIP
EncryptionDisabled: false
Environment:
Type: LINUX_CONTAINER
ComputeType: !Ref ComputeType
Image: !Ref Image
PrivilegedMode: false # Enable only if the build needs to run Docker commands
LogsConfig:
CloudWatchLogs:
Status: ENABLED
GroupName: !Ref LogGroup
TimeoutInMinutes: 60
QueuedTimeoutInMinutes: 30
Outputs:
ProjectName:
Description: >-
Store this as the CODEBUILD_PROJECT_NAME variable in GitHub Actions.
Value: !Ref Project
ProjectArn:
Description: ARN of the CodeBuild project.
Value: !GetAtt Project.Arn
ArtifactBucketName:
Description: >-
S3 bucket for build artifacts (SARIF reports). Pass this as the
ArtifactBucketName parameter when deploying the IAM role stack in step 4.
Value: !Ref ArtifactBucket
ServiceRoleArn:
Description: ARN of the CodeBuild service role.
Value: !GetAtt ServiceRole.ArnDeploy with defaults:
aws cloudformation deploy \
--template-file administrators-guide-codebuild-project.yaml \
--stack-name agent-plugins-codebuild \
--capabilities CAPABILITY_NAMED_IAMDeploy with custom parameters:
aws cloudformation deploy \
--template-file administrators-guide-codebuild-project.yaml \
--stack-name agent-plugins-codebuild \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
ProjectName=my-codebuild-project \
GitHubRepoUrl=https://github.com/my-org/my-repo \
ComputeType=BUILD_GENERAL1_MEDIUM \
Image=aws/codebuild/amazonlinux-x86_64-standard:5.0 \
LogRetentionDays=30After deployment, retrieve the ProjectName and ArtifactBucketName outputs — you will need them in steps 4 and 6:
aws cloudformation describe-stacks \
--stack-name agent-plugins-codebuild \
--query "Stacks[0].Outputs[?OutputKey=='ProjectName' || OutputKey=='ArtifactBucketName'].[OutputKey,OutputValue]" \
--output tableDeploy the CloudFormation stack below. It creates a single IAM role with:
- An OIDC trust policy scoped to
repo:awslabs/agent-plugins:environment:codebuild - Least-privilege permissions:
codebuild:StartBuild,codebuild:BatchGetBuilds,logs:GetLogEvents, ands3:GetObjecton the artifact bucket from step 3
CloudFormation template:
AWSTemplateFormatVersion: '2010-09-09'
Description: >-
IAM role for the Build (CodeBuild) GitHub Actions workflow.
Assumes OIDC federation from the "codebuild" GitHub environment.
Parameters:
GitHubOrg:
Type: String
Default: awslabs
Description: GitHub organization that owns the repository.
GitHubRepo:
Type: String
Default: agent-plugins
Description: GitHub repository name.
GitHubEnvironment:
Type: String
Default: codebuild
Description: >-
GitHub environment name. The OIDC subject claim is scoped to this
environment so only approved jobs can assume the role.
CodeBuildProjectName:
Type: String
Default: agent-plugins-build
Description: Name of the CodeBuild project (from step 3 ProjectName output).
ArtifactBucketName:
Type: String
Default: ''
Description: >-
Name of the S3 bucket for build artifacts (from step 3
ArtifactBucketName output). If provided, the role gets s3:GetObject
to download SARIF reports.
CreateOIDCProvider:
Type: String
Default: 'true'
AllowedValues: ['true', 'false']
Description: >-
Set to false if the GitHub Actions OIDC provider already exists in
this account. When false, provide OIDCProviderArn.
OIDCProviderArn:
Type: String
Default: ''
Description: >-
ARN of an existing GitHub Actions OIDC provider. Required when
CreateOIDCProvider is false.
Conditions:
ShouldCreateOIDCProvider: !Equals [!Ref CreateOIDCProvider, 'true']
HasExistingOIDCProvider: !Not [!Equals [!Ref OIDCProviderArn, '']]
HasArtifactBucket: !Not [!Equals [!Ref ArtifactBucketName, '']]
Resources:
GitHubOIDCProvider:
Type: AWS::IAM::OIDCProvider
Condition: ShouldCreateOIDCProvider
Properties:
Url: https://token.actions.githubusercontent.com
ClientIdList:
- sts.amazonaws.com
ThumbprintList:
- 6938fd4d98bab03faadb97b34396831e3780aea1
GitHubActionsCodeBuildRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub '${GitHubRepo}-codebuild-gha'
Description: !Sub >-
Assumed by GitHub Actions (OIDC) to run CodeBuild builds for
${GitHubOrg}/${GitHubRepo} from the ${GitHubEnvironment} environment.
MaxSessionDuration: 3600
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Federated: !If
- ShouldCreateOIDCProvider
- !Ref GitHubOIDCProvider
- !If
- HasExistingOIDCProvider
- !Ref OIDCProviderArn
- !Sub >-
arn:${AWS::Partition}:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com
Action: sts:AssumeRoleWithWebIdentity
Condition:
StringEquals:
token.actions.githubusercontent.com:aud: sts.amazonaws.com
token.actions.githubusercontent.com:sub: !Sub >-
repo:${GitHubOrg}/${GitHubRepo}:environment:${GitHubEnvironment}
Policies:
- PolicyName: CodeBuildAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AllowStartAndMonitorBuild
Effect: Allow
Action:
- codebuild:StartBuild
- codebuild:BatchGetBuilds
Resource: !Sub >-
arn:${AWS::Partition}:codebuild:${AWS::Region}:${AWS::AccountId}:project/${CodeBuildProjectName}
- Sid: AllowReadBuildLogs
Effect: Allow
Action:
- logs:GetLogEvents
Resource: !Sub >-
arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${CodeBuildProjectName}:*
- !If
- HasArtifactBucket
- Sid: AllowDownloadArtifacts
Effect: Allow
Action:
- s3:GetObject
- s3:ListBucket
Resource:
- !Sub 'arn:${AWS::Partition}:s3:::${ArtifactBucketName}'
- !Sub 'arn:${AWS::Partition}:s3:::${ArtifactBucketName}/*'
- !Ref AWS::NoValue
- !If
- HasArtifactBucket
- Sid: AllowDecryptArtifacts
Effect: Allow
Action:
- kms:Decrypt
- kms:GenerateDataKey
Resource: '*'
Condition:
StringEquals:
kms:ViaService: !Sub 's3.${AWS::Region}.amazonaws.com'
- !Ref AWS::NoValue
Outputs:
RoleArn:
Description: >-
Store this as the AWS_CODEBUILD_ROLE_ARN secret in the GitHub
"codebuild" environment.
Value: !GetAtt GitHubActionsCodeBuildRole.Arn
OIDCProviderArn:
Condition: ShouldCreateOIDCProvider
Description: ARN of the created OIDC provider.
Value: !Ref GitHubOIDCProviderDeploy with the artifact bucket from step 3:
aws cloudformation deploy \
--template-file administrators-guide-codebuild-role.yaml \
--stack-name agent-plugins-gha-codebuild \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
ArtifactBucketName=agent-plugins-build-artifacts-123456789012Deploy with an existing OIDC provider:
aws cloudformation deploy \
--template-file administrators-guide-codebuild-role.yaml \
--stack-name agent-plugins-gha-codebuild \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
CreateOIDCProvider=false \
OIDCProviderArn=arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com \
ArtifactBucketName=agent-plugins-build-artifacts-123456789012After deployment, note the RoleArn output — you will need it in step 6.
Using the GitHub CLI:
# Look up the agent-plugins-admins team ID
TEAM_ID=$(gh api orgs/awslabs/teams/agent-plugins-admins --jq '.id')
# Create the environment with the admins team as required reviewers
gh api -X PUT repos/awslabs/agent-plugins/environments/codebuild \
--input - <<EOF
{
"prevent_self_review": true,
"reviewers": [
{"type": "Team", "id": ${TEAM_ID}}
]
}
EOFUsing the GitHub UI:
- Go to Settings > Environments > New environment
- Name:
codebuild - Configure protection rules:
- Required reviewers: add the
@awslabs/agent-plugins-adminsteam - Prevent self-review: enable this checkbox
- Required reviewers: add the
The non-self-approval requirement ensures that the person who triggers the workflow cannot approve their own run.
Using the GitHub CLI:
# Environment secret (scoped to the codebuild environment)
gh secret set AWS_CODEBUILD_ROLE_ARN --env codebuild \
--body "<RoleArn output from step 4>"
# Repo-level variables (not sensitive)
gh variable set AWS_REGION --body "us-east-1"
gh variable set CODEBUILD_PROJECT_NAME --body "<ProjectName output from step 3>"Using the GitHub UI:
In Settings > Environments > codebuild:
| Type | Name | Value |
|---|---|---|
| Secret | AWS_CODEBUILD_ROLE_ARN |
The RoleArn output from the CloudFormation stack (step 4) |
In Settings > Secrets and variables > Actions > Variables:
| Type | Name | Value |
|---|---|---|
| Variable | AWS_REGION |
AWS region where the CodeBuild project lives (e.g., us-east-1) |
| Variable | CODEBUILD_PROJECT_NAME |
The ProjectName output from the CloudFormation stack (step 3) |
AWS_CODEBUILD_ROLE_ARN is scoped to the codebuild environment so it is only available to jobs that have passed the approval gate. AWS_REGION and CODEBUILD_PROJECT_NAME are not sensitive and are stored as repo-level variables.
- Go to Actions > Build (CodeBuild) > Run workflow
- Select the branch to build
- A reviewer (not yourself) must approve the environment deployment
- The build runs on CodeBuild and streams logs back to the Actions console
| Symptom | Cause | Fix |
|---|---|---|
| "Not authorized to perform sts:AssumeRoleWithWebIdentity" | OIDC subject claim mismatch | Verify the IAM trust policy sub condition matches repo:awslabs/agent-plugins:environment:codebuild exactly |
| "Could not find project" | Wrong project name or region | Check CODEBUILD_PROJECT_NAME and AWS_REGION variables |
| Build hangs waiting for approval | No reviewer configured | Add a required reviewer in the codebuild environment settings |
| "Access denied" on StartBuild | IAM policy too restrictive | Ensure the role has codebuild:StartBuild and codebuild:BatchGetBuilds on the correct project ARN |