Finishing Up
On this page, you will:
- Remove import blocks
- Verify configuration matches reality
- Update CI/CD workflows for Snowflake deployment
- Create Snowflake README
- Format and push your code
Remove Import Blocks
Now that all resources are imported, delete the imports.tf file (if you haven't already).
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- Snowflake provider configurationvariables.tf- Input variable definitionsterraform.tfvars- Configuration values (no secrets!)outputs.tf- Output definitionsusers.tf- User resources (admin, service account)
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 Snowflake infrastructure alongside GitHub and AWS resources.
Update CI Workflow
Edit .github/workflows/terraform_ci.yml to add a Snowflake 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 }}
plan-snowflake:
name: plan-snowflake
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 Snowflake credentials from AWS Secrets Manager
uses: aws-actions/aws-secretsmanager-get-secrets@v2
with:
secret-ids: |
SNOWFLAKE_CREDS, terraform/snowflake-credentials
parse-json-secrets: true
- name: Set Snowflake environment variables
run: |
echo "TF_VAR_SNOWFLAKE_PRIVATE_KEY<<EOF" >> $GITHUB_ENV
echo "${{ env.SNOWFLAKE_CREDS_PRIVATE_KEY }}" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: Terraform Plan
uses: ./.github/actions/terraform_plan_comment
with:
prefix: terraform/snowflake
github_token: ${{ secrets.GITHUB_TOKEN }}
Snowflake Credentials from Secrets Manager
The Snowflake job retrieves credentials from AWS Secrets Manager and sets them as environment variables. The parse-json-secrets: true option automatically creates separate environment variables for each key in the JSON secret.
The secret structure:
{
"organization_name": "MYORG",
"account_name": "MYACCOUNT",
"user": "SVC_TERRAFORM",
"private_key": "-----BEGIN PRIVATE KEY-----\n..."
}
Becomes:
- SNOWFLAKE_CREDS_ORGANIZATION_NAME
- SNOWFLAKE_CREDS_ACCOUNT_NAME
- SNOWFLAKE_CREDS_USER
- SNOWFLAKE_CREDS_PRIVATE_KEY
Note: The organization and account names are also in terraform.tfvars, so the secret primarily provides the private key for authentication.
Update Apply Workflow
Edit .github/workflows/terraform_apply.yml to add a Snowflake 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
apply-snowflake:
name: apply-snowflake
runs-on: ubuntu-latest
concurrency:
group: terraform-apply-snowflake
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 Snowflake credentials from AWS Secrets Manager
uses: aws-actions/aws-secretsmanager-get-secrets@v2
with:
secret-ids: |
SNOWFLAKE_CREDS, terraform/snowflake-credentials
parse-json-secrets: true
- name: Set Snowflake environment variables
run: |
echo "TF_VAR_SNOWFLAKE_PRIVATE_KEY<<EOF" >> $GITHUB_ENV
echo "${{ env.SNOWFLAKE_CREDS_PRIVATE_KEY }}" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: Terraform Apply
uses: ./.github/actions/terraform_apply
with:
prefix: terraform/snowflake
Separate Concurrency Groups
Each Terraform configuration uses its own concurrency group (terraform-apply-github, terraform-apply-aws, terraform-apply-snowflake). This allows all three 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 Snowflake README
Document your Snowflake Terraform configuration.
Create terraform/snowflake/README.md:
# Snowflake Infrastructure
This directory manages Snowflake account infrastructure via Terraform.
## Resources Managed
### Users
- **Admin user**: Your personal admin account (with ACCOUNTADMIN/ORGADMIN roles)
- **SVC_TERRAFORM**: Service account for Terraform operations
## What We Don't Manage Here
- **Password/MFA**: Admin credentials managed manually, stored in 1Password
- **RSA public keys**: Set via SQL, keys stored in 1Password/Secrets Manager
- **Warehouses/Databases**: Added in the Data Warehouse section
- **Roles**: Added in the Data Warehouse section
- **Grants**: Added in the Data Warehouse section
## File Structure
## Authentication
### Local Development
Set the private key as an environment variable:
```sh
export TF_VAR_SNOWFLAKE_PRIVATE_KEY=$(cat ~/.snowflake/svc_terraform_key.p8)
Or use 1Password CLI:
export TF_VAR_SNOWFLAKE_PRIVATE_KEY=$(op read "op://Vault/Snowflake SVC_TERRAFORM/Private Key")
CI/CD
Credentials are stored in AWS Secrets Manager (terraform/snowflake-credentials) and retrieved by GitHub Actions during workflow execution.
Making Changes
Adding a New User
- Add the user resource to
users.tf - Run
terraform planto preview - Create a PR for review
- After approval, merge and CI/CD will apply
- Set the user's password/key-pair manually in Snowflake
Modifying an Existing User
- Update the user resource in
users.tf - Run
terraform planto preview - Create a PR for review
- After approval, merge and CI/CD will apply
Note: Sensitive fields (password, RSA keys) are ignored by Terraform and must be managed manually.
Future Additions
This configuration is the foundation. The Data Warehouse section will add:
- Warehouses and resource monitors
- Databases for each environment
- Functional roles (ANALYTICS_DEVELOPER, etc.)
- Service accounts for dbt, Airbyte, etc.
- Network policies
- SSO integrations
- Storage integrations for S3
## Commit Your Work ```sh git add . git commit -m "Complete Snowflake Terraform configuration - Remove import blocks after successful import - Update CI/CD workflows for Snowflake deployment - Add README documentation - Configure SVC_TERRAFORM for CI/CD access"
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 Snowflake Terraform configuration runs through CI/CD. Watch the workflow runs to verify everything works correctly.
Verify CI/CD Works
After merging, check the GitHub Actions workflow:
- Go to your repository's Actions tab
- Find the "Apply Infrastructure" workflow run
- Verify the
apply-snowflakejob completed successfully
If it fails, check:
- The Snowflake credentials secret exists and contains valid JSON
- The TerraformGitHubActionsRole has permission to read
terraform/*secrets - The private key format is correct (including newlines)
Understanding What You've Done
You've now brought your Snowflake infrastructure under Terraform management:
Created Resources
- SVC_TERRAFORM: Dedicated service account with key-pair authentication
- AWS Secret: Snowflake credentials stored securely in Secrets Manager
Imported Resources
- Admin user: Your existing admin account
Established Patterns
- Key-pair authentication for service accounts
- Credentials stored in AWS Secrets Manager
- Lifecycle blocks to prevent Terraform from managing sensitive fields
- Import workflow for bringing existing resources under management
CI/CD Integration
- Plan on PR for all three configurations (GitHub, AWS, Snowflake)
- Apply on merge with separate concurrency groups
- Snowflake credentials retrieved from Secrets Manager
Key Benefits
- Version controlled: Snowflake user management is now in code
- Auditable: Changes tracked in Git history with approval requirements
- Consistent: CI/CD ensures uniform application of changes
- Secure: Private keys never in Terraform state or code
- Foundation: Ready for Data Warehouse configuration
Current File Structure
terraform/
├── github/
│ ├── backend.tf
│ ├── main.tf
│ ├── providers.tf
│ ├── variables.tf
│ ├── terraform.tfvars
│ ├── outputs.tf
│ ├── organisation.tf
│ ├── teams.tf
│ ├── users.tf
│ └── README.md
├── aws/
│ ├── backend.tf
│ ├── main.tf
│ ├── providers.tf
│ ├── variables.tf
│ ├── terraform.tfvars
│ ├── iam_users.auto.tfvars
│ ├── outputs.tf
│ ├── iam_roles.tf
│ ├── iam_users.tf
│ ├── oidc.tf
│ ├── state.tf
│ ├── budgets.tf
│ ├── secrets.tf
│ └── README.md
└── snowflake/
├── backend.tf
├── main.tf
├── providers.tf
├── variables.tf
├── terraform.tfvars
├── users.auto.tfvars
├── outputs.tf
├── users.tf
└── README.md
What's Next
You've successfully configured Snowflake infrastructure with Terraform:
- Service account created with key-pair authentication
- Admin user imported
- CI/CD workflows updated
- Credentials stored securely in Secrets Manager
- Documentation complete
Terraform Setup Complete!
You now have a complete Terraform setup managing:
- GitHub: Organisation, teams, users, repository settings
- AWS: IAM roles, users, state infrastructure, budgets, secrets
- Snowflake: Admin user, service account
All changes go through pull requests with automated planning and applying.
Your next step is to build out your data warehouse in Snowflake. You'll create warehouses, databases, roles, and additional users using the patterns established here.
Continue to Build Your Data Warehouse →