Terraform模块概述、使用及创建
原文地址:
- https://learn.hashicorp.com/terraform/modules/modules-overview
- https://learn.hashicorp.com/terraform/modules/using-modules
- https://learn.hashicorp.com/terraform/modules/creating-modules
Terraform模块概述
当你使用Terraform管理基础设施时,你将创建越来越复杂的配置。单一Terraform配置文件或目录的复杂性没有内在限制,因此可以继续在单一目录中写入和更新配置文件。但是,如果这样做,您可能会遇到一个或多个问题:
- 理解和导航配置文件将变得越来越困难。
- 更新配置将变得更加危险,因为对一个部分的更新可能会对配置的其他部分造成意外的后果。
- 类似的配置块会有越来越多的重复,例如在配置单独的dev/staging/production环境时,这将在更新配置的这些部分时造成越来越大的负担。
- 你可能希望在项目和团队之间共享部分配置,并且会很快发现,在项目之间剪切和粘贴配置块很容易出错,而且很难维护。
模块的用途是什么?
以下是模块帮助解决上述问题的一些方法:
组织配置
通过将配置的相关部分放在一起,模块使导航、理解和更新配置更加容易。即使是中等复杂的基础设施也可能需要数百或数千行配置才能实现。通过使用模块,您可以组织配置为逻辑组件。
封装配置
使用模块的另一个好处是将配置封装到不同的逻辑组件中。封装有助于防止意外的后果,例如对配置的一部分的更改意外地导致对其他基础结构的变更,并减少对两个不同资源使用相同名称等简单错误的可能性。
重用配置
从头开始编写所有配置可能会非常耗时且容易出错。使用模块可以通过重新使用你自己、团队其他成员或其他已发布模块供您使用的Terraform实践者编写的配置来节省时间并减少代价高昂的错误。你也可以与你的团队或公众分享你写的模块,让他们受益于你的努力工作。
提供一致性并确保最佳实践
模块也有助于在配置中提供一致性。一致性不仅使复杂的配置更易于理解,而且有助于确保在所有配置中应用最佳实践。例如,云提供商提供了许多配置对象存储服务的选项,比如Amazon S3或Google云存储。有许多引人注目的安全事件涉及不正确的安全对象存储,并且考虑到涉及的复杂配置选项的数量,很容易意外地错误配置这些服务。
使用模块可以帮助减少这些错误。例如,您可以创建一个模块来描述如何配置组织的所有公共网站存储,以及另一个用于记录应用程序的私有存储模块。此外,如果某类资源的配置需要更新,则使用模块可以在单个位置进行更新,并将其应用于使用该模块的所有情况。
什么是Terraform模块?
Terraform模块是单个目录中的一组Terraform配置文件。即使是包含一个目录和一个或多个.tf文件的简单配置也是一个模块。当您直接从这样的目录运行Terraform命令时,它被认为是根模块。因此从这个意义上说,每个Terraform配置都是模块的一部分。您可能有一组简单的Terraform配置文件,例如:
$ tree minimal-module/
.
├── LICENSE
├── README.md
├── main.tf
├── variables.tf
├── outputs.tf
在这种情况下,当您从最小模块目录中运行terraform命令时,该目录的内容被视为根模块。
调用模块
Terraform命令将只直接使用一个目录中的配置文件,该目录通常是当前工作目录。但是,您的配置可以使用模块块调用其他目录中的模块。当Terraform遇到模块块时,它加载并处理该模块的配置文件。
由另一个配置调用的模块有时称为该配置的“子模块”。
本地和远程模块
模块可以从本地文件系统加载,也可以从远程源加载。Terraform支持多种远程源,包括Terraform注册表、大多数版本控制系统、HTTP url和Terraform云或Terraform企业私有模块注册表。
模块最佳实践
在许多方面,Terraform模块类似于大多数编程语言中的库、包或模块的概念,并提供许多相同的好处。就像几乎所有有一定規模的计算机程序一样,真实世界的Terraform配置几乎总是使用模块来提供上述好处。
我们建议每个Terraform实践者遵循以下最佳实践来使用模块:
- 在编写配置时要考虑模块。即使是由一个人管理的稍微复杂的Terraform配置,您也会发现使用模块的好处超过了正确使用模块所花费的时间。
- 使用本地模块来组织和封装代码。即使您没有使用或发布远程模块,从一开始就按照模块来组织你的配置也会显著地减少维护和更新配置的负担,因为您的基础设施会变得越来越复杂。
- 使用公共Terraform注册表查找有用的模块。通过这种方式,你可以依靠其他人的工作来实现公共基础设施场景,从而更快速、更自信地实现你的配置。
- 与你的团队发布和共享模块。大多数基础设施是由一组人员管理的,模块是团队协作创建和维护基础设施的重要方式。如上所述,你可以公开或私下发布模块。
使用Terraform模块
虽然本指南中的概念适用于任何模块,但本指南使用的是Amazon Web Services (AWS)模块。如果你想跟随本指南,你需要遵循以下步骤:
- 创建一个AWS帐户,或者使用你已经拥有的一个。
- 配置AWS提供者文档中描述的身份验证方法之一。本指南中的示例假设你使用的是共享凭据文件方法和缺省AWS凭据文件和缺省配置文件。
- 确保Terraform已安装并可在你的命令行中使用;如果不是,从https://www.terraform.io/downloads.html下载。io和安装它。如果这是您第一次安装Terraform,请遵循我们深入的安装指南。
使用Terraform注册表
在新的浏览器选项卡或窗口中打开Terraform注册表VPC模块的页面。
你将看到关于模块的信息,以及到源存储库的链接。在页面的右侧,您将看到一个下拉界面来选择模块版本,以及使用模块提供基础设施的说明。
调用模块时,需要源参数。在本例中,Terraform将在Terraform注册表中搜索与给定字符串匹配的模块。您还可以为模块的源代码使用URL或本地文件路径。有关可能的模块源的列表,请参阅Terraform文档。
这里显示的另一个参数是版本。对于受支持的源代码,版本将允许您定义加载模块的一个或多个版本。在本指南中,您将为使用的模块指定准确的版本号。您可以在模块文档中了解更多指定版本的方法。
模块块的其他参数被视为模块的输入变量。
创建Terraform配置
在本指南中,你将使用模块在AWS环境中创建一个虚拟私有云(VPC)和两个EC2实例的示例。
你可以通过手动构建我们在指南中描述的目录结构和文件,或者通过克隆这个GitHub repo并check适当的tag来遵循这个指南。如果你想使用本指南中的repo而不是手动构建配置,那么现在就克隆它并check ec2-instances tag。
$ git clone git@github.com:hashicorp/learn-terraform-modules.git
$ cd learn-terraform-modules
$ git checkout ec2-instances
对于本指南,你的主要.tf文件看起来就像下面这样。
# Terraform configuration
provider "aws" {
region = "us-west-2"
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "2.21.0"
name = var.vpc_name
cidr = var.vpc_cidr
azs = var.vpc_azs
private_subnets = var.vpc_private_subnets
public_subnets = var.vpc_public_subnets
enable_nat_gateway = var.vpc_enable_nat_gateway
tags = var.vpc_tags
}
module "ec2_instances" {
source = "terraform-aws-modules/ec2-instance/aws"
version = "2.12.0"
name = "my-ec2-cluster"
instance_count = 2
ami = "ami-0c5204531f799e0c6"
instance_type = "t2.micro"
vpc_security_group_ids = [module.vpc.default_security_group_id]
subnet_id = module.vpc.public_subnets[0]
tags = {
Terraform = "true"
Environment = "dev"
}
}
这个配置包括三个模块:
provider “aws”
定义你的提供者。根据你选择的身份验证方法,你可能需要在提供程序块中包含其他参数。
module “vpc”
定义了一个虚拟私有云(vpc),它将为你的其余基础设施提供网络服务。
module “ec2_instances”
定义了VPC中的两个EC2实例。
设置模块输入变量的值
为了使用大多数模块,你需要将输入变量传递给模块配置。调用模块的配置负责设置其输入值,这些值作为参数在模块块中传递。除了source和version之外,模块块的大多数参数都将设置变量值。
在Terraform注册表AWS VPC模块的页面上,你将看到一个input选项卡,它描述了模块支持的所有输入变量。
一些输入变量是必需的,这意味着模块不提供默认值——为了正确运行Terraform,必须提供一个显式的值。
在模块“vpc”块中,检查您正在设置的输入变量。您可以在Terraform注册表中找到每个记录在案的输入变量。
- name 将是AWS中VPC的名称
- cidr 描述了在VPC中使用的CIDR块
- azs 是将用于VPC子网的可用区域
- private_subnets 是VPC中的子网,它将包含没有公共IP地址或路由的资源
- public_subnets 是包含具有公共IP地址和路由的资源的子网
- enable_nat_gateway 如果为true,模块将为你的私有子网提供NAT网关
- tags 为AWS中此配置提供的每个资源指定标记
注意:上面的配置使用了适用于本指南前面配置的us-west-2区域的可用性区域。如果使用不同的区域,则需要使用匹配的可用性区域。
您还可以通过与模块文档进行比较来查看模块“ec2_instances”块的参数。
在创建EC2实例时,需要为它们指定要使用的子网和安全组。本例将使用VPC模块提供的方法。
注意:本例使用us-west-2区域的AMI ID,你在提供程序块中配置了该ID。如果您使用的是一个不同的区域,那么你将需要一个适用于该区域的AMI ID。你可以在AWS控制台中看到您正在使用的区域的AMI id。
定义根输入变量
在模块中使用输入变量与在任何Terraform配置中使用变量非常类似。一种常见的模式是确定将来可能要更改哪些模块输入变量,并在配置的变量中创建匹配的variables.tf文件与合理的默认值。然后可以将这些变量作为参数传递给模块块。
并非所有模块输入变量都需要使用配置中的变量进行设置。例如,你可能希望这个VPC始终启用NAT网关,因为你提供的应用程序需要它。在这种情况下,使用变量来设置enable_nat_gateway将适得其反。
你需要在配置中定义这些变量来使用它们。
如果你已经克隆了本指南前面提到的git repo,那么你的变量。tf将如下图所示。
否则,复制并粘贴以下内容到variables.tf:
# Input variable definitions
variable "vpc_name" {
description = "Name of VPC"
type = string
default = "example-vpc"
}
variable "vpc_cidr" {
description = "CIDR block for VPC"
type = string
default = "10.0.0.0/16"
}
variable "vpc_azs" {
description = "Availability zones for VPC"
type = list
default = ["us-west-2a", "us-west-2b", "us-west-2c"]
}
variable "vpc_private_subnets" {
description = "Private subnets for VPC"
type = list(string)
default = ["10.0.1.0/24", "10.0.2.0/24"]
}
variable "vpc_public_subnets" {
description = "Public subnets for VPC"
type = list(string)
default = ["10.0.101.0/24", "10.0.102.0/24"]
}
variable "vpc_enable_nat_gateway" {
description = "Enable NAT gateway for VPC"
type = bool
default = true
}
variable "vpc_tags" {
description = "Tags to apply to resources created by VPC module"
type = map(string)
default = {
Terraform = "true"
Environment = "dev"
}
}
定义根输出值
模块还具有输出值,这些值在模块中使用output关键字定义。你可以通过引用模块module.MODULE_NAME.OUTPUT_NAME来访问它们。与输入变量一样,模块输出也在Terraform注册表的“输出”选项卡下列出。
模块输出通常要么传递到配置的其他部分,要么定义为根模块中的输出。在本指南中,你将看到这两种用法。
在您的配置目录中,outputs.tf需要包含:
output "vpc_public_subnets" {
description = "IDs of the VPC's public subnets"
value = module.vpc.public_subnets
}
output "ec2_instance_public_ips" {
description = "Public IP addresses of EC2 instances"
value = module.ec2_instances.public_ip
}
在本例中,vpc_public_subnets的值来自名为vpc的模块的public_subnets输出,ec2_instance_public_ips定义为module.ec2_ins.public_ip。
提供基础设施
通过运行terraform init来初始化你的Terraform配置。
$ terraform init
Initializing modules...
Downloading terraform-aws-modules/ec2-instance/aws 2.12.0 for ec2_instances...
- ec2_instances in .terraform/modules/ec2_instances/terraform-aws-modules-terraform-aws-ec2-instance-ed6dcd9
Downloading terraform-aws-modules/vpc/aws 2.21.0 for vpc...
- vpc in .terraform/modules/vpc/terraform-aws-modules-terraform-aws-vpc-2417f60
Initializing the backend...
Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "aws" (hashicorp/aws) 2.44.0...
# ...
Terraform已经安装了provider程序和你的配置所引用的两个模块。
现在运行terraform apply来创建你的VPC和EC2实例:
$ terraform apply
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# module.ec2_instances.aws_instance.this[0] will be created
+ resource "aws_instance" "this" {
+ ami = "ami-0c5204531f799e0c6"
+ arn = (known after apply)
+ associate_public_ip_address = (known after apply)
+ availability_zone = (known after apply)
# ...
Plan: 22 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value:
你将注意到,将创建比VPC和EC2实例多得多的资源。我们使用的模块定义了这些资源是什么。
输入yes应用更改并继续,你应该看到你的配置输出实例的IP地址:
# ...
Outputs:
ec2_instance_public_ips = [
"34.220.43.248",
"34.208.3.72",
]
vpc_public_subnets = [
"subnet-0ecd7a5db2a78879d",
"subnet-005a1cfe9fbbf596c",
]
了解模块如何工作
首次使用新模块时,必须运行terraform init或terraform get来安装模块。在运行这两个命令时,Terraform将在你的配置工作目录中的.terraform/modules目录中安装任何新的模块。对于本地模块,Terraform将创建软链接到模块目录。因此,对本地模块的任何变更都将立即生效,而不必重新运行terraform get。
在遵循本指南之后,你的.terraform/modules目录将如下所示:
.terraform/modules
├── ec2_instances
│ └── terraform-aws-modules-terraform-aws-ec2-instance-ed6dcd9
├── modules.json
└── vpc
└── terraform-aws-modules-terraform-aws-vpc-2417f60
清理你的基础设施
现在你已经了解了如何使用Terraform注册表中的模块,如何使用输入变量配置这些模块,以及如何从这些模块获取输出值。
在进入下一个指南之前,运行terraform destroy命令销毁你创建的基础设施:
$ terraform destroy
module.vpc.aws_eip.nat[1]: Refreshing state... [id=eipalloc-019208f8c88f9c7d6]
module.vpc.aws_eip.nat[0]: Refreshing state... [id=eipalloc-0046def26f17a2eb2]
module.vpc.aws_vpc.this[0]: Refreshing state... [id=vpc-0d6e08bf4f27de4ab]
# ...
Plan: 0 to add, 0 to change, 22 to destroy.
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
module.vpc.aws_route_table_association.private[1]: Destroying... [id=rtbassoc-0cc54bc0390c1af5e]
module.vpc.aws_route_table_association.private[0]: Destroying... [id=rtbassoc-0d435e5616f58a9fc]
# ...
module.vpc.aws_internet_gateway.this[0]: Destruction complete after 11s
module.vpc.aws_vpc.this[0]: Destroying... [id=vpc-0d6e08bf4f27de4ab]
module.vpc.aws_vpc.this[0]: Destruction complete after 0s
Destroy complete! Resources: 22 destroyed.
输入yes,Terraform将销毁您创建的基础设施。
注意:如果没有破坏您在本指南中创建的基础设施,可能会导致AWS收费。
创建一个Terraform模块
模块结构
Terraform将模块块的源参数中引用的任何本地目录视为模块。一个新模块的典型文件结构是:
$ tree minimal-module/
.
├── LICENSE
├── README.md
├── main.tf
├── variables.tf
├── outputs.tf
这些文件都不是必需的,当它使用你的模块时,对Terraform也没有任何特殊意义。您可以使用单个的.tf文件创建模块,或者使用你喜欢的任何其他文件结构。
每个文件都有一个目的:
-
LICENSE 将包含你的模块发布所依据的许可证。当你共享你的模块时,许可文件将让使用它的人知道提供它的条件。Terraform本身不使用此文件。
-
README.md 将包含文档描述如何使用你的模块,使用markdown格式。Terraform不使用此文件,但是Terraform Registry和GitHub等服务将向访问你的模块的Terraform Registry或GitHub页面的人显示此文件的内容。
-
main.tf 将包含模块的主要配置集。你还可以创建其他配置文件并组织它们,但是这对你的项目是有意义的。
-
variables.tf 将包含模块的变量定义。当你的模块被其他人使用时,这些变量将被配置为模块块中的参数。由于所有的Terraform值都必须定义,所以任何没有默认值的变量都将成为必需的参数。具有默认值的变量也可以作为模块参数提供,覆盖默认值。
-
outputs.tf 将包含模块的输出定义。模块输出可用于使用模块的配置,因此它们通常用于将模块定义的基础结构部分的信息传递给配置的其他部分。
还有一些其他文件需要注意,并确保你不把它们作为模块的一部分:
- terraform.tfstate 这些文件包含你的Terraform状态,并且是Terraform跟踪你的配置和它提供的基础设施之间的关系的方式。
- .terraform 此目录包含用于提供基础设施的模块和插件。在提供基础设施时,这些文件是特定于Terraform的特定实例的,而不是.tf文件中定义的基础设施的配置。
- *\*.tfvars** 由于模块输入变量是通过配置模块块的参数设置的,所以不需要分发任何.tfvars文件与你的模块,除非您也使用它作为一个独立的Terraform配置。
如果你正在跟踪版本控制系统(如git)中模块的变更,则需要将版本控制系统配置为忽略这些文件。例如,查看来自GitHub的.gitignore文件。
警告:上面提到的文件通常包含密码或访问密钥等秘密信息,如果这些文件提交到GitHub等公共版本控制系统,这些信息就会公开。
创建一个模块
本指南将使用模块指南中创建的配置作为起点。你可以继续在本地目录中进行配置,或者克隆这个GitHub Repo,并在终端中切换到该目录。如果你确实克隆了GitHub Repo,则需要从website-complete-example tag开始。例如:
$ git clone git@github.com:hashicorp/learn-terraform-modules.git
$ cd learn-terraform-modules
$ git checkout website-complete-example
如果你已经克隆了GitHub库,你还需要确保Terraform已经下载了所有必需的providers程序和模块,方法是初始化它:
$ terraform init
Initializing modules...
Downloading terraform-aws-modules/ec2-instance/aws 2.12.0 for ec2_instances...
- ec2_instances in .terraform/modules/ec2_instances/terraform-aws-modules-terraform-aws-ec2-instance-ed6dcd9
Downloading terraform-aws-modules/vpc/aws 2.21.0 for vpc...
- vpc in .terraform/modules/vpc/terraform-aws-modules-terraform-aws-vpc-2417f60
- website_s3_bucket in modules/aws-s3-static-website-bucket
Initializing the backend...
Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "aws" (hashicorp/aws) 2.44.0...
# ...
在本指南中,你将在现有配置中创建一个使用来自AWS provider的s3 bucket资源的本地子模块。
如果没有克隆示例存储库,则需要为模块创建目录。在现有的配置目录中,创建一个名为modules的目录,其中包含一个名为aws-s3-static-website-bucket的目录。例如,在Linux或Mac系统上运行:
$ mkdir -p modules/aws-s3-static-website-bucket
创建这些目录后,配置的目录结构如下:
$ tree
.
├── LICENSE
├── README.md
├── main.tf
├── modules
│ └── aws-s3-static-website-bucket
├── outputs.tf
├── terraform.tfstate
├── terraform.tfstate.backup
└── variables.tf
2 directories, 8 files
如果你已经克隆了GitHub Repo,那么在运行terraform apply命令之前,tfstate文件不会出现。
使用S3托管静态网站是一个相当常见的用例。虽然通过这种方式提供bucket并不难找到正确的配置,但是将这种配置封装到一个模块中可以为你的用户提供一种快速、简单的方法来创建bucket,他们可以使用它来托管遵循最佳实践的静态网站。使用模块的另一个好处是,模块名可以准确地描述用它创建的bucket的用途。在本例中,aws-s3-static-website-bucket模块创建了托管静态网站的s3 bucket。
创建一个README.md和LICENSE
如果你已经克隆了GitHub Repo,它将包括README.md和LICENSE文件。这些文件根本不会被Terraform使用。它们包含在这个示例中,以演示最佳实践。如果需要,可以按如下方式创建它们。
在aws-s3-static-website-bucket目录中,创建一个名为README.md的文件具有以下内容。
# AWS S3 static website bucket
This module provisions AWS S3 buckets configured for static website hosting.
为模块选择正确的许可超出了本指南的范围。本指南将使用Apache 2.0开放源码许可。
使用以下内容创建另一个名为LICENSE的文件。
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
这些文件都不是Terraform所需要或使用的。对于将来可能与他人共享的模块来说,拥有它们是最佳实践。
添加模块配置
你将在aws-s3-static-website-bucket目录中使用三个Terraform配置文件:main.tf、 variables.tf、outputs.tf
如果checkout git repo,那么这些文件就已经存在了。否则,你现在就可以创建这些空文件。这样做之后,你的模块目录结构将如下所示:
$ tree modules/
modules/
└── aws-s3-static-website-bucket
├── LICENSE
├── README.md
├── main.tf
├── outputs.tf
└── variables.tf
在modules/aws-s3-static-website-bucket目录中的main.tf中添加S3 bucket资源。
resource "aws_s3_bucket" "s3_bucket" {
bucket = var.bucket_name
acl = "public-read"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::${var.bucket_name}/*"
]
}
]
}
EOF
website {
index_document = "index.html"
error_document = "error.html"
}
tags = var.tags
}
此配置创建一个公共S3 bucket,用于为网站提供索引页和错误页。
你将注意到,在此配置中没有提供程序块。当Terraform处理模块块时,它将从封闭的配置中继承提供程序。因此,我们建议你不要在模块中包含提供程序块。
就像配置的根模块一样,模块将定义和使用变量。
在modules/aws-s3-static-website-bucket目录中的variables.tf中定义以下变量:
variable "bucket_name" {
description = "Name of the s3 bucket. Must be unique."
type = string
}
variable "tags" {
description = "Tags to set on the bucket."
type = map(string)
default = {}
}
模块内的变量的工作方式与根模块几乎完全相同。当你在根配置上运行terraform命令时,有多种方法可以设置变量值,例如在命令行上传递变量值,或者使用.tfvars文件。在使用模块时,通过向配置中的模块传递参数来设置变量。在从根模块的main.tf调用此模块时,将设置其中一些变量。
模块中定义的变量没有给出默认值,因此必须在使用模块时设置。
在创建模块时,请考虑将哪些资源参数作为输入变量公开给模块最终用户。例如,你可能决定将此模块的索引和错误文档作为变量公开给最终用户,但不定义变量来设置ACL,因为要承载一个网站,你的bucket需要将ACL设置为“public-read”。
你还应该考虑将哪些值添加为输出,因为输出是用户获取模块配置的资源信息的唯一受支持的方式。
在modules/aws-s3-static-website-bucket目录下的的outputs.tf文件中向模块添加:
# Output variable definitions
output "arn" {
description = "ARN of the bucket"
value = aws_s3_bucket.s3_bucket.arn
}
output "name" {
description = "Name (id) of the bucket"
value = aws_s3_bucket.s3_bucket.id
}
output "website_endpoint" {
description = "Domain name of the bucket"
value = aws_s3_bucket.s3_bucket.website_endpoint
}
与变量一样,模块中的输出与根模块中的输出功能是相同的,但访问方式不同。模块的输出可以作为模块对象上的只读属性访问,该属性在调用模块的配置中可用。你可以在表达式中将这些输出作为模块引用”module.<MODULE NAME>.<OUTPUT NAME>“。
现在你已经创建了你的模块,回到根模块中的main.tf,并添加引用到新的模块:
module "website_s3_bucket" {
source = "./modules/aws-s3-static-website-bucket"
bucket_name = "<UNIQUE BUCKET NAME>"
tags = {
Terraform = "true"
Environment = "dev"
}
}
AWS S3 bucket必须是全局唯一的。因此,你需要将“<UNIQUE BUCKET NAME>”替换为S3 bucket的唯一有效名称。使用你的名字和日期通常是生成一个唯一bucket名称的好方法。例如:
bucket_name = "robin-example-2020-01-15"
在本例中,bucket_name和tags参数将传递给模块,并为modules/aws-s3-static-website-bucket/variables.tf中找到的匹配变量提供值。
定义输出
在前面,你向aws-s3-static-website-bucket模块添加了几个输出,使这些值可用于根模块配置。
通过向输出添加以下内容,将这些值作为输出添加到根模块。根模块目录下的tf文件(不是modules/aws-s3-static-website-bucket中的文件)。
output "website_bucket_arn" {
description = "ARN of the bucket"
value = module.website_s3_bucket.arn
}
output "website_bucket_name" {
description = "Name (id) of the bucket"
value = module.website_s3_bucket.name
}
output "website_endpoint" {
description = "Domain name of the bucket"
value = module.website_s3_bucket.website_endpoint
}
安装本地模块
每当你向配置中添加一个新模块时,Terraform必须安装该模块才能使用它。terraform get和terraform init命令都将安装和更新模块。terraform init命令还将初始化后端并安装插件。
现在通过运行terraform get来安装模块。
$ terraform get
注意:安装远程模块时,Terraform会将其下载到配置根目录下的.terraform目录中。安装本地模块时,Terraform将直接引用源目录。因此,Terraform将自动发现本地模块的变更,而不必重新运行terraform init或terraform get。
现在你的新模块已经安装并配置好了,运行terraform apply来准备你的bucket。
$ terraform apply
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# ...
# module.website_s3_bucket.aws_s3_bucket.s3_bucket will be created
+ resource "aws_s3_bucket" "s3_bucket" {
+ acceleration_status = (known after apply)
# ...
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value:
输入yes后你的bucket和其他资源将被分配。
注意:这也将提供前面指南中的EC2实例。当你用本指南删除那些EC2实例时,不要忘记运行terraform destroy,否则你可能要为此付出代价。
运行terraform apply后,将创建bucket。
将文件上传到bucket中
现在你已经配置并使用自己的模块创建了一个静态网站。你可能想要访问这个静态网站。现在你的bucket里没有任何东西,所以如果你访问bucket的网站,就没有东西可以看了。为了查看任何内容,你需要将对象上传到bucket。你可以使用AWS控制台或AWS命令行工具将在GitHub Repo中找到的www目录的内容上传到bucket中,例如:
$ aws s3 cp modules/aws-s3-static-website-bucket/www/ s3://$(terraform output website_bucket_name)/ --recursive
upload: modules/aws-s3-static-website-bucket/www/error.html to s3://robin-test-2020-01-15/error.html
upload: modules/aws-s3-static-website-bucket/www/index.html to s3://robin-test-2020-01-15 /index.html
当你最后一次运行terraform apply应用程序时,或者当你运行terraform output时,将显示网站地址。
在web浏览器中访问网站地址,你将看到网站内容。https://<YOUR BUCKET NAME>.s3-us-west-2.amazonaws.com/index.html
清理网站和基础设施
如果你已经上传了文件到你的bucket中,你需要在bucket被销毁之前删除它们。例如,你可以运行:
$ aws s3 rm s3://$(terraform output website_bucket_name)/ --recursive
delete: s3://robin-test-2020-01-15/index.html
delete: s3://robin-test-2020-01-15/error.html
一旦bucket是空的,摧毁您的Terraform资源:
$ terraform destroy
module.vpc.aws_vpc.this[0]: Refreshing state... [id=vpc-00ff17f9d4801bdef]
module.vpc.aws_eip.nat[0]: Refreshing state... [id=eipalloc-0364049fdf8f8d33d]
# ...
Plan: 0 to add, 0 to change, 23 to destroy.
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
module.vpc.aws_route_table_association.private[1]: Destroying... [id=rtbassoc-01a79185940c0247b]
module.vpc.aws_route.public_internet_gateway[0]: Destroying... [id=r-rtb-0d22a7780ac9509df1080289494]
# ...
Destroy complete! Resources: 23 destroyed.
输入yes后Terraform将销毁通过遵循本指南创建的所有资源。