Skip to content

Commit fd68d53

Browse files
authored
update example and github actions (#18)
1 parent f978044 commit fd68d53

7 files changed

Lines changed: 197 additions & 7 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ on:
77
- "tests/**"
88
- "schemas/**"
99
- ".github/workflows/ci.yml"
10+
- "!examples/**"
1011
push:
1112
branches: [main]
1213
paths:
1314
- "**.tf"
1415
- "tests/**"
1516
- "schemas/**"
17+
- "!examples/**"
1618

1719
jobs:
1820
validate:

.github/workflows/test.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,21 @@ on:
77
push:
88
branches:
99
- main
10+
paths:
11+
- "**.tf"
12+
- "modules/**"
13+
- "tests/**"
14+
- "schemas/**"
15+
- ".github/workflows/test.yml"
16+
- "!examples/**"
1017
pull_request:
18+
paths:
19+
- "**.tf"
20+
- "modules/**"
21+
- "tests/**"
22+
- "schemas/**"
23+
- ".github/workflows/test.yml"
24+
- "!examples/**"
1125

1226
permissions:
1327
contents: read
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
name: Deploy Terraform Changes
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: read
11+
actions: write # require to upload artifacts
12+
13+
concurrency:
14+
group: terraform-deploy
15+
cancel-in-progress: false
16+
17+
jobs:
18+
changes:
19+
runs-on: ubuntu-latest
20+
# Required permissions
21+
permissions:
22+
pull-requests: read
23+
outputs:
24+
# Expose matched filters as job 'packages' output variable
25+
modules: ${{ steps.filter.outputs.changes }}
26+
steps:
27+
# For pull requests it's not necessary to checkout the code
28+
- uses: dorny/paths-filter@v2
29+
id: filter
30+
with:
31+
filters: |
32+
account:
33+
- 'dbt_cloud/account/**'
34+
initial_project:
35+
- 'dbt_cloud/projects/initial/**'
36+
terraform:
37+
runs-on: ubuntu-latest
38+
strategy:
39+
matrix:
40+
# TODO: set dynamically based on ${{ fromJSON(needs.changes.outputs.projects) }}
41+
# but I'm not sure how to dynamically set the other variables in a way that feels this clean
42+
include:
43+
- module: account
44+
- module: initial_project
45+
46+
steps:
47+
- name: Check out the repository
48+
uses: actions/checkout@v4
49+
if: ${{ contains(fromJSON(needs.changes.outputs.modules), matrix.module) }}
50+
51+
- name: Download Encrypted Artifact & Decrypt
52+
uses: badgerhobbs/terraform-state@v2
53+
if: ${{ contains(fromJSON(needs.changes.outputs.modules), matrix.module) }}
54+
with:
55+
encryption_key: ${{ secrets.AES_256_ENCRYPTION_KEY }}
56+
operation: download
57+
location: artifact
58+
github_token: ${{ secrets.GITHUB_TOKEN }}
59+
continue-on-error: true
60+
61+
- name: Set up Terraform
62+
uses: hashicorp/setup-terraform@v3
63+
if: ${{ contains(fromJSON(needs.changes.outputs.modules), matrix.module) }}
64+
with:
65+
terraform_version: 1.5.0 # Specify your Terraform version
66+
67+
- name: Terraform Init
68+
if: ${{ contains(fromJSON(needs.changes.outputs.modules), matrix.module) }}
69+
run: terraform init
70+
71+
- name: Terraform Plan
72+
id: plan
73+
if: ${{ contains(fromJSON(needs.changes.outputs.modules), matrix.module) }}
74+
run: terraform plan -out=tfplan_${{ matrix.module }} -target=module.${{ matrix.module }}
75+
76+
- name: Apply Terraform Changes
77+
# if: github.ref == 'refs/heads/main' && contains(fromJSON(needs.changes.outputs.modules), matrix.module)
78+
if: false
79+
run: terraform apply -auto-approve tfplan_${{ matrix.module }}
80+
env:
81+
TF_VAR_dbt_account_id: ${{ secrets.TF_VAR_DBT_ACCOUNT_ID }}
82+
TF_VAR_dbt_token: ${{ secrets.TF_VAR_DBT_TOKEN }}
83+
TF_VAR_dbt_host_url: ${{ secrets.TF_VAR_DBT_HOST_URL }}
84+
85+
- name: Encrypt Artifact & Upload Encrypted Artifact
86+
uses: badgerhobbs/terraform-state@v2
87+
if: ${{ contains(fromJSON(needs.changes.outputs.modules), matrix.module) }}
88+
with:
89+
encryption_key: ${{ secrets.AES_256_ENCRYPTION_KEY }}
90+
operation: upload
91+
location: artifact
92+
github_token: ${{ secrets.GITHUB_TOKEN }}

examples/basic/.github/workflows/cd.yml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# Settings > Environments > production > Required reviewers
66
#
77
# Uses the same secrets as ci.yml — see that file for the full list
8-
# and setup instructions.
8+
# and setup instructions, including AES_256_ENCRYPTION_KEY for state storage.
99

1010
name: CD — Terraform Apply
1111

@@ -18,6 +18,7 @@ on:
1818

1919
permissions:
2020
contents: read
21+
actions: write # required to upload state artifact
2122

2223
jobs:
2324
apply:
@@ -39,6 +40,15 @@ jobs:
3940
- name: Checkout
4041
uses: actions/checkout@v4
4142

43+
- name: Download Terraform State
44+
uses: badgerhobbs/terraform-state@v2
45+
with:
46+
encryption_key: ${{ secrets.AES_256_ENCRYPTION_KEY }}
47+
operation: download
48+
location: artifact
49+
github_token: ${{ secrets.GITHUB_TOKEN }}
50+
continue-on-error: true # OK to fail on first run — no artifact exists yet
51+
4252
- name: Setup Terraform
4353
uses: hashicorp/setup-terraform@v3
4454
with:
@@ -52,3 +62,12 @@ jobs:
5262

5363
- name: Terraform Apply
5464
run: terraform apply -auto-approve tfplan
65+
66+
- name: Upload Terraform State
67+
uses: badgerhobbs/terraform-state@v2
68+
if: always() # upload even if apply partially succeeded, to preserve any changes
69+
with:
70+
encryption_key: ${{ secrets.AES_256_ENCRYPTION_KEY }}
71+
operation: upload
72+
location: artifact
73+
github_token: ${{ secrets.GITHUB_TOKEN }}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Validate Terraform Changes
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
9+
jobs:
10+
terraform-validate:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Check out the repository
15+
uses: actions/checkout@v4
16+
17+
- name: Download Encrypted Artifact & Decrypt
18+
uses: badgerhobbs/terraform-state@v2
19+
with:
20+
encryption_key: ${{ secrets.AES_256_ENCRYPTION_KEY }}
21+
operation: download
22+
location: artifact
23+
github_token: ${{ secrets.GITHUB_TOKEN }}
24+
continue-on-error: true
25+
26+
- name: Set up Terraform
27+
uses: hashicorp/setup-terraform@v3
28+
with:
29+
terraform_version: 1.5.0 # Specify your Terraform version
30+
31+
- name: Check Terraform Format
32+
run: terraform fmt -check -recursive
33+
34+
- name: Terraform Init
35+
run: terraform init
36+
37+
- name: Validate Terraform
38+
run: terraform validate
39+
40+
- name: Terraform Plan
41+
run: terraform plan
42+
env:
43+
TF_VAR_dbt_account_id: ${{ secrets.TF_VAR_DBT_ACCOUNT_ID }}
44+
TF_VAR_dbt_token: ${{ secrets.TF_VAR_DBT_TOKEN }}
45+
TF_VAR_dbt_host_url: ${{ secrets.TF_VAR_DBT_HOST_URL }}

examples/basic/.github/workflows/ci.yml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@
88
# DBT_PAT — personal access token (GitHub App integration only;
99
# can be the same value as DBT_TOKEN otherwise)
1010
# ENVIRONMENT_CREDENTIALS — JSON blob, see .env.example for shape
11+
# AES_256_ENCRYPTION_KEY — random 32-char string used to encrypt the state artifact
12+
# generate with: openssl rand -hex 16
1113
#
1214
# Optional secrets (omit if not used):
1315
# CONNECTION_CREDENTIALS — JSON blob for OAuth/service principal connections
1416
# LINEAGE_TOKENS — JSON blob for Tableau/Looker integrations
1517
# OAUTH_CLIENT_SECRETS — JSON blob for OAuth configurations
1618
#
17-
# Remote state: configure a backend in main.tf (S3, GCS, Terraform Cloud, etc.)
18-
# before using these workflows in production. Without it, state is local and
19-
# lost between runs.
19+
# State storage: these workflows store Terraform state as an encrypted GitHub Actions
20+
# artifact — no backend configuration required to get started. For team use or
21+
# production, migrate to a real backend (S3, GCS, Terraform Cloud, etc.) in main.tf
22+
# and remove the badgerhobbs/terraform-state steps.
2023

2124
name: CI — Terraform Plan
2225

@@ -50,11 +53,23 @@ jobs:
5053
- name: Checkout
5154
uses: actions/checkout@v4
5255

56+
- name: Download Terraform State
57+
uses: badgerhobbs/terraform-state@v2
58+
with:
59+
encryption_key: ${{ secrets.AES_256_ENCRYPTION_KEY }}
60+
operation: download
61+
location: artifact
62+
github_token: ${{ secrets.GITHUB_TOKEN }}
63+
continue-on-error: true # OK to fail on first run — no artifact exists yet
64+
5365
- name: Setup Terraform
5466
uses: hashicorp/setup-terraform@v3
5567
with:
5668
terraform_version: "~1"
5769

70+
- name: Terraform Format Check
71+
run: terraform fmt -check -recursive
72+
5873
- name: Terraform Init
5974
run: terraform init
6075

examples/basic/README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,23 @@ terraform apply
5757

5858
The `.github/workflows/` directory has ready-to-use GitHub Actions workflows:
5959

60-
- **`ci.yml`** — runs `terraform plan` on every PR and posts the plan as a comment
61-
- **`cd.yml`** — runs `terraform apply` when changes merge to main
60+
- **`ci.yml`** — formats check, validates, plans on every PR, and posts the plan as a comment
61+
- **`cd.yml`** — applies on merge to main, with an optional approval gate via GitHub Environments
62+
63+
Terraform state is stored as an encrypted artifact in GitHub Actions — no remote backend required to get started.
6264

6365
Set these GitHub repository secrets (Settings > Secrets and variables > Actions):
6466

6567
```
6668
DBT_ACCOUNT_ID numeric account ID
6769
DBT_TOKEN dbt Cloud service token
6870
ENVIRONMENT_CREDENTIALS JSON, e.g. {"analytics_prod":{"credential_type":"databricks","token":"dapi...","catalog":"main","schema":"analytics"}}
71+
AES_256_ENCRYPTION_KEY random key used to encrypt the state artifact — generate with: openssl rand -hex 16
6972
```
7073

7174
Optional secrets (omit if not used): `DBT_PAT`, `CONNECTION_CREDENTIALS`, `LINEAGE_TOKENS`, `OAUTH_CLIENT_SECRETS`.
7275

73-
Add a [Terraform backend](https://developer.hashicorp.com/terraform/language/backend) to `main.tf` before enabling CD so state is stored remotely.
76+
> **When to graduate off artifact state:** artifact state works well for a single user or small team. When you need concurrent runs, state locking, or a more durable audit trail, add a [Terraform backend](https://developer.hashicorp.com/terraform/language/backend) to `main.tf` and remove the `badgerhobbs/terraform-state` steps from both workflow files.
7477
7578
## Going further
7679

0 commit comments

Comments
 (0)