refactoring via GH Copilot streamlining the content

This commit is contained in:
Christian Lechner 2025-11-03 16:40:12 +01:00
parent 013d3e87af
commit 67c0ec299e

View File

@ -1,190 +1,129 @@
--- description: 'Terraform conventions and guidelines for SAP Business Technology Platform (SAP BTP).'
description: 'Terraform Conventions and Guidelines on SAP Business Technology Platform (SAP BTP).' applyTo: '**/*.tf, **/*.tfvars, **/*.tflint.hcl, **/*.tf.json, **/*.tfvars.json'
applyTo: '**/*.terraform, **/*.tf, **/*.tfvars, **/*.tflint.hcl, **/*.tfstate, **/*.tf.json, **/*.tfvars.json'
---
# Terraform on SAP BTP - Best Practices and Conventions # Terraform on SAP BTP Best Practices & Conventions
## General Instructions ## Core Principles
- Use Terraform to provision and manage infrastructure. Keep Terraform code minimal, modular, repeatable, secure, and auditable.
- Use version control for your Terraform configurations. Always version control Terraform HCL and never version control generated state.
## Security ## Security
- Always use the latest stable version of Terraform and its providers. Mandatory:
- Regularly update your Terraform configurations to incorporate security patches and improvements. - Use the latest stable Terraform CLI and provider versions; upgrade proactively for security patches.
- Never commit sensitive information such as credentials, API keys, passwords, certificates, or Terraform state to version control. - Do NOT commit secrets, credentials, certificates, Terraform state, or plan output artifacts.
- Use `.gitignore` to exclude files containing sensitive information from version control. - Mark all secret variables and outputs as `sensitive = true`.
- Always mark sensitive variables as `sensitive = true` in your Terraform configurations. - Prefer ephemeral / writeonly provider auth (Terraform >= 1.11) so secrets never persist in state.
- This prevents sensitive values from being displayed in the Terraform plan or apply output. - Minimize sensitive outputs; emit only what downstream automation truly needs.
- Use `ephemeral` secrets with write-only parameters when supported (Terraform v1.11+) to avoid storing secrets in state files. - Continuously scan with `tfsec`, `trivy`, `checkov` (pick at least one) in CI.
- Avoid outputting sensitive data unless absolutely necessary. - Periodically review provider credentials, rotate keys, and enable MFA where supported.
- Regularly review and audit your Terraform configurations for security vulnerabilities.
- Use tools like `trivy`, `tfsec`, or `checkov` to scan your Terraform configurations for security issues.
## Modularity ## Modularity
- Use separate projects for each major component of the infrastructure; this: Structure for clarity and speed:
- Reduces complexity. - Split by logical domain (e.g., entitlements, service instances) NOT by environment.
- Makes it easier to manage and maintain configurations. - Use modules for reusable multiresource patterns only; avoid singleresource wrapper modules.
- Speeds up `plan` and `apply` operations. - Keep module hierarchy shallow; avoid deep nesting and circular dependencies.
- Allows for independent development and deployment of components. - Expose only essential crossmodule data via `outputs` (mark sensitive when required).
- Reduces the risk of accidental changes to unrelated resources.
- Use modules to avoid duplication of configurations.
- Use modules to encapsulate related resources and configurations.
- Use modules to simplify complex configurations and improve readability.
- Avoid circular dependencies between modules.
- Avoid unnecessary layers of abstraction; use modules only when they add value.
- Avoid using modules for single resources; only use them for groups of related resources.
- Avoid excessive nesting of modules; keep the module hierarchy shallow.
- Use `output` blocks to expose important information about your infrastructure.
- Use outputs to provide information that is useful for other modules or for users of the configuration.
- Avoid exposing sensitive information in outputs; mark outputs as `sensitive = true` if they contain sensitive data.
## Maintainability ## Maintainability
- Prioritize readability, clarity, and maintainability. Aim for explicit > implicit.
- Use comments to explain complex configurations and why certain design decisions were made. - Comment WHY, not WHAT; avoid restating obvious resource attributes.
- Write concise, efficient, and idiomatic configs that are easy to understand. - Parameterize (variables) instead of hardcoding; provide defaults only when sensible.
- Avoid using hard-coded values; use variables for configuration instead. - Prefer data sources for external existing infra; never for resources just created in same root use outputs.
- Set default values for variables, where appropriate. - Avoid data sources in generic reusable modules; require inputs instead.
- Use data sources to retrieve information about existing resources instead of requiring manual configuration. - Remove unused / slow data sources; they degrade plan time.
- This reduces the risk of errors, ensures that configurations are always up-to-date, and allows configurations to adapt to different environments. - Use `locals` for derived or repeated expressions to centralize logic.
- Avoid using data sources for resources that are created within the same configuration; use outputs instead.
- Avoid using data sources in reusable modules. Prefer explicit module parameters over data source lookups.
- Avoid, or remove, unnecessary data sources; they slow down `plan` and `apply` operations.
- Use `locals` for values that are used multiple times to ensure consistency.
## Style and Formatting ## Style & Formatting
### General Style Guidelines ### General
- Descriptive, consistent names for resources, variables, outputs.
- snake_case for variables & locals.
- 2 spaces indentation; run `terraform fmt -recursive`.
- Follow Terraform best practices for resource naming and organization. ### Layout & Files
- Use descriptive names for resources, variables, and outputs.
- Use consistent naming conventions across all configurations.
- Use snake_case for all variable names.
- Follow the **Terraform Style Guide** for formatting.
- Use consistent indentation (2 spaces for each level).
### Folder Layout and File Organization
- Structure Terraform configurations with logical file separation:
- Use `main.tf` for resources.
- Use `variables.tf` for inputs.
- Use `outputs.tf` for outputs.
- Use `terraform.tf` for provider configurations.
- Use `locals.tf` to abstract complex expressions and for better readability.
- Use a consistent folder structure for Terraform configurations.
- Use tfvars to modify environmental differences. In general, aim to keep environments similar.
- A **suggested** structure is:
Recommended structure:
```text ```text
my-sap-bto-app/ my-sap-btp-app/
├── infra/ # Terraform root module ├── infra/ # Root module
│ ├── main.tf # Core resources │ ├── main.tf # Core resources (split by domain when large)
│ ├── variables.tf # Input variables │ ├── variables.tf # Inputs
│ ├── outputs.tf # Outputs │ ├── outputs.tf # Outputs
│ ├── provider.tf # Provider configuration │ ├── provider.tf # Provider config(s)
│ ├── locals.tf # Local values │ ├── locals.tf # Local/derived values
│ └── environments/ # Environment-specific configurations │ └── environments/ # Environment var files only
│ ├── dev.tfvars # Development environment │ ├── dev.tfvars
│ ├── test.tfvars # Test environment │ ├── test.tfvars
│ └── prod.tfvars # Production environment │ └── prod.tfvars
├── .github/workflows/ # CI/CD pipelines (if using github) ├── .github/workflows/ # CI/CD (if GitHub)
└── README.md # Documentation └── README.md # Documentation
``` ```
- Never change the folder structure without direct agreement with the user. Rules:
- If the `main.tf` or `variables.tf` files grow too large, split them into multiple files by resource type or function (e.g., `main.services.tf`, `main.rolecollectionassignments.tf` - move equivalent variables to `variables.services.tf`, etc.) - Do NOT create separate branches/repos/folders per environment (antipattern).
- **Antipatterns** are - Keep environment drift minimal; encode differences in *.tfvars files only.
- branch per environment - Split oversized `main.tf` / `variables.tf` into logically named fragments (e.g., `main_services.tf`, `variables_services.tf`).
- repository per environment Keep naming consistent.
- folder per environment
- or similar layouts that make it hard to test the root folder logic between environments.
### Resource Block Organization ### Resource Block Organization
- Place `depends_on` blocks at the very beginning of resource definitions to make dependency relationships clear. Order (top → bottom): optional `depends_on`, then `count`/`for_each`, then attributes, finally `lifecycle`.
- Use `depends_on` only when necessary to avoid circular dependencies. - Use `depends_on` ONLY when Terraform cannot infer dependency (e.g., data source needs entitlement).
- Place `for_each` and `count` blocks at the beginning of resource definitions to clarify the resource's instantiation logic. - Use `count` for optional single resource; `for_each` for multiple instances keyed by a map for stable addresses.
- Use `count` for 0-1 resources. - Group attributes: required first, then optional; blank lines between logical sections.
- Use `for_each` for multiple resources. - Alphabetize within a section for faster scanning.
- Use `for_each` for collections and `count` for numeric iterations.
- Prefer maps for stable resource addresses.
- Place them after `depends_on` blocks, if they are present.
- Place `lifecycle` blocks at the end of resource definitions.
- Group related attributes together within blocks.
- Place required attributes before optional ones, and comment each section accordingly.
- Separate attribute sections with blank lines to improve readability.
- Alphabetize attributes within each section for easier navigation.
- Use blank lines to separate logical sections of your configurations.
### Variables Style Standards ### Variables
- Every variable: explicit `type`, nonempty `description`.
- Prefer concrete types (`object`, `map(string)`, etc.) over `any`.
- Avoid null defaults for collections; use empty lists/maps instead.
- All variables must have explicit type declarations. ### Locals
- All variables must have a comprehensive descriptions. - Centralize computed or repeated expressions.
- Avoid nullable defaults for collection values unless there's a specific need. - Group related values into object locals for cohesion.
### Locals Style Standards ### Outputs
- Expose only what downstream modules/automation consume.
- Mark secrets `sensitive = true`.
- Always give a clear `description`.
- Use locals for computed values and complex expressions. ### Formatting & Linting
- Improve readability by extracting repeated expressions. - Run `terraform fmt -recursive` (required in CI).
- Combine related values into structured locals. - Enforce `tflint` (and optionally `terraform validate`) in precommit / CI.
### Outputs Style Standards
- Avoid unnecessary outputs, only use these to expose information needed by other configurations.
- Use `sensitive = true` for outputs containing secrets.
- Provide clear descriptions for all outputs.
### Formatting and Linting
- Use `terraform fmt -recursive` to format your configurations automatically.
- Use `tflint` to check for style violations and ensure configurations follow best practices.
- Run `tflint` regularly to catch style issues early in the development process.
## Documentation ## Documentation
- Always include `description` and `type` attributes for variables and outputs. Mandatory:
- Use clear and concise descriptions to explain the purpose of each variable and output. - `description` + `type` on all variables & outputs.
- Use appropriate types for variables (e.g., `string`, `number`, `bool`, `list`, `map`). - A concise root `README.md`: purpose, prerequisites, auth model, usage (init/plan/apply), testing, rollback.
- Document your Terraform configurations using comments, where appropriate. - Generate module docs with `terraform-docs` (add to CI if possible).
- Use comments to explain the purpose of resources and variables. - Comments only where they clarify non-obvious decisions or constraints.
- Use comments to explain complex configurations or decisions.
- Avoid redundant comments; comments should add value and clarity.
- Include a `README.md` file in each project to provide an overview of the project and its structure.
- Include instructions for setting up and using the configurations.
- Use `terraform-docs` to generate documentation for your configurations automatically.
## State Management ## State Management
- Use a remote backend supporting locking (e.g., Terraform Cloud, AWS S3, GCS, Azure Storage). Avoid SAP BTP Object Store (insufficient capabilities for reliable locking & security).
- Use remote backend with state locking - NEVER commit `*.tfstate` or backups.
- Do not use SAP BTP Object Store as a backend for Terraform state as your access is too limited to ensure a proper state management - Encrypt state at rest & in transit; restrict access by principle of least privilege.
- Never commit state files to source control
- Enable encryption at rest and in transit
## Validation ## Validation
- Run `terraform validate` (syntax & internal checks) before committing.
- Run `terraform validate` to check syntax - Confirm with user before `terraform plan` (requires auth & global account subdomain). Provide auth via env vars or tfvars; NEVER inline secrets in provider blocks.
- Ask before running `terraform plan`. Terraform plan will require authentication information and the global account subdomain. The authentication information should be sourced from environment parameters or via tfvars, but **MUST NOT** be coded in the provider block. - Test in nonprod first; ensure idempotent applies.
- Test configurations in non-production environments first
- Ensure idempotency (multiple applies produce same result)
## Testing ## Testing
- Use Terraform test framework (`*.tftest.hcl`) for module logic & invariants.
- Write tests to validate the functionality of your Terraform configurations. - Cover success & failure paths; keep tests stateless/idempotent.
- Use the `.tftest.hcl` extension for test files. - Prefer mocking external data sources where feasible.
- Write tests to cover both positive and negative scenarios.
- Ensure tests are idempotent and can be run multiple times without side effects.
## SAP BTP Provider Specifics ## SAP BTP Provider Specifics
When provisioning SAP BTP resources, follow these additional best practices: Guidelines:
- Resolve service plan IDs using `data "btp_subaccount_service_plan"` and reference `serviceplan_id` from that data source.
- Use the data source `btp_subaccount_service_plan` to retrieve service plan information when provisioning the resource `btp_subaccount_service_instance`
- Sample code should look like this:
Example:
```terraform ```terraform
data "btp_subaccount_service_plan" "example" { data "btp_subaccount_service_plan" "example" {
subaccount_id = var.subaccount_id subaccount_id = var.subaccount_id
@ -195,16 +134,11 @@ When provisioning SAP BTP resources, follow these additional best practices:
resource "btp_subaccount_service_instance" "example" { resource "btp_subaccount_service_instance" "example" {
subaccount_id = var.subaccount_id subaccount_id = var.subaccount_id
serviceplan_id = data.btp_subaccount_service_plan.example.id serviceplan_id = data.btp_subaccount_service_plan.example.id
name = "my-example-instance-new" name = "my-example-instance"
} }
``` ```
- Use **explicit dependencies** between `btp_subaccount_entitlement` and the data source `btp_subaccount_service_plan` to ensure correct provisioning order for the resource `btp_subaccount_service_instance` that implictly dpeends on the data source. Explicit dependencies (provider cannot infer):
- The connection between the resource `btp_subaccount_entitlement` and the data source `btp_subaccount_service_plan` is not automatically inferred by the provider.
- The data source `btp_subaccount_service_plan` depends on the `btp_subaccount_entitlement`.
- You can derive the dependency by comparing the `service_name` and `plan_name` of the resource and data source.
- Sample code should look like this:
```terraform ```terraform
resource "btp_subaccount_entitlement" "example" { resource "btp_subaccount_entitlement" "example" {
subaccount_id = var.subaccount_id subaccount_id = var.subaccount_id
@ -216,48 +150,44 @@ When provisioning SAP BTP resources, follow these additional best practices:
subaccount_id = var.subaccount_id subaccount_id = var.subaccount_id
service_name = "your_service_name" service_name = "your_service_name"
plan_name = "your_plan_name" plan_name = "your_plan_name"
depends_on = [btp_subaccount_entitlement.example] depends_on = [btp_subaccount_entitlement.example]
} }
``` ```
- Use **explicit dependencies** between `btp_subaccount_entitlement` and `btp_subaccount_subscription` resources to ensure correct provisioning order. Subscriptions also depend on entitlements; add `depends_on` when the provider cannot infer linkage via attributes (match `service_name`/`plan_name``app_name`).
- The connection between `btp_subaccount_entitlement` and `btp_subaccount_subscription` is not automatically inferred by the provider.
- The resource `btp_subaccount_subscription` depends on the `btp_subaccount_entitlement`.
- You can derive the dependency by comparing the `app_name` with the `service_name` and the attributes `plan_name`.
-
## Tool Integration ## Tool Integration
### Use Available Tools ### HashiCorp Terraform MCP Server
Use the Terraform MCP Server for interactive schema lookup, resource block drafting, and validation.
- **Terraform MCP Serer**: Use the Terraform MCP server for provider specific guidance https://github.com/mcp/hashicorp/terraform-mcp-server 1. Install & run server (see https://github.com/mcp/hashicorp/terraform-mcp-server).
2. Add it as a tool in your Copilot / MCP client configuration.
3. Query provider schema (e.g., list resources, data sources) before authoring.
4. Generate draft resource blocks, then refine manually for naming & tagging standards.
5. Validate plan summaries (never include secrets); confirm diff with reviewer before `apply`.
### Terraform Registry ### Terraform Registry
Reference the SAP BTP provider docs: https://registry.terraform.io/providers/SAP/btp/latest/docs for authoritative resource & data source fields. Crosscheck MCP responses with registry docs if uncertain.
- **Schema Information**: Use the Terraform Provider for SAP BTP at https://registry.terraform.io/providers/SAP/btp/latest/docs for SAP BTP resources available via the MCP server ## AntiPatterns (Avoid)
## Anti-Patterns to Avoid Configuration:
- Hardcoded environmentspecific values (use variables & tfvars).
- Routine use of `terraform import` (migration only).
- Deep / opaque conditional logic and dynamic blocks that reduce clarity.
- `local-exec` provisioners except for unavoidable integration gaps.
- Mixing SAP BTP provider with Cloud Foundry provider in the same root unless explicitly justified (split modules).
**Configuration:** Security:
- Storing secrets in HCL, state, or VCS.
- Disabling encryption, validation, or scanning for speed.
- Using default passwords/keys or reusing credentials across environments.
- MUST NOT hardcode values that should be parameterized Operational:
- MUST NOT use `terraform import` as a regular workflow pattern - Direct production applies without prior nonprod validation.
- SHOULD avoid complex conditional logic that makes code hard to understand - Manual drift changes outside Terraform.
- MUST NOT use `local-exec` provisioners unless absolutely necessary - Ignoring state inconsistencies / corruption symptoms.
- SHOULD NOT combine the Terraform provider for SAP BTP with the Terraform prpovider for Cloud Foundry in the same configuration - Running production applies from uncontrolled local laptops (use CI/CD or approved runners).
- Reading business data from raw `*.tfstate` instead of outputs / data sources.
**Security:** All changes must flow through Terraform CLI + HCL never mutate state manually.
- MUST NEVER store secrets in Terraform files or state
- MUST NOT disable security features for convenience
- MUST NOT use default passwords or keys in resource configurations
**Operational:**
- MUST NOT apply Terraform changes directly to production without testing
- MUST avoid making manual changes to Terraform-managed resources
- MUST NOT ignore Terraform state file corruption or inconsistencies
- MUST NOT run Terraform from local machines for production
- SHOULD NOT use a Terraform state file (`**/*.tfstate`) for read operations
- MUST NOT use a Terraform state file (`**/*.tfstate`) for read operations all changes must be made via Terraform CLI or HCL.