refactoring via GH Copilot streamlining the content
This commit is contained in:
parent
013d3e87af
commit
67c0ec299e
@ -1,263 +1,193 @@
|
|||||||
---
|
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 / write‑only 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 multi‑resource patterns only; avoid single‑resource 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 cross‑module 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 hard‑coding; 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
|
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:
|
Rules:
|
||||||
- Use `main.tf` for resources.
|
- Do NOT create separate branches/repos/folders per environment (antipattern).
|
||||||
- Use `variables.tf` for inputs.
|
- Keep environment drift minimal; encode differences in *.tfvars files only.
|
||||||
- Use `outputs.tf` for outputs.
|
- Split oversized `main.tf` / `variables.tf` into logically named fragments (e.g., `main_services.tf`, `variables_services.tf`).
|
||||||
- Use `terraform.tf` for provider configurations.
|
Keep naming consistent.
|
||||||
- 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.
|
|
||||||
|
|
||||||
### 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`, 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.
|
### 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 pre‑commit / 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 non‑prod 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`
|
Example:
|
||||||
- Sample code should look like this:
|
```terraform
|
||||||
|
data "btp_subaccount_service_plan" "example" {
|
||||||
```terraform
|
subaccount_id = var.subaccount_id
|
||||||
data "btp_subaccount_service_plan" "example" {
|
service_name = "your_service_name"
|
||||||
subaccount_id = var.subaccount_id
|
plan_name = "your_plan_name"
|
||||||
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"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- 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.
|
resource "btp_subaccount_service_instance" "example" {
|
||||||
- The connection between the resource `btp_subaccount_entitlement` and the data source `btp_subaccount_service_plan` is not automatically inferred by the provider.
|
subaccount_id = var.subaccount_id
|
||||||
- The data source `btp_subaccount_service_plan` depends on the `btp_subaccount_entitlement`.
|
serviceplan_id = data.btp_subaccount_service_plan.example.id
|
||||||
- You can derive the dependency by comparing the `service_name` and `plan_name` of the resource and data source.
|
name = "my-example-instance"
|
||||||
- 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]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- Use **explicit dependencies** between `btp_subaccount_entitlement` and `btp_subaccount_subscription` resources to ensure correct provisioning order.
|
Explicit dependencies (provider cannot infer):
|
||||||
- The connection between `btp_subaccount_entitlement` and `btp_subaccount_subscription` is not automatically inferred by the provider.
|
```terraform
|
||||||
- The resource `btp_subaccount_subscription` depends on the `btp_subaccount_entitlement`.
|
resource "btp_subaccount_entitlement" "example" {
|
||||||
- You can derive the dependency by comparing the `app_name` with the `service_name` and the attributes `plan_name`.
|
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
|
## 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. 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
|
Operational:
|
||||||
- MUST NOT use `terraform import` as a regular workflow pattern
|
- Direct production applies without prior non‑prod 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.
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user