

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 了解 Terraform 提供商
<a name="providers"></a>

在 Terraform 中，*提供者是一个与云提供商*、第三方工具和其他 API 交互的插件。要将 Terraform 与 AWS Terraform 配合使用，您可以使用与资源交互的[AWS 提供程序](https://registry.terraform.io/providers/hashicorp/aws/latest/docs)。 AWS 

如果你从未使用过[AWS CloudFormation 注册表](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/registry.html)将第三方扩展整合到部署堆栈中，那么 Terraform [提供商](https://developer.hashicorp.com/terraform/language/providers)可能需要一些时间来适应。因为 CloudFormation 是原生的 AWS，所以默认情况下， AWS 资源提供者已经存在了。另一方面，Terraform 没有单一的默认提供者，因此无法假设给定资源的起源。这意味着需要在 Terraform 配置文件中声明的第一件事就是资源的去向以及它们将如何到达那里。

这种区别给 Terraform 增加了一层额外的复杂性，这是不存在的。 CloudFormation但是，这种复杂性增加了灵活性。您可以在单个 Terraform 模块中声明多个提供者，然后创建的底层资源可以作为同一部署层的一部分相互交互。

这在很多方面都很有用。提供商不一定必须是单独的云提供商。提供商可以代表任何云资源来源。例如，以亚马逊 Elastic Kubernetes Service（亚马逊 EKS）为例。在配置 Amazon EKS 集群时，您可能需要使用 Helm 图表来管理第三方扩展，并使用 Kubernetes 本身来管理容器资源。因为 AWS[Helm](https://registry.terraform.io/providers/hashicorp/helm/latest/docs) 和 [Kubernetes](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs) 都有自己的 Terraform 提供程序，所以你可以同时配置和集成这些资源，然后在它们之间传递值。

在以下 Terraform 的代码示例中， AWS 提供程序创建了一个 Amazon EKS 集群，然后将生成的 Kubernetes 配置信息传递给 Helm 和 Kubernetes 提供商。

```
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 4.33.0"
    }

    helm = {
      source  = "hashicorp/helm"
      version = "2.12.1"
    }

    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = "2.26.0"
    }
  }
  required_version = ">= 1.2.0"
}

provider "aws" {
  region = "us-west-2"
}

resource "aws_eks_cluster" "example_0" {
  name     = "example_0"
  role_arn = aws_iam_role.cluster_role.arn
  vpc_config {
    endpoint_private_access = true
    endpoint_public_access  = true
    subnet_ids              = var.subnet_ids
  }
}

locals {
  host        = aws_eks_cluster.example_0.endpoint
  certificate = base64decode(aws_eks_cluster.example_0.certificate_authority.data)
}

provider "helm" {
  kubernetes {
    host                   = local.host
    cluster_ca_certificate = local.certificate
    # exec allows for an authentication command to be run to obtain user
    # credentials rather than having them stored directly in the file
    exec {
      api_version = "client.authentication.k8s.io/v1beta1"
      args        = ["eks", "get-token", "--cluster-name", aws_eks_cluster.example_0.name]
      command     = "aws"
    }
  }
}

provider "kubernetes" {
  host                   = local.host
  cluster_ca_certificate = local.certificate
  exec {
    api_version = "client.authentication.k8s.io/v1beta1"
    args        = ["eks", "get-token", "--cluster-name", aws_eks_cluster.example_0.name]
    command     = "aws"
  }
}
```

就这两个 IaC 工具而言，提供商需要权衡取舍。Terraform 完全依赖位于外部的提供程序包，这些软件包是推动其部署的引擎。 CloudFormation 内部支持所有主要 AWS 进程。使用 CloudFormation，只有当你想加入第三方扩展时，才需要担心第三方提供商。每种方法都有其优点和缺点。哪一个最适合你超出了本指南的范围，但是在评估这两种工具时，记住其中的区别很重要。

## 使用 Terraform 别名
<a name="using-terraform-aliases"></a>

在 Terraform 中，您可以将自定义配置传递给每个提供程序。那么，如果你想在同一个模块中使用多个提供商配置怎么办？ 在这种情况下，你必须使用[别名](https://developer.hashicorp.com/terraform/language/providers/configuration#alias-multiple-provider-configurations)。  别名可帮助您选择在每个资源或每个模块级别上使用哪个提供商。当您有同一个提供程序的多个实例时，您可以使用别名来定义非默认实例。例如，您的默认提供商实例可能是特定的 AWS 区域，但您可以使用别名来定义备用区域。

以下 Terraform 示例显示了如何使用别名在不同的存储桶中配置存储桶。 AWS 区域提供商的默认区域为`us-west-2`，但您可以使用 east 别名在中配置资源`us-east-2`。

```
provider "aws" {
  region = "us-west-2"
}

provider "aws" {
  alias  = "east"
  region = "us-east-2"
}

resource "aws_s3_bucket" "myWestS3Bucket" {
  bucket = "my-west-s3-bucket"
}

resource "aws_s3_bucket" "myEastS3Bucket" {
  provider = aws.east
  bucket   = "my-east-s3-bucket"
}
```

当你使用和`provider`元参数时，如前面的示例所示，你可以为特定资源指定不同的提供者配置。`alias`在单个堆栈 AWS 区域 中以多个方式配置资源仅仅是个开始。别名提供者在很多方面都非常方便。

例如，一次配置多个 Kubernetes 集群是很常见的。别名可以帮助您配置其他 Helm 和 Kubernetes 提供程序，这样您就可以针对不同的 Amazon EKS 资源以不同的方式使用这些第三方工具。以下 Terraform 代码示例说明了如何使用别名来执行此任务。

```
resource "aws_eks_cluster" "example_0" {
  name     = "example_0"
  role_arn = aws_iam_role.cluster_role.arn
  vpc_config {
    endpoint_private_access = true
    endpoint_public_access  = true
    subnet_ids              = var.subnet_ids[0]
  }
}

resource "aws_eks_cluster" "example_1" {
  name     = "example_1"
  role_arn = aws_iam_role.cluster_role.arn
  vpc_config {
    endpoint_private_access = true
    endpoint_public_access  = true
    subnet_ids              = var.subnet_ids[1]
  }
}

locals {
  host         = aws_eks_cluster.example_0.endpoint
  certificate  = base64decode(aws_eks_cluster.example_0.certificate_authority.data)
  host1        = aws_eks_cluster.example_1.endpoint
  certificate1 = base64decode(aws_eks_cluster.example_1.certificate_authority.data)
}

provider "helm" {
  kubernetes {
    host                   = local.host
    cluster_ca_certificate = local.certificate
    exec {
      api_version = "client.authentication.k8s.io/v1beta1"
      args        = ["eks", "get-token", "--cluster-name", aws_eks_cluster.example_0.name]
      command     = "aws"
    }
  }
}

provider "helm" {
  alias = "helm1"
  kubernetes {
    host                   = local.host1
    cluster_ca_certificate = local.certificate1
    exec {
      api_version = "client.authentication.k8s.io/v1beta1"
      args        = ["eks", "get-token", "--cluster-name", aws_eks_cluster.example_1.name]
      command     = "aws"
    }
  }
}

provider "kubernetes" {
  host                   = local.host
  cluster_ca_certificate = local.certificate
  exec {
    api_version = "client.authentication.k8s.io/v1beta1"
    args        = ["eks", "get-token", "--cluster-name", aws_eks_cluster.example_0.name]
    command     = "aws"
  }
}

provider "kubernetes" {
  alias                  = "kubernetes1"
  host                   = local.host1
  cluster_ca_certificate = local.certificate1
  exec {
    api_version = "client.authentication.k8s.io/v1beta1"
    args        = ["eks", "get-token", "--cluster-name", aws_eks_cluster.example_1.name]
    command     = "aws"
  }
}
```