GitHub Actions OIDC
GitHub Actions workflows authenticate with AWS using OpenID Connect (OIDC). Instead of storing long-lived AWS credentials as GitHub secrets, each workflow run receives a short-lived token that AWS exchanges for temporary credentials.
Before your pipeline can deploy to AWS, Team Cloud must provision an IAM role that trusts your specific GitHub repository.
How it works
Authentication follows a two-step chain:
- GitHub exchanges its OIDC token for temporary credentials in the Central-IAM-User AWS account.
- Those credentials assume a second IAM role in your application's AWS account. This role is scoped to your repository only—no other repository can assume it.
The BYM composite actions perform both steps automatically. If you write custom AWS steps, you add both steps yourself.
Step 1: Request access from Team Cloud
Contact Team Cloud on the #cloud-infrastructure Slack channel and provide:
- The full name of your GitHub repository, for example
BYM-IKT/KatteApp - The AWS account names that need access, for example
BYM-DP-Katteapp-TestandBYM-DP-Katteapp-Prod
Tip
Where to find the AWS account names? Open Terraform file, go to account-defaults.tf and look for
If it doesn't exist, log in to the AWS Access Portal to find the account name.Step 2: Add the OIDC permission to your workflow job
The BYM composite actions handle OIDC automatically when you provide the required inputs and add the permissions shown below to the job. To assume the role yourself instead of using a composite action, follow the "Custom workflow" example.
env:
AWS_ACCOUNT_ID: "<<AWS_ACCOUNT_ID>>"
ECR_REPOSITORY_NAME: kattehotell-api
DOCKER_CONTEXT_PATH: "."
DOCKERFILE_PATH: "Kattehotell.Api/Dockerfile"
jobs:
build_push_image_to_shared:
name: Build image and publish to ECR
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Build and push image to ECR
uses: BYM-IKT/github-actions/build-and-push-image-to-ecr@master
with:
aws-account-id: ${{ env.AWS_ACCOUNT_ID }}
ecr-name: ${{ env.ECR_REPOSITORY_NAME }}
docker-context-path: ${{ env.DOCKER_CONTEXT_PATH }}
dockerfile-path: ${{ env.DOCKERFILE_PATH }}
# ... build steps ...
env:
AWS_ACCOUNT_ID: "<<AWS_ACCOUNT_ID>>"
APP_URL: https://kattehotell.test.bymoslo.net
S3_BUCKET_NAME: test-kattehotell-cf
PROJECT_DIRECTORY: "."
BUILD_PATH: "build/client"
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# ... build steps ...
- name: Upload to S3 and invalidate CloudFront cache
uses: BYM-IKT/github-actions/upload-to-s3-and-invalidate-cloudfront@master
with:
aws-account-id: ${{ env.AWS_ACCOUNT_ID }}
s3-bucket-name: ${{ env.S3_BUCKET_NAME }}
build-directory: ${{ env.PROJECT_DIRECTORY }}/${{ env.BUILD_PATH }}
cloudfront-distribution-domain-name: ${{ env.APP_URL }}
env:
AWS_ACCOUNT_ID: "<<AWS_ACCOUNT_ID>>"
ECR_REPOSITORY_NAME: kattehotell-api
ECS_CLUSTER_NAME: test-kattehotell
ECS_SERVICE_NAME: kattehotell-api
jobs:
deploy_images_to_ecs_service_test:
name: "Deploy to TEST"
uses: BYM-IKT/github-actions/.github/workflows/deploy-image-to-ecs.yml@master
with:
environment: testing
aws-account-id-target: ${{ env.AWS_ACCOUNT_ID }}
ecr-name: ${{ env.ECR_REPOSITORY_NAME }}
image-tag-target: latest
image-tag-new: test
ecs-cluster-name: ${{ env.ECS_CLUSTER_NAME }}
ecs-service-name: ${{ env.ECS_SERVICE_NAME }}
# ... build steps ...
env:
AWS_ACCOUNT_ID: "<<AWS_ACCOUNT_ID>>"
ECR_REPOSITORY_NAME: kattehotell-worker
LAMBDA_FUNCTION_NAME: kattehotell-worker
jobs:
deploy_image_to_lambda_function_test:
name: "Deploy to TEST"
uses: BYM-IKT/github-actions/.github/workflows/deploy-image-to-lambda.yml@master
with:
environment: testing
aws-account-id-target: ${{ env.AWS_ACCOUNT_ID }}
ecr-name: ${{ env.ECR_REPOSITORY_NAME }}
image-tag-target: latest
image-tag-new: test
lambda-name: ${{ env.LAMBDA_FUNCTION_NAME }}
# ... build steps ...
env:
AWS_ACCOUNT_ID: "<<AWS_ACCOUNT_ID>>"
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Assume parent role in AWS
id: creds
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-region: eu-west-1
role-to-assume: arn:aws:iam::924598342799:role/github-oidc
role-session-name: Github-Action-${{ github.event.repository.name }}
output-credentials: true
- name: Assume execution role in AWS
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-access-key-id: ${{ steps.creds.outputs.aws-access-key-id }}
aws-secret-access-key: ${{ steps.creds.outputs.aws-secret-access-key }}
aws-session-token: ${{ steps.creds.outputs.aws-session-token }}
aws-region: eu-west-1
role-duration-seconds: 3500
role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/github-actions-${{ github.repository_id }}
role-session-name: Github-Action-${{ github.event.repository.name }}
# ... build steps ...
Tip
Not sure about the AWS account ID? Look for the default-account.tf file in your product's Terraform Infrastruktur-repo. The AWS account ID is the 12-digit number used for the account_id variable.
You can also log in to the AWS Access Portal to find the account ID.