diff --git a/instructions/terraform-sap-btp.instructions.md b/instructions/terraform-sap-btp.instructions.md index e8a8d50..9a335fc 100644 --- a/instructions/terraform-sap-btp.instructions.md +++ b/instructions/terraform-sap-btp.instructions.md @@ -1,263 +1,193 @@ ---- -description: 'Terraform Conventions and Guidelines on SAP Business Technology Platform (SAP BTP).' -applyTo: '**/*.terraform, **/*.tf, **/*.tfvars, **/*.tflint.hcl, **/*.tfstate, **/*.tf.json, **/*.tfvars.json' ---- +description: 'Terraform conventions and guidelines for SAP Business Technology Platform (SAP BTP).' +applyTo: '**/*.tf, **/*.tfvars, **/*.tflint.hcl, **/*.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. -- Use version control for your Terraform configurations. +Keep Terraform code minimal, modular, repeatable, secure, and auditable. +Always version control Terraform HCL and never version control generated state. ## Security -- Always use the latest stable version of Terraform and its providers. - - Regularly update your Terraform configurations to incorporate security patches and improvements. -- Never commit sensitive information such as credentials, API keys, passwords, certificates, or Terraform state to version control. - - Use `.gitignore` to exclude files containing sensitive information from version control. -- Always mark sensitive variables as `sensitive = true` in your Terraform configurations. - - This prevents sensitive values from being displayed in the Terraform plan or apply output. -- Use `ephemeral` secrets with write-only parameters when supported (Terraform v1.11+) to avoid storing secrets in state files. -- Avoid outputting sensitive data unless absolutely necessary. -- 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. +Mandatory: +- Use the latest stable Terraform CLI and provider versions; upgrade proactively for security patches. +- Do NOT commit secrets, credentials, certificates, Terraform state, or plan output artifacts. +- Mark all secret variables and outputs as `sensitive = true`. +- Prefer ephemeral / write‑only provider auth (Terraform >= 1.11) so secrets never persist in state. +- Minimize sensitive outputs; emit only what downstream automation truly needs. +- Continuously scan with `tfsec`, `trivy`, `checkov` (pick at least one) in CI. +- Periodically review provider credentials, rotate keys, and enable MFA where supported. ## Modularity -- Use separate projects for each major component of the infrastructure; this: - - Reduces complexity. - - Makes it easier to manage and maintain configurations. - - Speeds up `plan` and `apply` operations. - - Allows for independent development and deployment of components. - - 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. +Structure for clarity and speed: +- Split by logical domain (e.g., entitlements, service instances) – NOT by environment. +- Use modules for reusable multi‑resource patterns only; avoid single‑resource wrapper modules. +- Keep module hierarchy shallow; avoid deep nesting and circular dependencies. +- Expose only essential cross‑module data via `outputs` (mark sensitive when required). ## Maintainability -- Prioritize readability, clarity, and maintainability. -- Use comments to explain complex configurations and why certain design decisions were made. -- Write concise, efficient, and idiomatic configs that are easy to understand. -- Avoid using hard-coded values; use variables for configuration instead. - - Set default values for variables, where appropriate. -- Use data sources to retrieve information about existing resources instead of requiring manual configuration. - - This reduces the risk of errors, ensures that configurations are always up-to-date, and allows configurations to adapt to different environments. - - 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. +Aim for explicit > implicit. +- Comment WHY, not WHAT; avoid restating obvious resource attributes. +- Parameterize (variables) instead of hard‑coding; provide defaults only when sensible. +- Prefer data sources for external existing infra; never for resources just created in same root – use outputs. +- Avoid data sources in generic reusable modules; require inputs instead. +- Remove unused / slow data sources; they degrade plan time. +- Use `locals` for derived or repeated expressions to centralize logic. -## 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. - - 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). +### Layout & Files -### Folder Layout and File Organization +Recommended structure: +```text +my-sap-btp-app/ +├── infra/ # Root module +│ ├── main.tf # Core resources (split by domain when large) +│ ├── variables.tf # Inputs +│ ├── outputs.tf # Outputs +│ ├── provider.tf # Provider config(s) +│ ├── locals.tf # Local/derived values +│ └── environments/ # Environment var files only +│ ├── dev.tfvars +│ ├── test.tfvars +│ └── prod.tfvars +├── .github/workflows/ # CI/CD (if GitHub) +└── README.md # Documentation +``` -- 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: - - ```text - my-sap-bto-app/ - ├── infra/ # Terraform root module - │ ├── main.tf # Core resources - │ ├── variables.tf # Input variables - │ ├── outputs.tf # Outputs - │ ├── provider.tf # Provider configuration - │ ├── locals.tf # Local values - │ └── environments/ # Environment-specific configurations - │ ├── dev.tfvars # Development environment - │ ├── test.tfvars # Test environment - │ └── prod.tfvars # Production environment - ├── .github/workflows/ # CI/CD pipelines (if using github) - └── README.md # Documentation - ``` - -- Never change the folder structure without direct agreement with the user. -- 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.) -- **Antipatterns** are - - branch per environment - - repository per environment - - folder per environment - - or similar layouts that make it hard to test the root folder logic between environments. +Rules: +- Do NOT create separate branches/repos/folders per environment (antipattern). +- Keep environment drift minimal; encode differences in *.tfvars files only. +- Split oversized `main.tf` / `variables.tf` into logically named fragments (e.g., `main_services.tf`, `variables_services.tf`). + Keep naming consistent. ### Resource Block Organization -- Place `depends_on` blocks at the very beginning of resource definitions to make dependency relationships clear. - - Use `depends_on` only when necessary to avoid circular dependencies. -- Place `for_each` and `count` blocks at the beginning of resource definitions to clarify the resource's instantiation logic. - - Use `count` for 0-1 resources. - - Use `for_each` for multiple resources. - - 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. +Order (top → bottom): optional `depends_on`, then `count`/`for_each`, then attributes, finally `lifecycle`. +- Use `depends_on` ONLY when Terraform cannot infer dependency (e.g., data source needs entitlement). +- Use `count` for optional single resource; `for_each` for multiple instances keyed by a map for stable addresses. +- Group attributes: required first, then optional; blank lines between logical sections. +- Alphabetize within a section for faster scanning. -### Variables Style Standards +### Variables +- Every variable: explicit `type`, non‑empty `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. -- All variables must have a comprehensive descriptions. -- Avoid nullable defaults for collection values unless there's a specific need. +### Locals +- Centralize computed or repeated expressions. +- 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. -- Improve readability by extracting repeated expressions. -- Combine related values into structured locals. - -### 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. +### Formatting & Linting +- Run `terraform fmt -recursive` (required in CI). +- Enforce `tflint` (and optionally `terraform validate`) in pre‑commit / CI. ## Documentation -- Always include `description` and `type` attributes for variables and outputs. - - Use clear and concise descriptions to explain the purpose of each variable and output. - - Use appropriate types for variables (e.g., `string`, `number`, `bool`, `list`, `map`). -- Document your Terraform configurations using comments, where appropriate. - - Use comments to explain the purpose of resources and variables. - - 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. +Mandatory: +- `description` + `type` on all variables & outputs. +- A concise root `README.md`: purpose, prerequisites, auth model, usage (init/plan/apply), testing, rollback. +- Generate module docs with `terraform-docs` (add to CI if possible). +- Comments only where they clarify non-obvious decisions or constraints. ## State Management - -- Use remote backend with state locking -- 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 -- Never commit state files to source control -- Enable encryption at rest and in transit +- 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). +- NEVER commit `*.tfstate` or backups. +- Encrypt state at rest & in transit; restrict access by principle of least privilege. ## Validation - -- Run `terraform validate` to check syntax -- 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 configurations in non-production environments first -- Ensure idempotency (multiple applies produce same result) +- Run `terraform validate` (syntax & internal checks) before committing. +- Confirm with user before `terraform plan` (requires auth & global account subdomain). Provide auth via env vars or tfvars; NEVER inline secrets in provider blocks. +- Test in non‑prod first; ensure idempotent applies. ## Testing - -- Write tests to validate the functionality of your Terraform configurations. -- Use the `.tftest.hcl` extension for test files. -- Write tests to cover both positive and negative scenarios. -- Ensure tests are idempotent and can be run multiple times without side effects. +- Use Terraform test framework (`*.tftest.hcl`) for module logic & invariants. +- Cover success & failure paths; keep tests stateless/idempotent. +- Prefer mocking external data sources where feasible. ## 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: - - ```terraform - data "btp_subaccount_service_plan" "example" { - subaccount_id = var.subaccount_id - service_name = "your_service_name" - plan_name = "your_plan_name" - } - - resource "btp_subaccount_service_instance" "example" { - subaccount_id = var.subaccount_id - serviceplan_id = data.btp_subaccount_service_plan.example.id - name = "my-example-instance-new" - } - ``` +Example: +```terraform +data "btp_subaccount_service_plan" "example" { + subaccount_id = var.subaccount_id + service_name = "your_service_name" + plan_name = "your_plan_name" +} -- 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. - - 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 - resource "btp_subaccount_entitlement" "example" { - subaccount_id = var.subaccount_id - service_name = "your_service_name" - plan_name = "your_plan_name" - } - - data "btp_subaccount_service_plan" "example" { - subaccount_id = var.subaccount_id - service_name = "your_service_name" - plan_name = "your_plan_name" - - depends_on = [btp_subaccount_entitlement.example] - } - ``` +resource "btp_subaccount_service_instance" "example" { + subaccount_id = var.subaccount_id + serviceplan_id = data.btp_subaccount_service_plan.example.id + name = "my-example-instance" +} +``` -- Use **explicit dependencies** between `btp_subaccount_entitlement` and `btp_subaccount_subscription` resources to ensure correct provisioning order. - - 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`. -- +Explicit dependencies (provider cannot infer): +```terraform +resource "btp_subaccount_entitlement" "example" { + subaccount_id = var.subaccount_id + service_name = "your_service_name" + plan_name = "your_plan_name" +} + +data "btp_subaccount_service_plan" "example" { + subaccount_id = var.subaccount_id + service_name = "your_service_name" + plan_name = "your_plan_name" + depends_on = [btp_subaccount_entitlement.example] +} +``` + +Subscriptions also depend on entitlements; add `depends_on` when the provider cannot infer linkage via attributes (match `service_name`/`plan_name` ↔ `app_name`). ## Tool Integration -### Use Available Tools - -- **Terraform MCP Serer**: Use the Terraform MCP server for provider specific guidance https://github.com/mcp/hashicorp/terraform-mcp-server +### HashiCorp Terraform MCP Server +Use the Terraform MCP Server for interactive schema lookup, resource block drafting, and validation. +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 +Reference the SAP BTP provider docs: https://registry.terraform.io/providers/SAP/btp/latest/docs for authoritative resource & data source fields. Cross‑check 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 +## Anti‑Patterns (Avoid) -## Anti-Patterns to Avoid +Configuration: +- Hard‑coded environment‑specific 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 -- MUST NOT use `terraform import` as a regular workflow pattern -- SHOULD avoid complex conditional logic that makes code hard to understand -- MUST NOT use `local-exec` provisioners unless absolutely necessary -- SHOULD NOT combine the Terraform provider for SAP BTP with the Terraform prpovider for Cloud Foundry in the same configuration +Operational: +- Direct production applies without prior non‑prod validation. +- Manual drift changes outside Terraform. +- Ignoring state inconsistencies / corruption symptoms. +- 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:** - -- 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. +All changes must flow through Terraform CLI + HCL – never mutate state manually.