A Forgejo action for OpenTofu module testing on AWS
2025-09-30 - A corner stone of my CI
Tags: AWS CI Forgejo OpenTofu terraform
Introduction
I have been using a Forgejo action (compatible with GitHub actions) for testing my OpenTofu/Terraform modules on AWS. I planned to blog this earlier, but since it worked without quirks I completely forgot to publish it.
Usage example
The action relies on having an AWS IAM access key provisioned in your CI’s
secrets. An astute reader will notice I am naming the secret
AWS_ACCESS_KEY_SECRET
instead of the standard AWS_SECRET_ACCESS_KEY
, mind
this if trying to reproduce in your own environment:
- uses: "https://git.adyxax.org/adyxax/action-tofu-aws-test@2.0.0"
with:
aws-access-key-id: "${{ vars.AWS_ACCESS_KEY_ID }}"
aws-access-key-secret: "${{ secrets.AWS_ACCESS_KEY_SECRET }}"
Action steps
This action initializes the AWS credentials, then runs formatting and linting checks as well as tofu tests on the repository of an OpenTofu/Terraform module.
Boilerplate
Forgejo actions need some light boilerplate to define how they are to be used:
name: "tofu-aws-test"
description: "Test a tofu module on AWS."
inputs:
aws-access-key-id:
description: "AWS access key id."
required: true
aws-access-key-secret:
description: "AWS access key secret."
required: true
runs:
using: "composite"
steps:
Formatting and linting
These are two simple steps. The only caveat is that I need to unset the
GITHUB_TOKEN
environment variable before running tflint
. This is because
Forgejo strives for compatibility with GitHub actions, but sadly tflint
will
try to use said token for cloning its plugins. Since the token is generated on
my Forgejo instance, it is only valid there and not on GitHub. This would cause
the step to fail.
- name: "fmt"
shell: "bash"
run: |
tofu fmt -check -recursive
- name: "lint"
shell: "bash"
run: |
unset GITHUB_TOKEN
tflint --init
tflint --recursive
AWS credentials initialization
If you followed my past articles you will know that I am using multiple AWS
accounts to separate everything. I strive to have all my test resources created
in a tests
AWS account, but some things like DNS records will occasionally
require access to my core
AWS account where DNS zones are provisioned.
I am using AWS policies to tightly control what tests can provision in this
core
account, but I will not detail it in this article.
- name: "configure AWS profiles"
shell: "bash"
run: |
ROLE_NAME="repository_${GITHUB_REPOSITORY/\//_}"
cat >aws_config <<EOF
[profile core]
role_arn = arn:aws:iam::123456789012:role/${ROLE_NAME}
source_profile = root
[profile root]
aws_access_key_id = ${{ inputs.aws-access-key-id }}
aws_secret_access_key = ${{ inputs.aws-access-key-secret }}
region = eu-west-3
[profile tests]
role_arn = arn:aws:iam::345678901234:role/${ROLE_NAME}
source_profile = root
EOF
One should of course use their own AWS account IDs here!
OpenTofu/Terraform lock files
It is important to make sure your lock files match the provider versions pinned in your configuration. This step does exactly this!
This step has some code to recurse in subfolders because I love to use a pattern where some OpenTofu code specific to a repository lives alongside a module or some other code:
- name: "check tofu providers lock files"
shell: "bash"
run: |
unset GITHUB_TOKEN
export AWS_CONFIG_FILE="$(pwd)/aws_config"
shopt -s globstar nullglob
for lockfile in **/.terraform.lock.hcl; do
(cd "$(dirname "$lockfile")"; tofu init; tofu providers lock -platform=linux_amd64)
done
git diff --exit-code
OpenTofu/Terraform tests
I am running module tests with:
- name: "tofu test"
shell: "bash"
run: |
export AWS_CONFIG_FILE="$(pwd)/aws_config"
tofu init
tofu test
Cleanup
We clean up to be sure that the next steps in the workflow are not affected:
- name: "clean"
shell: "bash"
run: |
rm aws_config
Conclusion
Here is the link to the repository. Writing composite actions like this one is a good exercise and can be used to keep your workflows tidy and DRY!