从零开始使用Terraform
首先
本次将会写一篇文章,详细介绍一个之前没有接触过Terraform的学生如何逐渐掌握它。我们会以简明易懂的解释方式来进行,欢迎大家一起来阅读。
投稿主的前提条件
-
- 大学院生
-
- 応用情報技術者取得
-
- アルバイトでゲーム会社のwebアプリエンジニアとして勤務(2~3年)
-
- aws経験はボチボチ
-
- 圧倒的Mac派
-
- 書ける言語
Python
Go
Java
Javascript
Vue.js
Google App Script
Terraform是什么?
Terraform(テラフォーム)是由HashiCorp公司开发的开源基础设施编码工具。通过这个工具,可以使用代码管理和构建云资源和数据中心资源。
特点
不依赖于云服务提供商
Terraform可以与众多云服务提供商如AWS、Azure、Google Cloud Platform等进行集成。
provider "aws" {
region = "us-west-2"
}
2. 宣言性的代码 de
Terraform通过声明性的代码来定义基础架构。只需写下想要实现的内容,资源将按照所写的方式构建。
resource "aws_instance" "example" {
ami = "ami-123456"
instance_type = "t2.micro"
}
这段代码是使用Terraform来在AWS上创建虚拟机的声明性定义。
具体而言,包括以下配置。
resource “aws_instance” “example”: AWS上にインスタンスを作成するリソースの定義で、このリソースには名前として”example”が付けられています。
ami = “ami-123456”: 使用するAmazon Machine Image(AMI)のIDを指定しています。このIDは、インスタンス作成時に使用するOSやソフトウェアがプリインストールされたイメージを表します。
instance_type = “t2.micro”: インスタンスのサイズを指定しています。この例では、”t2.micro”タイプのインスタンスが作成されます。
当在Terraform中执行这段代码时,将会使用指定的AMI和实例类型,在AWS上创建一个新的虚拟机。
详细的语法将在下一章介绍。
3. 通过代码实现版本控制
通过将基础设施进行代码化,可以实现版本管理,并轻松追踪变更记录。
基础设施的生命周期
Terraform的操作主要由以下几个步骤组成。
-
- 初始化:terraform init
-
- 创建计划:terraform plan
-
- 应用:terraform apply
- 销毁:terraform destroy
Terraform的基本语法
terraform代码块
terraform块用于Terraform配置。它包含与Terraform操作相关的配置,如后端设置和提供者版本约束。
供应商的版本限制
您可以使用required_providers块来指定提供者的版本。
terraform {
required_version = ">=1.3"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 2.0"
}
}
}
这个设置要求使用AWS提供程序的版本2.0以上,但不超过3.0。
后端设置
后端定义了保存Terraform状态的位置。以下是使用S3存储桶作为后端的示例。
terraform {
backend "s3" {
bucket = "my-tf-state-bucket"
key = "terraform.tfstate"
region = "us-west-2"
}
}
变量
变量被用于集中管理在代码中多次使用的值。通过使用变量,可以提高代码的可重用性和可维护性。
variable "region" {
description = "AWSのリージョン"
default = "us-west-2"
}
资源
资源块用于定义云基础设施中的物理组件。以下是创建AWS的S3存储桶的示例。
resource "aws_s3_bucket" "bucket" {
bucket = "my-tf-test-bucket"
acl = "private"
}
数据源
数据源块用于访问现有的云基础设施组件。
data "aws_ami" "example" {
most_recent = true
owners = ["self"]
}
施加力量或努力
出力模块用于输出有关由Terraform构建或更改的基础设施的信息。
output "bucket_name" {
value = aws_s3_bucket.bucket.id
}
标准模块结构
基本上,按照HashiCorp的Terraform标准模块的文件夹结构来组织是一个不错的选择。
可以参考以下内容。
以下是由最少的标准模块组成的。
$ tree minimal-module/
.
├── README.md
├── main.tf
├── variables.tf
├── outputs.tf
下面是一个包含所有选项元素的模块配置。
$ tree complete-module/
.
├── README.md
├── main.tf
├── variables.tf
├── outputs.tf
├── ...
├── modules/
│ ├── nestedA/
│ │ ├── README.md
│ │ ├── variables.tf
│ │ ├── main.tf
│ │ ├── outputs.tf
│ ├── nestedB/
│ ├── .../
├── examples/
│ ├── exampleA/
│ │ ├── main.tf
│ ├── exampleB/
│ ├── .../
设定教程 (Mac)
我将在以下说明在Mac上设置Terraform的步骤。
安装tfenv
tfenv是一种用于管理Terraform版本的工具,可以针对每个项目使用不同版本的Terraform。以下是在Mac上安装的步骤:
使用Homebrew进行安装
使用Homebrew安装tfenv。
$ brew install tfenv
确认安装
请执行以下命令,以确保tfenv已正确安装。
$ tfenv --version
安装和选择Terraform的版本。
你可以使用tfenv来安装特定版本的Terraform,并选择要使用的版本。
$ tfenv install 1.3.9
$ tfenv use 1.3.9
我已经将项目中使用的Terraform版本设置为0.12.0。
确认可使用的版本
$ tfenv list
* 1.3.9 (set by /Users/local/.tfenv/version)
1.3.8
您可以使用 `tfenv list-remote` 命令查看可安装的版本列表。
安装Terraform
使用Homebrew安装Terraform。
$ brew tap hashicorp/tap
$ brew install hashicorp/tap/terraform
确认安装情况
请运行下面的命令,以确认Terraform已正确安装。
$ terraform -version
这个命令会显示已安装的Terraform版本。
初始化 (Chū Qī Huà)
我们先进入项目目录,然后进行Terraform的初始化。
$ cd your_project_directory
$ terraform init
这个命令会下载项目所需的提供商并初始化Terraform。
尝试使用-基础篇
进行初始化
$ mkdir terraform_test
$ cd terraform_test
在执行terraform init之前,需要创建一个用于保存tfstate的S3存储桶才行。
我们将尝试创建一个名为terraform-paupau-tfstate的存储桶。


下一步,我们将把S3的信息添加到backend.tf文件中。
由于terraform的官方文档非常丰富,我们可以提取模板使用。
由于文档十分详尽,对于初学者来说真是太好了!
我将模板替换为适应我的环境,并创建了以下类似的东西。
provider "aws"{
region = "ap-northeast-1"
}
terraform {
required_version = "1.5.5"
backend "s3" {
bucket = "terraform-paupau-tfstate"
key = "terraform.tfstate"
region = "ap-northeast-1"
}
}
随后尝试使用terraform init进行初始化…
$ terraform init
Initializing the backend...
Terraform encountered problems during initialisation, including problems
with the configuration, described below.
The Terraform configuration must be valid before initialization so that
Terraform can determine which modules and providers need to be installed.
╷
│ Error: error configuring S3 Backend: error validating provider credentials: error calling sts:GetCallerIdentity: InvalidClientTokenId: The security token included in the request is invalid.
│ status code: 403, request id: xxxxxx
嗯,无法进行认证吗…?
是的,AWS CLI的设置还没有完成。
我们将进行config和credentials的设置。
$ cat ~/.aws/credentials
[default]
aws_access_key_id = hogehoge
aws_secret_access_key = hogehoge
$ cat ~/.aws/config
[default]
region = ap-northeast-1
output = json
这样完美了!
.terraform % terraform init -reconfigure
Initializing the backend...
Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v5.12.0...
- Installed hashicorp/aws v5.12.0 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
初始化成功!
创建VPC和子网
这也是从 Terraform 的文档中引用的。
VPC 的文档在这里。
子网的文档在这里。
我将尝试创建vpc.tf文件。
resource "aws_vpc" "main" {
cidr_block = "192.168.1.0/24"
tags = {
Name = "Main-vpc"
}
}
resource "aws_subnet" "main_1a" {
vpc_id = aws_vpc.main.id
cidr_block = "192.168.1.0/25"
availability_zone = "ap-northeast-1a"
}
接下来,我们将使用terraform validate来检查语法是否正确。
这将用于检查Terraform配置文件是否具有正确的语法和结构。
$ terraform validate
Success! The configuration is valid.
一切顺利。
接下来,我将使用terraform plan进行确认。
它会查看tfstate和当前代码之间的差异,并告诉我们状态。
(省略部分内容)
% terraform plan
・
・
・
Plan: 2 to add, 0 to change, 0 to destroy.
我已确认有两个计划正在制定中。
顺便提一下,使用terraform fmt能够让代码格式更加整齐。
终于,我将尝试执行terraform apply。
$ terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_subnet.main_1a will be created
+ resource "aws_subnet" "main_1a" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1a"
+ availability_zone_id = (known after apply)
+ cidr_block = "192.168.1.0/25"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_public_ip_on_launch = false
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ tags_all = (known after apply)
+ vpc_id = (known after apply)
}
# aws_vpc.main will be created
+ resource "aws_vpc" "main" {
+ arn = (known after apply)
+ cidr_block = "192.168.1.0/24"
+ default_network_acl_id = (known after apply)
+ default_route_table_id = (known after apply)
+ default_security_group_id = (known after apply)
+ dhcp_options_id = (known after apply)
+ enable_dns_hostnames = (known after apply)
+ enable_dns_support = true
+ enable_network_address_usage_metrics = (known after apply)
+ id = (known after apply)
+ instance_tenancy = "default"
+ ipv6_association_id = (known after apply)
+ ipv6_cidr_block = (known after apply)
+ ipv6_cidr_block_network_border_group = (known after apply)
+ main_route_table_id = (known after apply)
+ owner_id = (known after apply)
+ tags = {
+ "Name" = "Main-vpc"
}
+ tags_all = {
+ "Name" = "Main-vpc"
}
}
Plan: 2 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: yes
aws_vpc.main: Creating...
╷
│ Error: creating EC2 VPC: operation error EC2: CreateVpc, https response error StatusCode: 403, RequestID: 71b56b4f-b2c5-4b51-9138-a859a5707eb3, api error UnauthorizedOperation: You are not authorized to perform this operation. Encoded authorization failure message: HFvjs_g1WA1DvLX7IkOqRVkEP6BRUVXdIgl2l2Z6TBm69UahT9QAnCvHbZSDVviMDyucCaXAsOpuz2vls0JTMEiuLMM6CGhluUggZp8VdlaMmmHfDfH
嗯,看起来不太顺利。详细内容已经被编码了呢…
让我们使用aws sts decode-authorization-message —encoded-message “encode_message”来进行解码试试看。
为了解码,需要将DecodeAuthorizationMessage添加到IAM策略中。
前往IAM > 策略 > 新建策略 > 进入JSON。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:DecodeAuthorizationMessage",
"Resource": "*"
}
]
}

然后将我刚制定的政策附加上去。


看起来一切都没问题,成功附加了。
刚刚对其进行解码并分析错误后,似乎需要与VPC和子网相关的策略。
以下是策略的JSON。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:CreateVpc",
"ec2:DescribeVpcs",
"ec2:DeleteVpc",
"ec2:ModifyVpcAttribute",
"ec2:CreateTags",
"ec2:DeleteTags",
"ec2:DescribeTags",
"ec2:CreateSubnet",
"ec2:DescribeSubnets",
"ec2:DeleteSubnet",
"ec2:DescribeVpcAttribute"
],
"Resource": "*"
}
]
}
我已经创建并附加了政策。
我将再次尝试进行terraform apply。
$ terraform 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
aws_vpc.main: Destroying... [id=vpc-06e030806]
aws_vpc.main: Destruction complete after 0s
aws_vpc.main: Creating...
aws_vpc.main: Creation complete after 1s [id=vpc-0e77b6339378]
aws_subnet.main_1a: Creating...
aws_subnet.main_1a: Creation complete after 1s [id=subnet-000f00088170b]
Apply complete! Resources: 2 added, 0 changed, 1 destroyed.

看起来VPC和子网设置得很好!
S3中的tfstate也已经更新了呢^^ (S3 de tfstate yě le ne)
试着使用一下~应用篇~
尝试进行模块化和变量化
我在以下类似的树状结构中尝试实现了模块化和变量化。
$ tree
.
├── backend.tf
├── modules
│ └── vpc_subnet
│ ├── main.tf
│ └── variables.tf
└── use_module.tf
backend.tf文件的源代码保持不变。
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr_block
tags = {
Name = "Main-vpc"
}
}
resource "aws_subnet" "main_1a" {
vpc_id = aws_vpc.main.id
cidr_block = var.subnet_1a_cidr_block
availability_zone = "ap-northeast-1a"
}
variable "vpc_cidr_block" {
type = string
}
variable "subnet_1a_cidr_block" {
type = string
}
module "vpc_subnet1" {
source = "./modules/vpc_subnet/"
vpc_cidr_block = "192.168.1.0/24"
subnet_1a_cidr_block = "192.168.1.0/25"
}
module "vpc_subnet2" {
source = "./modules/vpc_subnet/"
vpc_cidr_block = "192.168.12.0/24"
subnet_1a_cidr_block = "192.168.2.0/25"
}
工作空间的使用
我想要在工作空間中分别创建两个环境,一个是stage,另一个是master。
$ terraform workspace list
* default
$ terraform workspace new "stage"
Created and switched to workspace "stage"!
You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.
$ terraform workspace new "master"
Created and switched to workspace "master"!
You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.
然后,我会试着制作到可以使用的阶段。
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr_block
tags = {
Name = "${terraform.workspace}-test-vpc" //現在選ばれているworkspaceが入る
}
}
总结
这次,一个没有经验的学生尝试挑战基础内容。希望能对某人有所参考。