Skip to content

Commit 84c2d96

Browse files
committed
Initial Azure FinOps lab
0 parents  commit 84c2d96

62 files changed

Lines changed: 2933 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
name: Terraform Checks
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
workflow_dispatch:
11+
12+
permissions:
13+
contents: read
14+
pull-requests: write
15+
security-events: write
16+
17+
env:
18+
TF_VERSION: "1.12.0"
19+
TF_VAR_subscription_id: "00000000-0000-0000-0000-000000000000"
20+
21+
jobs:
22+
terraform:
23+
name: Terraform validation
24+
runs-on: ubuntu-latest
25+
steps:
26+
- name: Checkout
27+
uses: actions/checkout@v4
28+
29+
- name: Setup Terraform
30+
uses: hashicorp/setup-terraform@v3
31+
with:
32+
terraform_version: ${{ env.TF_VERSION }}
33+
34+
- name: Terraform fmt
35+
run: terraform fmt -check -recursive
36+
37+
- name: Terraform init
38+
run: terraform init -backend=false -reconfigure -input=false
39+
40+
- name: Terraform validate
41+
run: terraform validate
42+
43+
lint-security:
44+
name: Lint and security
45+
runs-on: ubuntu-latest
46+
steps:
47+
- name: Checkout
48+
uses: actions/checkout@v4
49+
50+
- name: Setup TFLint
51+
uses: terraform-linters/setup-tflint@v4
52+
53+
- name: TFLint init
54+
run: tflint --init
55+
56+
- name: TFLint
57+
run: tflint --recursive
58+
59+
- name: Checkov
60+
uses: bridgecrewio/checkov-action@v12
61+
with:
62+
directory: .
63+
framework: terraform
64+
soft_fail: true
65+
66+
- name: Gitleaks
67+
uses: gitleaks/gitleaks-action@v2
68+
env:
69+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
70+
71+
infracost:
72+
name: Infracost
73+
runs-on: ubuntu-latest
74+
if: github.event_name == 'pull_request'
75+
permissions:
76+
contents: read
77+
pull-requests: write
78+
env:
79+
INFRACOST_API_KEY: ${{ secrets.INFRACOST_API_KEY }}
80+
steps:
81+
- name: Skip when Infracost is not configured
82+
if: env.INFRACOST_API_KEY == ''
83+
run: echo "INFRACOST_API_KEY is not configured; skipping cost diff."
84+
85+
- name: Checkout base branch
86+
if: env.INFRACOST_API_KEY != ''
87+
uses: actions/checkout@v4
88+
with:
89+
ref: ${{ github.event.pull_request.base.ref }}
90+
91+
- name: Setup Infracost
92+
if: env.INFRACOST_API_KEY != ''
93+
uses: infracost/actions/setup@v3
94+
with:
95+
api-key: ${{ secrets.INFRACOST_API_KEY }}
96+
97+
- name: Generate base cost
98+
if: env.INFRACOST_API_KEY != ''
99+
run: |
100+
infracost breakdown --path . \
101+
--terraform-var-file environments/lab.tfvars \
102+
--format json \
103+
--out-file /tmp/infracost-base.json
104+
105+
- name: Checkout PR branch
106+
if: env.INFRACOST_API_KEY != ''
107+
uses: actions/checkout@v4
108+
109+
- name: Generate cost diff
110+
if: env.INFRACOST_API_KEY != ''
111+
run: |
112+
infracost diff --path . \
113+
--terraform-var-file environments/lab.tfvars \
114+
--compare-to /tmp/infracost-base.json \
115+
--format json \
116+
--out-file /tmp/infracost.json
117+
118+
- name: Comment cost diff
119+
if: env.INFRACOST_API_KEY != ''
120+
run: |
121+
infracost comment github --path /tmp/infracost.json \
122+
--repo ${{ github.repository }} \
123+
--github-token ${{ secrets.GITHUB_TOKEN }} \
124+
--pull-request ${{ github.event.pull_request.number }} \
125+
--behavior update
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
name: Terraform Lifecycle
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
environment:
7+
description: Environment profile
8+
type: choice
9+
required: true
10+
default: lab
11+
options:
12+
- dev
13+
- lab
14+
- full
15+
action:
16+
description: Terraform action
17+
type: choice
18+
required: true
19+
default: plan
20+
options:
21+
- plan
22+
- apply
23+
- destroy
24+
destroy_confirm:
25+
description: Type DESTROY when action is destroy
26+
required: false
27+
default: ""
28+
29+
permissions:
30+
contents: read
31+
id-token: write
32+
33+
env:
34+
TF_VERSION: "1.12.0"
35+
ARM_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }}
36+
ARM_TENANT_ID: ${{ vars.AZURE_TENANT_ID }}
37+
ARM_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }}
38+
TF_VAR_subscription_id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
39+
40+
jobs:
41+
lifecycle:
42+
name: ${{ inputs.action }} ${{ inputs.environment }}
43+
runs-on: ubuntu-latest
44+
environment: ${{ inputs.environment }}
45+
steps:
46+
- name: Validate destroy confirmation
47+
if: inputs.action == 'destroy' && inputs.destroy_confirm != 'DESTROY'
48+
run: |
49+
echo "Destroy requires destroy_confirm=DESTROY."
50+
exit 1
51+
52+
- name: Checkout
53+
uses: actions/checkout@v4
54+
55+
- name: Azure login
56+
uses: azure/login@v2
57+
with:
58+
client-id: ${{ vars.AZURE_CLIENT_ID }}
59+
tenant-id: ${{ vars.AZURE_TENANT_ID }}
60+
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
61+
62+
- name: Setup Terraform
63+
uses: hashicorp/setup-terraform@v3
64+
with:
65+
terraform_version: ${{ env.TF_VERSION }}
66+
67+
- name: Register Cost Management export provider
68+
run: az provider register --namespace Microsoft.CostManagementExports --wait
69+
70+
- name: Terraform init
71+
run: terraform init -input=false
72+
73+
- name: Terraform plan
74+
if: inputs.action == 'plan' || inputs.action == 'apply'
75+
run: terraform plan -var-file="environments/${{ inputs.environment }}.tfvars" -out="${{ inputs.environment }}.tfplan" -input=false
76+
77+
- name: Upload plan artifact
78+
if: inputs.action == 'plan' || inputs.action == 'apply'
79+
uses: actions/upload-artifact@v4
80+
with:
81+
name: ${{ inputs.environment }}-tfplan
82+
path: ${{ inputs.environment }}.tfplan
83+
84+
- name: Terraform apply
85+
if: inputs.action == 'apply'
86+
run: terraform apply -input=false "${{ inputs.environment }}.tfplan"
87+
88+
- name: Terraform destroy
89+
if: inputs.action == 'destroy'
90+
run: terraform destroy -var-file="environments/${{ inputs.environment }}.tfvars" -auto-approve -input=false

.gitignore

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Terraform
2+
.terraform/
3+
*.tfstate
4+
*.tfstate.*
5+
*.tfplan
6+
crash.log
7+
crash.*.log
8+
override.tf
9+
override.tf.json
10+
*_override.tf
11+
*_override.tf.json
12+
13+
# Local secrets and evidence
14+
terraform.tfvars
15+
*.auto.tfvars
16+
artifacts/
17+
reports/
18+
*.local.json
19+
20+
# Editors and OS
21+
.vscode/
22+
.idea/
23+
Thumbs.db
24+
Desktop.ini

.terraform.lock.hcl

Lines changed: 63 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Chris Panagiotidis
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)