Skip to content

Commit 8d83ebb

Browse files
Initial commit
0 parents  commit 8d83ebb

46 files changed

Lines changed: 1803 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.

‎.gitattributes‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Set the default behavior, in case people don't have core.autocrlf set.
2+
* text=auto
3+
4+
# Explicitly declare text files you want to always be normalized and converted
5+
# to native line endings on checkout.
6+
*.c text
7+
*.h text
8+
9+
# Declare files that will always have CRLF line endings on checkout.
10+
*.sln text eol=crlf
11+
12+
# Denote all files that are truly binary and should not be modified.
13+
*.png binary
14+
*.jpg binary
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
name: "Bug report \U0001F41B"
3+
about: Report errors or unexpected behaviour
4+
title: 'Bug Report'
5+
labels: 'needs triage'
6+
assignees: ''
7+
---
8+
9+
### Community note
10+
11+
- Please vote on this issue by adding a thumbs-up reaction to the original issue, to help the
12+
community and maintainers prioritise it.
13+
- Please do not leave "+1" or "me too" comments; they add noise without helping triage.
14+
- If you are working on this issue or have opened a pull request, please leave a comment.
15+
16+
### Versions
17+
18+
- **terraform**:
19+
- **azurerm provider**:
20+
- **terraform-azure action (tag or sha)**:
21+
- **LibreDevOpsHelpers**:
22+
23+
### Description
24+
25+
#### Describe the bug
26+
27+
<!-- A clear and concise description of what the bug is. -->
28+
29+
#### Steps to reproduce
30+
31+
1. step 1
32+
2. step 2
33+
3. ...
34+
35+
#### Expected behaviour
36+
37+
<!-- What you expected to happen. -->
38+
39+
#### Logs
40+
41+
<!-- Relevant run logs. The action logs structured JSON; include the trace_id if you can. -->
42+
43+
#### Additional context
44+
45+
<!-- Anything else that helps explain the problem. -->

‎.github/ISSUE_TEMPLATE/config.yml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
blank_issues_enabled: false
3+
contact_links:
4+
- name: Libre DevOps documentation
5+
url: https://libredevops.org/docs
6+
about: Standards, cheatsheets, and guidance.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
name: "Feature request \U0001F680"
3+
about: Suggest an idea or enhancement
4+
title: 'Feature Request'
5+
labels: 'enhancement'
6+
assignees: ''
7+
---
8+
9+
### Community note
10+
11+
- Please vote on this issue by adding a thumbs-up reaction to the original issue, to help the
12+
community and maintainers prioritise it.
13+
- Please do not leave "+1" or "me too" comments; they add noise without helping triage.
14+
15+
### Description
16+
17+
#### What problem are you trying to solve?
18+
19+
<!-- A clear and concise description of the problem or limitation. -->
20+
21+
#### Proposed solution
22+
23+
<!-- What you would like to happen. -->
24+
25+
#### Alternatives considered
26+
27+
<!-- Any alternative approaches you have considered. -->
28+
29+
#### Additional context
30+
31+
<!-- Anything else that helps explain the request. -->

‎.github/PULL_REQUEST_TEMPLATE.md‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<!-- markdownlint-disable first-line-h1 -->
2+
<!-- Thank you for submitting a pull request. Please fill out the template below. -->
3+
4+
## Overview
5+
6+
<!-- A brief description of what this pull request fixes, changes, or adds. -->
7+
8+
## This PR fixes / adds / changes / removes
9+
10+
1. *Replace me*
11+
2. *Replace me*
12+
13+
### Breaking changes
14+
15+
1. *Replace me, or state "None".*
16+
17+
## Testing evidence
18+
19+
<!-- Show that the change works: CI run, local run output, or screenshots as appropriate. -->
20+
21+
## Checklist
22+
23+
- [ ] Checked for duplicate pull requests and issues.
24+
- [ ] Branch is up to date with the latest `main`.
25+
- [ ] `terraform fmt -check -recursive` passes, and the examples `validate`, `tflint`, and `trivy` clean.
26+
- [ ] PSScriptAnalyzer and the Pester tests pass.
27+
- [ ] Updated the relevant documentation.

‎.github/workflows/ci.yml‎

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
validate:
14+
name: Validate (analyzer, fmt, validate, test, tflint, trivy)
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- uses: hashicorp/setup-terraform@v3
20+
with:
21+
terraform_version: "~> 1.9"
22+
23+
- name: PSScriptAnalyzer
24+
shell: pwsh
25+
run: |
26+
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
27+
Install-Module PSScriptAnalyzer -MinimumVersion 1.21.0 -Force -Scope CurrentUser
28+
$results = Invoke-ScriptAnalyzer -Path ./Sort-LdoTerraform.ps1 -Settings ./PSScriptAnalyzerSettings.psd1
29+
if ($results) { $results | Format-Table -AutoSize | Out-String | Write-Host }
30+
$errors = @($results | Where-Object { $_.Severity -eq 'Error' })
31+
if ($errors.Count -gt 0) { throw "PSScriptAnalyzer found $($errors.Count) error(s)." }
32+
Write-Host 'PSScriptAnalyzer: clean.'
33+
34+
- name: Terraform fmt
35+
run: terraform fmt -check -recursive
36+
37+
- name: Validate and test the module and examples
38+
run: |
39+
set -euo pipefail
40+
for d in . examples/minimal examples/complete; do
41+
echo "== ${d} =="
42+
terraform -chdir="${d}" init -backend=false -input=false >/dev/null
43+
terraform -chdir="${d}" validate
44+
done
45+
terraform test
46+
47+
- name: Setup TFLint
48+
uses: terraform-linters/setup-tflint@b480b8fcdaa6f2c577f8e4fa799e89e756bb7c93 # v6.2.2
49+
50+
- name: TFLint (examples)
51+
run: |
52+
set -euo pipefail
53+
for d in examples/*/; do
54+
echo "== ${d%/} =="
55+
( cd "${d%/}" && tflint --init && tflint --recursive )
56+
done
57+
58+
# The trivyignores input is avoided on purpose: trivy-action concatenates the listed files
59+
# into an extension-less temp file, and trivy decides the ignore-file format by extension,
60+
# so a YAML waiver file silently stops matching. The TRIVY_IGNOREFILE env var hands trivy
61+
# the file directly, extension intact. Trivy hard-fails when the file is missing, so the
62+
# repo always carries at least the stub .trivyignore.yaml.
63+
- name: Trivy config scan (report CRITICAL to LOW, non-blocking)
64+
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
65+
env:
66+
TRIVY_IGNOREFILE: .trivyignore.yaml
67+
with:
68+
scan-type: config
69+
scan-ref: .
70+
severity: CRITICAL,HIGH,MEDIUM,LOW
71+
exit-code: "0"
72+
73+
- name: Trivy config gate (fail on HIGH or CRITICAL)
74+
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
75+
env:
76+
TRIVY_IGNOREFILE: .trivyignore.yaml
77+
with:
78+
scan-type: config
79+
scan-ref: .
80+
severity: HIGH,CRITICAL
81+
exit-code: "1"
82+
83+
# Live apply-then-destroy of an example via the terraform-azure action. Runs only on a manual
84+
# dispatch, since it needs the org OIDC variables, the firewalled state account, and the org
85+
# secrets that the tenant bootstrap publishes. Trigger with: gh workflow run ci.yml
86+
self-test:
87+
name: Self-test (plan, apply, destroy)
88+
if: ${{ github.event_name == 'workflow_dispatch' }}
89+
needs: validate
90+
runs-on: ubuntu-latest
91+
permissions:
92+
id-token: write
93+
contents: read
94+
steps:
95+
- uses: actions/checkout@v4
96+
97+
- name: Plan and apply the example stacks
98+
uses: libre-devops/terraform-azure@v1
99+
with:
100+
terraform-code-location: examples
101+
terraform-stack-to-run-json: '["minimal","complete"]'
102+
run-terraform-plan: true
103+
run-terraform-apply: true
104+
arm-client-id: ${{ vars.AZURE_CLIENT_ID }}
105+
arm-tenant-id: ${{ vars.AZURE_TENANT_ID }}
106+
arm-subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
107+
firewall-storage-account-name: ${{ secrets.TFSTATE_STORAGE_ACCOUNT }}
108+
firewall-storage-resource-group: ${{ secrets.TFSTATE_RESOURCE_GROUP }}
109+
terraform-init-extra-args-json: '["-reconfigure","-upgrade","-backend-config=resource_group_name=${{ secrets.TFSTATE_RESOURCE_GROUP }}","-backend-config=storage_account_name=${{ secrets.TFSTATE_STORAGE_ACCOUNT }}","-backend-config=container_name=${{ secrets.TFSTATE_BLOB_CONTAINER }}"]'
110+
111+
# ALWAYS runs, even when the apply step failed: a partial apply must still be torn down
112+
# rather than left for a human to notice. A destroy against a half-applied or empty state
113+
# is safe (it removes whatever landed).
114+
- name: Plan-destroy and destroy the example stacks
115+
if: ${{ always() }}
116+
uses: libre-devops/terraform-azure@v1
117+
with:
118+
terraform-code-location: examples
119+
terraform-stack-to-run-json: '["minimal","complete"]'
120+
run-terraform-plan: false
121+
run-terraform-plan-destroy: true
122+
run-terraform-destroy: true
123+
arm-client-id: ${{ vars.AZURE_CLIENT_ID }}
124+
arm-tenant-id: ${{ vars.AZURE_TENANT_ID }}
125+
arm-subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
126+
firewall-storage-account-name: ${{ secrets.TFSTATE_STORAGE_ACCOUNT }}
127+
firewall-storage-resource-group: ${{ secrets.TFSTATE_RESOURCE_GROUP }}
128+
terraform-init-extra-args-json: '["-reconfigure","-upgrade","-backend-config=resource_group_name=${{ secrets.TFSTATE_RESOURCE_GROUP }}","-backend-config=storage_account_name=${{ secrets.TFSTATE_STORAGE_ACCOUNT }}","-backend-config=container_name=${{ secrets.TFSTATE_BLOB_CONTAINER }}"]'
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: Terraform Deploy
2+
3+
# Manual apply of an example stack via the libre-devops/terraform-azure action. This workflow only
4+
# ever runs on a manual dispatch (no push/PR trigger), so nothing is applied automatically; it is
5+
# here for on-demand applies. It needs the org OIDC variables and the org secrets the tenant
6+
# bootstrap publishes (same as the CI self-test).
7+
on:
8+
workflow_dispatch:
9+
inputs:
10+
stack:
11+
description: Example stack to apply.
12+
type: choice
13+
options: [minimal, complete]
14+
default: minimal
15+
workspace:
16+
description: Terraform workspace (environment).
17+
type: string
18+
default: dev
19+
20+
permissions:
21+
id-token: write
22+
contents: read
23+
24+
jobs:
25+
deploy:
26+
name: Plan and apply (${{ inputs.stack }})
27+
runs-on: ubuntu-latest
28+
steps:
29+
- uses: actions/checkout@v4
30+
31+
- name: Plan and apply the example
32+
uses: libre-devops/terraform-azure@v1
33+
with:
34+
terraform-code-location: examples
35+
terraform-stack-to-run-json: '["${{ inputs.stack }}"]'
36+
terraform-workspace: ${{ inputs.workspace }}
37+
run-terraform-plan: true
38+
run-terraform-apply: true
39+
arm-client-id: ${{ vars.AZURE_CLIENT_ID }}
40+
arm-tenant-id: ${{ vars.AZURE_TENANT_ID }}
41+
arm-subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
42+
firewall-storage-account-name: ${{ secrets.TFSTATE_STORAGE_ACCOUNT }}
43+
firewall-storage-resource-group: ${{ secrets.TFSTATE_RESOURCE_GROUP }}
44+
terraform-init-extra-args-json: '["-reconfigure","-upgrade","-backend-config=resource_group_name=${{ secrets.TFSTATE_RESOURCE_GROUP }}","-backend-config=storage_account_name=${{ secrets.TFSTATE_STORAGE_ACCOUNT }}","-backend-config=container_name=${{ secrets.TFSTATE_BLOB_CONTAINER }}"]'
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Terraform Destroy
2+
3+
# Manual destroy of an example stack via the libre-devops/terraform-azure action. This workflow only
4+
# ever runs on a manual dispatch (no push/PR trigger), so nothing is destroyed automatically; it is
5+
# here for on-demand teardowns. Lint/scan/policy gates are skipped on a destroy run by the action. It
6+
# needs the org OIDC variables and the org secrets the tenant bootstrap publishes.
7+
on:
8+
workflow_dispatch:
9+
inputs:
10+
stack:
11+
description: Example stack to destroy.
12+
type: choice
13+
options: [minimal, complete]
14+
default: minimal
15+
workspace:
16+
description: Terraform workspace (environment).
17+
type: string
18+
default: dev
19+
20+
permissions:
21+
id-token: write
22+
contents: read
23+
24+
jobs:
25+
destroy:
26+
name: Plan-destroy and destroy (${{ inputs.stack }})
27+
runs-on: ubuntu-latest
28+
steps:
29+
- uses: actions/checkout@v4
30+
31+
- name: Plan-destroy and destroy the example
32+
uses: libre-devops/terraform-azure@v1
33+
with:
34+
terraform-code-location: examples
35+
terraform-stack-to-run-json: '["${{ inputs.stack }}"]'
36+
terraform-workspace: ${{ inputs.workspace }}
37+
run-terraform-plan: false
38+
run-terraform-plan-destroy: true
39+
run-terraform-destroy: true
40+
arm-client-id: ${{ vars.AZURE_CLIENT_ID }}
41+
arm-tenant-id: ${{ vars.AZURE_TENANT_ID }}
42+
arm-subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
43+
firewall-storage-account-name: ${{ secrets.TFSTATE_STORAGE_ACCOUNT }}
44+
firewall-storage-resource-group: ${{ secrets.TFSTATE_RESOURCE_GROUP }}
45+
terraform-init-extra-args-json: '["-reconfigure","-upgrade","-backend-config=resource_group_name=${{ secrets.TFSTATE_RESOURCE_GROUP }}","-backend-config=storage_account_name=${{ secrets.TFSTATE_STORAGE_ACCOUNT }}","-backend-config=container_name=${{ secrets.TFSTATE_BLOB_CONTAINER }}"]'

‎.gitignore‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Terraform working files
2+
**/.terraform/*
3+
*.tfstate
4+
*.tfstate.*
5+
crash.log
6+
crash.*.log
7+
8+
# Plan output
9+
*.tfplan
10+
*.plan
11+
tfplan*
12+
13+
# Local-only overrides and sensitive files (the .example is committed)
14+
backend_override.tf
15+
override.tf
16+
override.tf.json
17+
*_override.tf
18+
*_override.tf.json
19+
*.tfvars
20+
*.tfvars.json
21+
!*.tfvars.example
22+
23+
# Engine cache used by some justfiles
24+
.ldo/
25+
26+
# OS and editor
27+
.DS_Store
28+
.idea/
29+
.vscode/
30+
*.log
31+
32+
# The examples commit their .terraform.lock.hcl (they are root configs). The module root lock is
33+
# not used by consumers, so ignore only that one.
34+
/.terraform.lock.hcl

0 commit comments

Comments
 (0)