Finishing Up
On this page, you will:
- Remove import blocks
- Verify configuration matches reality
- Update CI/CD workflows for AWS deployment
- Create AWS README
- Format and push your code
Remove Import Blocks
Now that all resources are imported, delete the imports.tf file.
Import blocks are only needed once. After successful import, the entire file can be deleted.
Delete imports.tf
rm imports.tf
Your configuration now consists of:
backend.tf- S3 backend configurationmain.tf- Terraform and provider version requirementsproviders.tf- AWS provider configurationvariables.tf- Input variable definitionsterraform.tfvars- Configuration valuesoutputs.tf- Output definitionsiam_roles.tf- IAM role definitionsiam_users.tf- IAM user definitionsoidc.tf- GitHub OIDC provider and TerraformGitHubActionsRolestate.tf- S3 bucket and DynamoDB table for Terraform statebudgets.tf- AWS budget configurationsecrets.tf- Secrets Manager configuration
Verify No Changes
terraform plan
Expected output:
No changes. Your infrastructure matches the configuration.
If Terraform shows planned changes, something is misconfigured. Review the diff and adjust your resource definitions to match reality.
Update CI/CD Workflows
Now you need to update the GitHub Actions workflows to plan and apply AWS infrastructure alongside GitHub resources.
Update CI Workflow
Edit .github/workflows/terraform_ci.yml to add an AWS plan job:
name: CI - fmt and plan
on:
workflow_dispatch:
pull_request:
jobs:
check-pre-commit:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v3
- name: Run Pre-Commit
uses: pre-commit/action@v3.0.1
plan-github:
name: plan-github
runs-on: ubuntu-latest
needs: check-pre-commit
permissions:
pull-requests: write
id-token: write
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: 'arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/TerraformGitHubActionsRole'
role-session-name: github-actions-terraform
aws-region: ${{ vars.AWS_REGION }}
- name: Get secrets from AWS Secrets Manager
uses: aws-actions/aws-secretsmanager-get-secrets@v2
with:
secret-ids: |
TF_VAR_GITHUB_TOKEN, terraform/github-token
parse-json-secrets: false
- name: Terraform Plan
uses: ./.github/actions/terraform_plan_comment
with:
prefix: terraform/github
github_token: ${{ secrets.GITHUB_TOKEN }}
plan-aws:
name: plan-aws
runs-on: ubuntu-latest
needs: check-pre-commit
permissions:
pull-requests: write
id-token: write
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: 'arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/TerraformGitHubActionsRole'
role-session-name: github-actions-terraform
aws-region: ${{ vars.AWS_REGION }}
- name: Terraform Plan
uses: ./.github/actions/terraform_plan_comment
with:
prefix: terraform/aws
github_token: ${{ secrets.GITHUB_TOKEN }}
No Secrets Manager for AWS Plan
The AWS plan job doesn't need to retrieve secrets from Secrets Manager because the AWS provider uses the OIDC credentials directly. Only the GitHub provider needs the PAT from Secrets Manager.
Update Apply Workflow
Edit .github/workflows/terraform_apply.yml to add an AWS apply job:
name: Apply Infrastructure
on:
workflow_dispatch:
push:
branches:
- main
jobs:
apply-github:
name: apply-github
runs-on: ubuntu-latest
concurrency:
group: terraform-apply-github
cancel-in-progress: false
permissions:
id-token: write
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: 'arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/TerraformGitHubActionsRole'
role-session-name: github-actions-terraform
aws-region: ${{ vars.AWS_REGION }}
- name: Get secrets from AWS Secrets Manager
uses: aws-actions/aws-secretsmanager-get-secrets@v2
with:
secret-ids: |
TF_VAR_GITHUB_TOKEN, terraform/github-token
parse-json-secrets: false
- name: Terraform Apply
uses: ./.github/actions/terraform_apply
with:
prefix: terraform/github
apply-aws:
name: apply-aws
runs-on: ubuntu-latest
concurrency:
group: terraform-apply-aws
cancel-in-progress: false
permissions:
id-token: write
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: 'arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/TerraformGitHubActionsRole'
role-session-name: github-actions-terraform
aws-region: ${{ vars.AWS_REGION }}
- name: Terraform Apply
uses: ./.github/actions/terraform_apply
with:
prefix: terraform/aws
Separate Concurrency Groups
Each Terraform configuration uses its own concurrency group (terraform-apply-github, terraform-apply-aws). This allows GitHub and AWS applies to run in parallel whilst preventing concurrent applies within the same configuration.
Format and Validate
Format your code:
terraform fmt -recursive
Validate syntax:
terraform validate
Expected output:
Success! The configuration is valid.
Create AWS README
Document your AWS Terraform configuration.
Create terraform/aws/README.md:
# AWS Infrastructure
This directory manages AWS account infrastructure via Terraform.
## Resources Managed
### IAM Roles
- **AdminRole**: Full administrative access for account administrators
- **DataEngineerRole**: Data platform development with state file restrictions
- **InfrastructureAdminRole**: Terraform infrastructure management
- **TerraformGitHubActionsRole**: CI/CD pipeline access via OIDC
### IAM Users
- Personal IAM user with AssumeRolesPolicy
- Pattern for team member management with `for_each`
### OIDC Provider
- GitHub Actions OIDC provider for secure, credential-less authentication
### State Infrastructure
- S3 bucket for Terraform state storage
- DynamoDB table for state locking
### Budgets
- Monthly cost budget with configurable alerts
- Pattern for service-specific budgets
### Secrets Manager
- Secret containers for Terraform provider credentials
- Pattern for managing secrets without exposing values in state
## What We Don't Manage Here
- **Secret values**: Set via CLI to keep out of Terraform state
- **Access keys**: Created by users, not in code
- **MFA devices**: User-specific, require interactive setup
- **Console passwords**: User-managed
## File Structure
## Configuration Approach
### Policy Documents
Policies use `aws_iam_policy_document` data sources:
- Validates at plan time, catching errors early
- References variables and resources directly
- No separate JSON files to manage
- Type-safe Terraform syntax
### Environment Variables
Shared values (like GitHub organisation name) use `TF_VAR_*` environment variables:
- Set in `.envrc` at repository root
- Shared between `terraform/github/` and `terraform/aws/`
- No duplication in multiple `tfvars` files
## Making Changes
### Adding a New IAM Role
1. Add a `data "aws_iam_policy_document"` for the trust policy (if needed)
2. Add a `data "aws_iam_policy_document"` for permissions
3. Add the `aws_iam_role` resource
4. Add the `aws_iam_policy` resource using the policy document
5. Add `aws_iam_role_policy_attachment` to link them
6. Run `terraform plan` to preview
7. Create a PR for review
8. After approval, merge and CI/CD will apply
### Adding a New Secret
1. Add the secret resource to `secrets.tf`
2. Run `terraform apply` (via CI/CD) to create the container
3. Set the secret value via CLI:
```sh
aws secretsmanager put-secret-value \
--secret-id "terraform/new-secret" \
--secret-string "value" \
--profile infrastructure-admin
```
4. Update IAM policies if using a new prefix
### Updating Budget Alerts
1. Modify `budgets.tf` or `terraform.tfvars`
2. Run `terraform plan` to preview
3. Create a PR for review
4. After approval, merge and CI/CD will apply
## AWS CLI Profiles
This configuration expects these AWS CLI profiles:
| Profile | Role | Use Case |
|---------|------|----------|
| `data-engineer` | DataEngineerRole | Day-to-day data work |
| `infrastructure-admin` | InfrastructureAdminRole | Local Terraform operations (initial import only) |
| `admin` | AdminRole | Account administration |
After initial setup, all Terraform operations should run through CI/CD, not locally.
Commit Your Work
git add .
git commit -m "Complete AWS Terraform configuration
- Remove import blocks after successful import
- Update CI/CD workflows for AWS deployment
- Add README documentation
- Configure TerraformGitHubActionsRole for AWS management"
Push to GitHub:
git push
Create the PR, get the relevant approvals, and merge to main.
First CI/CD Run
This will be the first time the AWS Terraform configuration runs through CI/CD. Watch the workflow runs to verify everything works correctly.
Understanding What You've Done
You've now brought your AWS infrastructure under Terraform management:
Imported Existing Resources
- IAM roles (AdminRole, DataEngineerRole, InfrastructureAdminRole)
- TerraformGitHubActionsRole and policy
- GitHub OIDC provider
- S3 state bucket and DynamoDB lock table
- IAM users and policies
- Budget alerts
- Secrets Manager secrets
Established Patterns
aws_iam_policy_documentfor type-safe, validated policies- Secrets management without exposing values in state
- Environment variables for shared configuration
- Import workflow for bringing existing resources under management
CI/CD Integration
- Plan on PR for both GitHub and AWS configurations
- Apply on merge with separate concurrency groups
- OIDC authentication eliminating long-lived credentials
Key Benefits
- Version controlled: All AWS infrastructure is now in code
- Auditable: Changes tracked in Git history with approval requirements
- Consistent: CI/CD ensures uniform application of changes
- Secure: Secrets values never in Terraform state
- Documented: Configuration serves as documentation
- Scalable: Patterns established for adding resources
What's Next
You've successfully configured AWS infrastructure with Terraform:
- IAM roles managed in code
- State infrastructure managed in code
- Budget alerts managed in code
- Secrets Manager configured
- CI/CD workflows updated
- Documentation complete
Your next step is to configure Snowflake with Terraform. You'll create a service account for Terraform, import your admin user, and establish patterns for managing Snowflake resources.
Continue to Snowflake Infrastructure with Terraform →
Building More AWS Infrastructure
This section focused on importing existing AWS resources and establishing the foundation. When you're ready to build additional AWS infrastructure for your data platform (S3 data lake buckets, application secrets), continue to the Build Your AWS Infrastructure section.