# 命名约定

## 一般约定

{% hint style="info" %}
至少应该没有理由不遵循这些约定 :)
{% endhint %}

{% hint style="info" %}
请注意，实际的云资源通常对允许的名称有限制。 例如，有些资源不能包含破折号，有些资源必须采用驼峰式大小写。 本书中的约定指的是 Terraform 名称本身。
{% endhint %}

1. 在任何地方（资源名称、数据源名称、变量名称、输出等）使用 `_` (下划线) 而不是 `-` (破折号)。&#x20;
2. 非常适合使用小写字母和数字（即使支持 UTF-8）。

## 资源和数据源参数

1. 不要在资源名称中重复资源类型（既不要部分，更不要完全）：

   <div data-gb-custom-block data-tag="hint" data-style="success" class="hint hint-success"><p><code>resource "aws_route_table" "public" {}</code></p></div>

   <div data-gb-custom-block data-tag="hint" data-style="danger" class="hint hint-danger"><p><code>resource "aws_route_table" "public_route_table" {}</code></p></div>

   <div data-gb-custom-block data-tag="hint" data-style="danger" class="hint hint-danger"><p><code>resource "aws_route_table" "public_aws_route_table" {}</code></p></div>
2. 如果没有更多的描述性和通用名称可用，或者如果资源模块创建这种类型的单个资源，资源名称应该被命名为`this`（例如，在[AWS VPC module](https://github.com/terraform-aws-modules/terraform-aws-vpc) 中有一个类型为 `aws_nat_gateway`的资源和多个类型为`aws_route_table`的资源，所以 `aws_nat_gateway` 应该命名为`this`，并且`aws_route_table` 应该有更具描述性的名称——比如`private`, `public`, `database）`。
3. 始终使用单数名词作为名称。
4. 在参数值内部和值将显示给人类的地方，使用`-`（例如，在 RDS 实例的 DNS 名称内部）。
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"
  # ... remaining arguments omitted
}

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

  vpc_id = "vpc-12345678"
  # ... remaining arguments omitted
}
```

{% endcode %}
{% endhint %}

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

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

  # ... remaining arguments omitted
}
```

{% 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" {    # Best
  count = var.create_public_subnets ? 1 : 0
}

resource "aws_nat_gateway" "this" {    # Good
  count = length(var.public_subnets) > 0 ? 1 : 0
}
```

{% endcode %}
{% endhint %}

## Variables（变量）

1. 不要在资源模块中重新造轮子：使用你正在使用的资源的“参数参考”部分中定义的变量 `name，description`和 `default`。
2. 对变量验证的支持相当有限（例如，无法访问其他变量或进行查找）。 相应地计划，因为在许多情况下此功能是无用的。
3. 当type是`list(...)` 或 `map(...)`，在变量名中使用复数形式。&#x20;
4. 在变量块中排序键，比如：`description`，`type`，`default`，`validation`。&#x20;
5. 始终包括对所有变量的`description`，即使你认为它很明显（将来你会需要它）。
6. 优先使用简单类型（`number`，`string`，`list(...)`，`map(...)`， `any`) 而不是像`object()`这样的特定类型，除非您需要对每个键都有严格的约束。
7. 如果map的所有元素都具有相同的类型（例如`string`）或可以转换为它（例如`number`类型可以转换为`string`），则使用特定类型，如`map(map(string))`。
8. 使用类型`any`来禁用从某个深度开始或者在支持多个类型时的类型验证。
9. 值`{}`有时是一个map，有时是一个对象。使用`tomap(...)` 制作map，因为没有办法制作对象。

## Outputs（输出）

使输出在其范围之外保持一致且易于理解（当用户使用模块时，它返回的值的类型和属性应该很明显）。

1. 输出的名称应该描述它包含的属性，并且不像你通常想要的那样自由。
2. 输出名称的良好结构类似于`{name}_{type}_{attribute}`，其中：
   1. `{name}`是没有提供者前缀的资源或数据源名称。`aws_subnet` 的`{name}是subnet`，`aws_vpc` 的`{name}`是`vpc`
   2. `{type}` 是一种资源来源
   3. `{attribute}`是输出返回的属性
   4. &#x20;参见示例 [See examples](#code-examples-of-output).
3. 如果输出返回一个带有插值函数和多个资源的值，`{name}` 和 `{type}` 应该尽可能通用（`this` 作为前缀应该省略）， 参见示例 [See example](#code-examples-of-output)。&#x20;
4. 如果返回值是一个列表，它应该有一个复数名称。 参见示例 [See example](#use-plural-name-if-the-returning-value-is-a-list)。&#x20;
5. 始终包括所有输出的`description`，即使你认为它很明显。&#x20;
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` 在输出名称中应省略：

{% 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/zh/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.
