# Канвенцыя наймення

## Агульныя пагадненні

{% hint style="info" %}
Не павінна быць прычын не прытрымлівацца хаця б гэтых канвенцый :)
{% endhint %}

{% hint style="info" %}
Звярніце ўвагу, што рэальныя воблачныя рэсурсы часта маюць абмежаванні на дазволеныя назвы. Некаторыя рэсурсы, напрыклад не могуць утрымліваць працяжнікі, а некаторыя павінны быць напісаны ў camel-case. Пагадненні ў гэтай кнізе адносяцца да саміх назваў Terraform.
{% endhint %}

1. Выкарыстоўвайце `_` (падкрэсліванне) замест `-` (дэфіса) паўсюль (у назвах рэсурсаў, назвах крыніц даных, назвах пераменных, outputs і г.д.).
2. Аддавайце перавагу малым літарам і лічбам (хоць UTF-8 і падтрымліваецца).

## Аргументы рэсурсаў і крыніц дадзеных

1. Не паўтарайце тып рэсурсу ў назве рэсурсу (ні часткова, ні цалкам):

{% hint style="success" %}

```
`resource "aws_route_table" "public" {}`
```

{% endhint %}

{% hint style="danger" %}

```
`resource "aws_route_table" "public_route_table" {}`
```

{% endhint %}

{% hint style="danger" %}

```
`resource "aws_route_table" "public_aws_route_table" {}`
```

{% endhint %}

2. Назва рэсурсу павінна быць `this` калі няма больш дакладнай і агульнай назвы, або калі модуль рэсурсаў стварае адзіны рэсурс гэтага тыпу (напрыклад, у [AWS VPC модулі](https://github.com/terraform-aws-modules/terraform-aws-vpc) ёсць адзін рэсурс тыпу `aws_nat_gateway` and  і некалькі рэсурсаў тыпу `aws_route_table`, таму `aws_nat_gateway` павінен называцца `this` а `aws_route_table` павінны мець больш дакладныя назвы — напрыклад `private`, `public`, `database`).
3. Заўсёды выкарыстоўвайце назоўнікі ў адзіночным ліку для назваў.
4. Выкарыстоўвайце `-` у значэннях аргументаў і ў месцах, дзе значэнне будзе бачна чалавеку (напрыклад, у DNS імёнах альбо RDS экзэмпляры).
5. Уключайце колькасць аргументаў `count` / `for_each` у блоку рэсурса або крыніцы даных у якасці першага аргумента ўверсе і аддзяляйце ад яго адзіным радком.
6. Уключайце аргумент `tags` калі яны падтрымліваюцца рэсурсам, у якасці апошняга рэальнага аргумента, пасля `depends_on` і `lifecycle`, калі гэта неабходна. Усе яны павінны быць аддзеленыя адзіным пустым радком.
7. Пры выкарыстанні ўмоў у аргуменце `count` / `for_each` аддавайце перавагу лагічным значэнням замест выкарыстання `length` альбо іншых выразаў.

## Прыклады кода `resource`

### Выкарыстанне `count` / `for_each`

{% hint style="success" %}
{% code title="main.tf" %}

```hcl
resource "aws_route_table" "public" {
  count = 2

  vpc_id = "vpc-12345678"
  # ... астатнія аргументы прапушчаны
}

resource "aws_route_table" "private" {
  for_each = toset(["one", "two"])

  vpc_id = "vpc-12345678"
  # ... астатнія аргументы прапушчаны
}
```

{% endcode %}
{% endhint %}

{% hint style="danger" %}
{% code title="main.tf" %}

```hcl
resource "aws_route_table" "public" {
  vpc_id = "vpc-12345678"
  count  = 2

  # ... астатнія аргументы прапушчаны
}
```

{% endcode %}
{% endhint %}

### Размяшчэнне `tags`

{% hint style="success" %}
{% code title="main.tf" %}

```hcl
resource "aws_nat_gateway" "this" {
  count = 2

  allocation_id = "..."
  subnet_id     = "..."

  tags = {
    Name = "..."
  }

  depends_on = [aws_internet_gateway.this]

  lifecycle {
    create_before_destroy = true
  }
}
```

{% endcode %}
{% endhint %}

{% hint style="danger" %}
{% code title="main.tf" %}

```hcl
resource "aws_nat_gateway" "this" {
  count = 2

  tags = "..."

  depends_on = [aws_internet_gateway.this]

  lifecycle {
    create_before_destroy = true
  }

  allocation_id = "..."
  subnet_id     = "..."
}
```

{% endcode %}
{% endhint %}

### Умовы ў `count`

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
resource "aws_nat_gateway" "that" {    # найлепш
  count = var.create_public_subnets ? 1 : 0
}

resource "aws_nat_gateway" "this" {    # добра
  count = length(var.public_subnets) > 0 ? 1 : 0
}
```

{% endcode %}
{% endhint %}

## Пераменныя

1. Не вынаходзьце ровар у модулях рэсурсаў: выкарыстоўвайце `name`, `description`, і `default` для зменных, як гэта вызначана ў раздзеле «Спасылка на аргументы» для рэсурса, з якім вы працуеце.
2. адтрымка валідацыі для зменных даволі абмежаваная (напрыклад немагчыма атрымаць доступ да іншых пераменных або выконваць пошук, калі выкарыстоўваецца версія да Terraform `1.9`). Плануйце адпаведна, бо ў многіх выпадках гэтая функцыя бескарысная.
3. Выкарыстоўвайце форму множага ліку ў назве пераменнай калі тып — `list(...)` або `map(...)`.
4. Размяшчайце ключы ў блоку зменных у такім парадку: `description` , `type`, `default`, `validation`.
5. Заўсёды дадавайце `description` да ўсіх пераменных, нават калі вам здаецца, што яно відавочнае (яно спатрэбіцца ў будучыні). Выкарыстоўвайце тую ж тэрміналогію, што і ў зыходнай дакументацыі, калі гэта магчыма.
6. Аддавайце перавагу выкарыстанню простых тыпаў (`number`, `string`, `list(...)`, `map(...)`, `any`) перад спецыфічнымі, такімі як `object()` калі толькі вам не патрэбны строгія абмежаванні для кожнага ключа.
7. Выкарыстоўвайце такія спецыфічныя тыпы, як `map(map(string))` калі ўсе элементы мапы маюць аднолькавы тып (напрыклад `string`) або могуць быць пераўтвораны ў яго (напрыклад `number` можна пераўтварыць у `string`).
8. Выкарыстоўвайце тып `any` каб адключыць праверку тыпаў пачынаючы з пэўнай глыбіні або калі неабходна падтрымліваць некалькі тыпаў.
9. Значэнне `{}` часам з'яўляецца мапай, а часам — аб'ектам. Выкарыстоўвайце `tomap(...)` каб стварыць мапу, бо няма спосабу стварыць аб'ект.
10. Пазбягайце падвойных адмоў: выкарыстоўвайце станоўчыя назвы зменных, каб пазбегнуць блытаніны. Напрыклад, выкарыстоўвайце `encryption_enabled` замест `encryption_disabled`.
11. Для пераменных, якія ніколі не павінны быць `null`, усталюйце `nullable = false`. Гэта гарантуе, што пры перадачы `null` будзе выкарыстоўвацца значэнне па змаўчанні замест `null`. Калі `null` з'яўляецца прымальным значэннем, вы можаце прапусціць nullable або ўсталяваць яго значэнне на `true`.

## Outputs

Рабіце outputs паслядоўнымі і зразумелымі па-за межамі яго сферы прымянення (калі карыстальнік працуе з модулем, павінна быць відавочна, які тып і атрыбут мае вяртаемыя ім значэнне).

1. Назва output павінна апісваць уласцівасць, якую яна змяшчае, і быць менш свабоднай формы, чым звычайна хацелася б.
2. Добрая структура для назвы output выглядае так `{name}_{type}_{attribute}` , дзе:
   1. `{name}` ёсць назвай рэсурсу або крыніцы дадзеных
      * `{name}` для `data "aws_subnet" "private"` ёсць `private`
      * `{name}` для `resource "aws_vpc_endpoint_policy" "test"` ёсць `test`
   2. `{type}` з'яўляецца тыпам рэсурсу або крыніцы дадзеных без прэфікса пастаўшчыка
      * `{type}` для `data "aws_subnet" "private"` ёсць `subnet`
      * `{type}` для `resource "aws_vpc_endpoint_policy" "test"` ёсць `vpc_endpoint_policy`
   3. `{attribute}` з'яўляецца атрыбутам, які вяртаецца output
   4. [Глядзі прыклад](#code-examples-of-output).
3. Калі output вяртае значэнне з інтэрпаляцыйнымі функцыямі і некалькімі рэсурсамі, `{name}` і `{type}` павінны быць максімальна агульнымі (прэфікс `this` варта выключыць). [Глядзі прыклад](#code-examples-of-output).
4. Калі вернутае значэнне ёсць спісам, яго назва павінна быць у множным ліку. [Глядзі прыклад](#use-plural-name-if-the-returning-value-is-a-list).
5. Заўсёды дадавайце `description` для ўсіх outputs нават калі вам здаецца, што яно відавочнае.
6. Пазбягайце вызначаць `sensitive` аргумент, калі вы не кантралюеце цалкам выкарыстанне гэтага вываду ва ўсіх месцах ва ўсіх модулях.
7. Аддавайце перавагу `try()` (даступна з Terraform 0.13) перад `element(concat(...))` (стары падыход для версій да 0.13)

### Прыклады кода `output`

Вяртайце максімум адзін ID групы бяспекі:

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
output "security_group_id" {
  description = "The ID of the security group"
  value       = try(aws_security_group.this[0].id, aws_security_group.name_prefix[0].id, "")
}
```

{% endcode %}
{% endhint %}

Калі ёсць некалькі рэсурсаў аднаго тыпу, `this` трэба выключыць з назвы output:

{% hint style="danger" %}
{% code title="outputs.tf" %}

```hcl
output "this_security_group_id" {
  description = "The ID of the security group"
  value       = element(concat(coalescelist(aws_security_group.this.*.id, aws_security_group.web.*.id), [""]), 0)
}
```

{% endcode %}
{% endhint %}

### Выкарыстоўвайце імя ў множным ліку, калі вяртанае значэнне ёсць спісам

{% hint style="success" %}
{% code title="outputs.tf" %}

```hcl
output "rds_cluster_instance_endpoints" {
  description = "A list of all cluster instance endpoints"
  value       = aws_rds_cluster_instance.this.*.endpoint
}
```

{% endcode %}
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.terraform-best-practices.com/be/naming.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
