Skip to content

Commit 761e503

Browse files
authored
Merge pull request #10 from stackitcloud/feature/cleanup-and-dynamic-inputs
Feature/cleanup and dynamic inputs
2 parents be0a9cc + c607f78 commit 761e503

75 files changed

Lines changed: 1081 additions & 1595 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/copilot-instructions.md

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
# Terraform Style Guide
2+
3+
This file defines the Terraform style conventions for this repository, based on the [HashiCorp Terraform Style Guide](https://developer.hashicorp.com/terraform/language/style). All Terraform code in this workspace MUST conform to these rules. When reviewing or generating Terraform code, enforce every rule below.
4+
5+
---
6+
7+
## Code Formatting
8+
9+
- Indent **two spaces** per nesting level.
10+
- Align `=` signs when multiple single-line arguments appear on consecutive lines at the same nesting level:
11+
```hcl
12+
ami = "abc123"
13+
instance_type = "t2.micro"
14+
```
15+
- Place all arguments at the **top** of a block, then nested blocks **below**, separated by one blank line.
16+
- Use **empty lines** to separate logical groups of arguments within a block.
17+
- **Meta-arguments first**: list meta-arguments (`count`, `for_each`) at the top of a resource block, separated from other arguments by one blank line.
18+
- **Meta-argument blocks last**: place `lifecycle`, `depends_on` blocks at the bottom, separated from other blocks by one blank line.
19+
```hcl
20+
resource "aws_instance" "example" {
21+
# meta-argument first
22+
count = 2
23+
24+
ami = "abc123"
25+
instance_type = "t2.micro"
26+
27+
network_interface {
28+
# ...
29+
}
30+
31+
# meta-argument block last
32+
lifecycle {
33+
create_before_destroy = true
34+
}
35+
}
36+
```
37+
- Separate **top-level blocks** with exactly one blank line.
38+
- Separate **nested blocks** with blank lines, except when grouping related blocks of the same type.
39+
- Run `terraform fmt` before committing. Use `-recursive` to format subdirectories.
40+
41+
## Code Validation
42+
43+
- Run `terraform validate` before committing to check syntax and internal consistency.
44+
45+
## Comments
46+
47+
- Use `#` for **all** comments (single-line and multi-line). Do NOT use `//` or `/* */`.
48+
- Write self-explanatory code; only add comments when necessary to clarify complexity.
49+
50+
## Resource Naming
51+
52+
- Use a **descriptive noun** for every resource name.
53+
- Separate words with **underscores** (`_`), not hyphens or camelCase.
54+
- Do NOT include the resource type in the resource name (the address already contains it).
55+
- Wrap both resource type and name in **double quotes**.
56+
57+
**Bad:**
58+
```hcl
59+
resource aws_instance webAPI-aws-instance {...}
60+
```
61+
**Good:**
62+
```hcl
63+
resource "aws_instance" "web_api" {...}
64+
```
65+
66+
## Resource Order
67+
68+
- Define **data sources before** the resources that reference them so code "builds on itself".
69+
- Within a resource block, order parameters as follows:
70+
1. `count` or `for_each` meta-argument
71+
2. Resource-specific non-block parameters
72+
3. Resource-specific block parameters
73+
4. `lifecycle` block (if required)
74+
5. `depends_on` (if required)
75+
76+
## Variables
77+
78+
- Every variable MUST have a `type` and a `description`.
79+
- Provide a `default` for optional variables.
80+
- Set `sensitive = true` for passwords, private keys, and other secrets.
81+
- Use `validation` blocks only when values have uniquely restrictive requirements.
82+
- Order variable parameters:
83+
1. `type`
84+
2. `description`
85+
3. `default` (optional)
86+
4. `sensitive` (optional)
87+
5. `validation` blocks
88+
89+
```hcl
90+
variable "db_disk_size" {
91+
type = number
92+
description = "Disk size for the API database"
93+
default = 100
94+
}
95+
96+
variable "db_password" {
97+
type = string
98+
description = "Database password"
99+
sensitive = true
100+
}
101+
```
102+
103+
## Outputs
104+
105+
- Every output MUST have a `description`.
106+
- Order output parameters:
107+
1. `description`
108+
2. `value`
109+
3. `sensitive` (optional)
110+
111+
```hcl
112+
output "web_public_ip" {
113+
description = "Public IP of the web instance"
114+
value = aws_instance.web.public_ip
115+
}
116+
```
117+
118+
## Local Values
119+
120+
- Use local values **sparingly**; overuse makes code harder to understand.
121+
- If referenced in multiple files, define locals in a `locals.tf` file.
122+
- If specific to one file, define locals at the **top** of that file.
123+
- Use descriptive nouns with underscores for local value names.
124+
125+
## Provider Configuration
126+
127+
- Always include a **default provider configuration** (without `alias`).
128+
- Define **all providers** in the same file.
129+
- If multiple instances of a provider exist, define the **default first**.
130+
- For non-default providers, the `alias` parameter must be the **first** parameter in the block.
131+
132+
## Dynamic Resource Count (`count` / `for_each`)
133+
134+
- Use `count` and `for_each` **sparingly**; they add complexity.
135+
- Use `count` when resources are almost identical.
136+
- Use `for_each` when arguments need distinct values not derivable from an integer.
137+
- A common pattern for conditional resources: `count = var.condition ? 1 : 0`.
138+
- If the effect of a meta-argument is not immediately obvious, add a comment.
139+
140+
## File Naming Conventions
141+
142+
- `main.tf` — resource and data source blocks (or split by logical group as the codebase grows).
143+
- `variables.tf` — all variable blocks, in **alphabetical order**.
144+
- `outputs.tf` — all output blocks, in **alphabetical order**.
145+
- `providers.tf` — all `provider` blocks and configuration.
146+
- `terraform.tf` — single `terraform` block with `required_version` and `required_providers`.
147+
- `backend.tf` — backend configuration.
148+
- `locals.tf` — local values (if shared across files).
149+
- `override.tf` — override definitions (use sparingly, comment the original resource).
150+
- When the codebase grows, split resources into logically named files (e.g., `network.tf`, `storage.tf`, `compute.tf`). It should be immediately clear where to find any resource.
151+
152+
## Version Pinning
153+
154+
- Pin **provider versions** in `required_providers`.
155+
- Pin **module versions** to a specific major and minor version.
156+
- Set a minimum `required_version` for the Terraform binary in the `terraform` block.
157+
```hcl
158+
terraform {
159+
required_providers {
160+
aws = {
161+
source = "hashicorp/aws"
162+
version = "5.34.0"
163+
}
164+
}
165+
required_version = ">= 1.7"
166+
}
167+
```
168+
- For registry modules, use the `version` parameter in the `module` block.
169+
170+
## Module Structure
171+
172+
- Group logically related resources into modules.
173+
- Store local (child) modules in `./modules/<module_name>`.
174+
- Follow the [standard module structure](https://developer.hashicorp.com/terraform/language/modules/develop/structure).
175+
- Name module repositories `terraform-<PROVIDER>-<NAME>` if publishing to a registry.
176+
177+
## .gitignore
178+
179+
Do NOT commit:
180+
- `terraform.tfstate` and `terraform.tfstate.*` backup files.
181+
- `.terraform.tfstate.lock.info`.
182+
- `.terraform/` directory.
183+
- Saved plan files (from `terraform plan -out`).
184+
- `.tfvars` files containing sensitive information.
185+
186+
Always commit:
187+
- All `.tf` code files.
188+
- `.terraform.lock.hcl` dependency lock file.
189+
- `.gitignore`.
190+
- `README.md`.
191+
192+
## Secrets Management
193+
194+
- Never store secrets in plain-text Terraform files.
195+
- Use provider-specific environment variables for credentials.
196+
- Use a secrets manager (e.g., HashiCorp Vault) where possible.
197+
- Mark sensitive variables with `sensitive = true`.
198+
199+
## Testing
200+
201+
- Write tests for Terraform modules.
202+
- Run tests as pre-merge checks in pull requests or as CI/CD pipeline steps.
203+
204+
## Linting
205+
206+
- Use a linter such as [TFLint](https://github.com/terraform-linters/tflint) to enforce coding standards.
207+
- Run `terraform fmt` and `terraform validate` before every commit.

CODEOWNERS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
* david.wenzel@stackit.cloud @mahauber
2+
docs/* @lweberru
3+
scripts/* @lweberru

README.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,29 @@ Contributions are welcome! Please feel free to submit a Pull Request.
9393

9494
## 📄 License
9595

96-
This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details.
96+
This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details.
97+
<!-- BEGIN_TF_DOCS -->
98+
### Requirements
99+
100+
No requirements.
101+
102+
### Providers
103+
104+
No providers.
105+
106+
### Modules
107+
108+
No modules.
109+
110+
### Resources
111+
112+
No resources.
113+
114+
### Inputs
115+
116+
No inputs.
117+
118+
### Outputs
119+
120+
No outputs.
121+
<!-- END_TF_DOCS -->

examples/01-standalone/README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<!-- BEGIN_TF_DOCS -->
2+
## Requirements
3+
4+
| Name | Version |
5+
|------|---------|
6+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.10 |
7+
| <a name="requirement_stackit"></a> [stackit](#requirement\_stackit) | 0.88.0 |
8+
9+
## Providers
10+
11+
No providers.
12+
13+
## Modules
14+
15+
| Name | Source | Version |
16+
|------|--------|---------|
17+
| <a name="module_devops"></a> [devops](#module\_devops) | ../../modules/devops | n/a |
18+
| <a name="module_governance"></a> [governance](#module\_governance) | ../../modules/governance | n/a |
19+
| <a name="module_landing_zone"></a> [landing\_zone](#module\_landing\_zone) | ../../modules/landing-zone | n/a |
20+
| <a name="module_management"></a> [management](#module\_management) | ../../modules/management | n/a |
21+
| <a name="module_sandboxes"></a> [sandboxes](#module\_sandboxes) | ../../modules/sandboxes | n/a |
22+
23+
## Resources
24+
25+
No resources.
26+
27+
## Inputs
28+
29+
| Name | Description | Type | Default | Required |
30+
|------|-------------|------|---------|:--------:|
31+
| <a name="input_company_code"></a> [company\_code](#input\_company\_code) | Company code used in resource naming conventions. | `string` | n/a | yes |
32+
| <a name="input_company_name"></a> [company\_name](#input\_company\_name) | Name of the company. | `string` | n/a | yes |
33+
| <a name="input_labels"></a> [labels](#input\_labels) | Additional labels to apply to all resources. | `map(string)` | `{}` | no |
34+
| <a name="input_landing_zones"></a> [landing\_zones](#input\_landing\_zones) | Map of landing zones to create (public, without network area). | <pre>map(object({<br/> project_name = string<br/> project_code = string<br/> owner_email = string<br/> env = optional(string, "dev")<br/> role_assignments = optional(list(object({<br/> role = string<br/> subject = string<br/> })), [])<br/> network_prefix_length = optional(number, null)<br/> custom_roles = optional(list(object({<br/> name = string<br/> description = string<br/> permissions = list(string)<br/> })), [])<br/> }))</pre> | `{}` | no |
35+
| <a name="input_organization_auditors"></a> [organization\_auditors](#input\_organization\_auditors) | List of organization auditors. | `list(string)` | `[]` | no |
36+
| <a name="input_organization_id"></a> [organization\_id](#input\_organization\_id) | Container ID of the root organization. | `string` | n/a | yes |
37+
| <a name="input_organization_owners"></a> [organization\_owners](#input\_organization\_owners) | List of organization owners. | `list(string)` | `[]` | no |
38+
| <a name="input_owner_email"></a> [owner\_email](#input\_owner\_email) | Email address of the owner. Required for STACKIT resource manager. | `string` | n/a | yes |
39+
| <a name="input_platform_admins"></a> [platform\_admins](#input\_platform\_admins) | List of platform administrators. | `list(string)` | `[]` | no |
40+
| <a name="input_region"></a> [region](#input\_region) | STACKIT region for regional resources. | `string` | `"eu01"` | no |
41+
| <a name="input_sandboxes"></a> [sandboxes](#input\_sandboxes) | List of sandboxes to create. | <pre>list(object({<br/> project_name = string<br/> owner_emails = optional(list(string))<br/> project_owner_email = string<br/> }))</pre> | `[]` | no |
42+
43+
## Outputs
44+
45+
| Name | Description |
46+
|------|-------------|
47+
| <a name="output_devops_project_id"></a> [devops\_project\_id](#output\_devops\_project\_id) | The project ID of the DevOps project. |
48+
| <a name="output_governance_folder_ids"></a> [governance\_folder\_ids](#output\_governance\_folder\_ids) | Map of governance folder names to their container IDs. |
49+
| <a name="output_landing_zone_projects"></a> [landing\_zone\_projects](#output\_landing\_zone\_projects) | Map of landing zone project IDs. |
50+
| <a name="output_management_project_id"></a> [management\_project\_id](#output\_management\_project\_id) | The project ID of the Management project. |
51+
| <a name="output_sandbox_projects"></a> [sandbox\_projects](#output\_sandbox\_projects) | The created sandbox projects. |
52+
<!-- END_TF_DOCS -->

0 commit comments

Comments
 (0)