Learn Terraform from Scratch#
Note
🎓🎓🎓 本文档的目的是帮助大家快速入门Terraform,顺便可以通过 HashiCorp Certified: Terraform Associate
的认证考试
Warning
⚠️⚠️⚠️ 本文档部分代码在cloud中部署的时候,因为使用了一些资源,可能会产生一些费用💰💰💰,请周知。最好及时的去进行清理和删除, 以免产生过多的费用💰💰💰。
Before Start You Need Know#
一定的Linux命令行基础
AWS的基础知识
目录#
What is Infrastructure as Code?#
What is Infrastructure?#
Infrastructure通常指的是application运行所依赖的底层的 基础设施
和它们的 配置
. 这里的基础设施通常是指物理层之上的部分,并不是一个物理设备,一块硬盘,而是一个虚拟机,一个操作系统,一个软件防火墙,网络配置,负载均衡等等。
Infrastructure as Code (IaC)#
简单来说,IaC就是通过代码的方式去管理Infrastructure,它的创建,配置等等。
IaC可以解决软件部署时,运行环境的一致性问题。通过同一套代码就可以创建出一套完全相同的环境,而这样一套环境在IaC产生之前,需要通过人工在Web GUI上去创建和管理。
Infrastructure as code (IaC) tools allow you to manage infrastructure with configuration files rather than through a graphical user interface. IaC allows you to build, change, and manage your infrastructure in a safe, consistent, and repeatable way by defining resource configurations that you can version, reuse, and share.
The idea is to treat your Infrastructure like software. You need to write the code, test it, and execute it to set up, deploy, update, or delete the required Infrastructure.
IaC是实现DevOps的关键基础之一。
Which are the Best IaC Tools in 2022?#
https://medium.com/cloudnativeinfra/when-to-use-which-infrastructure-as-code-tool-665af289fbde
Terraform
Ansible
Cloud init
Chef
Puppet
SaltStack
Vagrant

Declarative vs. Imperative#
可以参考 Declarative and Imperative programming
Declarative - Tell what “not” how
Imperative - Tell what “and” how
Quick Start#
What is Terraform#
Infrastructure as code tool
Open-source and vendor agnostic
Single binary compiled from Go
Declarative syntax
in HCL(HashCorp Configuration Language) or JSON format
Agentless (Push mode)
Terraform Core Components#
Terraform executable file
Configuration files
Provider plugins
State data
Terraform Install#
下载可执行文件
添加PATH
https://www.terraform.io/downloads
以Windows为例:
PS C:\> terraform
Usage: terraform [global options] <subcommand> [args]
The available commands for execution are listed below.
The primary workflow commands are given first, followed by
less common or more advanced commands.
Main commands:
init Prepare your working directory for other commands
validate Check whether the configuration is valid
plan Show changes required by the current configuration
apply Create or update infrastructure
destroy Destroy previously-created infrastructure
All other commands:
console Try Terraform expressions at an interactive command prompt
fmt Reformat your configuration in the standard style
force-unlock Release a stuck lock on the current workspace
get Install or upgrade remote Terraform modules
graph Generate a Graphviz graph of the steps in an operation
import Associate existing infrastructure with a Terraform resource
login Obtain and save credentials for a remote host
logout Remove locally-stored credentials for a remote host
output Show output values from your root module
providers Show the providers required for this configuration
refresh Update the state to match remote systems
show Show the current state or a saved plan
state Advanced state management
taint Mark a resource instance as not fully functional
test Experimental support for module integration testing
untaint Remove the 'tainted' state from a resource instance
version Show the current Terraform version
workspace Workspace management
Global options (use these before the subcommand, if any):
-chdir=DIR Switch to a different working directory before executing the
given subcommand.
-help Show this help output, or the help for a specified subcommand.
-version An alias for the "version" subcommand.
PS C:\> terraform version
Terraform v1.2.6
on windows_amd64
PS C:\>
Terraform Object Types#
Providers
Resources
Data sources
Terraform workflow#
terraform init
terraform plan
terraform apply
terraform destroy
环境准备#
准备AWS 账户以及access key和secret access key
创建第一个tf文件#
main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">= 1.2.0"
}
provider "aws" {
access_key = "********************"
secret_key = "********************"
region = "eu-central-1"
}
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = "true"
tags = {
Name = "my-vpc"
}
}
terraform init#
> terraform init
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 4.16"...
- Installing hashicorp/aws v4.24.0...
- Installed hashicorp/aws v4.24.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.
terraform fmt & validate#
> terraform fmt
> terraform validate
Success! The configuration is valid.
terraform plan#
> terraform plan
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_vpc.vpc will be created
+ resource "aws_vpc" "vpc" {
+ arn = (known after apply)
+ cidr_block = "10.0.0.0/16"
+ 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_classiclink = (known after apply)
+ enable_classiclink_dns_support = (known after apply)
+ enable_dns_hostnames = true
+ enable_dns_support = true
+ 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" = "my-vpc"
}
+ tags_all = {
+ "Name" = "my-vpc"
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
terraform apply#
Note
如果不想每次在apply或者destroy的时候提示输入yes,而是直接apply或则destroy,那么可以加参数 -auto-approve
, 例如 terraform apply -auto-approve
> 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_vpc.vpc will be created
+ resource "aws_vpc" "vpc" {
+ arn = (known after apply)
+ cidr_block = "10.0.0.0/16"
+ 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_classiclink = (known after apply)
+ enable_classiclink_dns_support = (known after apply)
+ enable_dns_hostnames = true
+ enable_dns_support = true
+ 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" = "my-vpc"
}
+ tags_all = {
+ "Name" = "my-vpc"
}
}
Plan: 1 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.vpc: Creating...
aws_vpc.vpc: Still creating... [10s elapsed]
aws_vpc.vpc: Creation complete after 11s [id=vpc-0226b147ad3c83404]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Inspect state#
> terraform show
# aws_vpc.vpc:
resource "aws_vpc" "vpc" {
arn = "arn:aws:ec2:eu-central-1:879589088447:vpc/vpc-0226b147ad3c83404"
assign_generated_ipv6_cidr_block = false
cidr_block = "10.0.0.0/16"
default_network_acl_id = "acl-08f8e3c4aca141247"
default_route_table_id = "rtb-0490c915e0bebf54d"
default_security_group_id = "sg-04aa1dce6a47ed020"
dhcp_options_id = "dopt-f207cf9a"
enable_classiclink = false
enable_classiclink_dns_support = false
enable_dns_hostnames = true
enable_dns_support = true
id = "vpc-0226b147ad3c83404"
instance_tenancy = "default"
ipv6_netmask_length = 0
main_route_table_id = "rtb-0490c915e0bebf54d"
owner_id = "879589088447"
tags = {
"Name" = "my-vpc"
}
tags_all = {
"Name" = "my-vpc"
}
}
> terraform state list
aws_vpc.vpc
Update#
update main.ft
file, change the VPC tag name from my-vpc
to my-vpc-demo
then try to do a plan and apply
> terraform plan
aws_vpc.vpc: Refreshing state... [id=vpc-0226b147ad3c83404]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# aws_vpc.vpc will be updated in-place
~ resource "aws_vpc" "vpc" {
id = "vpc-0226b147ad3c83404"
~ tags = {
~ "Name" = "my-vpc" -> "my-vpc-demo"
}
~ tags_all = {
~ "Name" = "my-vpc" -> "my-vpc-demo"
}
# (15 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
> terraform apply
aws_vpc.vpc: Refreshing state... [id=vpc-0226b147ad3c83404]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# aws_vpc.vpc will be updated in-place
~ resource "aws_vpc" "vpc" {
id = "vpc-0226b147ad3c83404"
~ tags = {
~ "Name" = "my-vpc" -> "my-vpc-demo"
}
~ tags_all = {
~ "Name" = "my-vpc" -> "my-vpc-demo"
}
# (15 unchanged attributes hidden)
}
Plan: 0 to add, 1 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.vpc: Modifying... [id=vpc-0226b147ad3c83404]
aws_vpc.vpc: Modifications complete after 1s [id=vpc-0226b147ad3c83404]
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
terraform destroy#
> terraform destroy
aws_vpc.vpc: Refreshing state... [id=vpc-0226b147ad3c83404]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
# aws_vpc.vpc will be destroyed
- resource "aws_vpc" "vpc" {
- arn = "arn:aws:ec2:eu-central-1:879589088447:vpc/vpc-0226b147ad3c83404" -> null
- assign_generated_ipv6_cidr_block = false -> null
- cidr_block = "10.0.0.0/16" -> null
- default_network_acl_id = "acl-08f8e3c4aca141247" -> null
- default_route_table_id = "rtb-0490c915e0bebf54d" -> null
- default_security_group_id = "sg-04aa1dce6a47ed020" -> null
- dhcp_options_id = "dopt-f207cf9a" -> null
- enable_classiclink = false -> null
- enable_classiclink_dns_support = false -> null
- enable_dns_hostnames = true -> null
- enable_dns_support = true -> null
- id = "vpc-0226b147ad3c83404" -> null
- instance_tenancy = "default" -> null
- ipv6_netmask_length = 0 -> null
- main_route_table_id = "rtb-0490c915e0bebf54d" -> null
- owner_id = "879589088447" -> null
- tags = {
- "Name" = "my-vpc-demo"
} -> null
- tags_all = {
- "Name" = "my-vpc-demo"
} -> null
}
Plan: 0 to add, 0 to change, 1 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
aws_vpc.vpc: Destroying... [id=vpc-0226b147ad3c83404]
aws_vpc.vpc: Destruction complete after 1s
Destroy complete! Resources: 1 destroyed.
Variables and Outputs#
input variables
local values
output values
If you’re familiar with traditional programming languages, it can be useful to compare Terraform modules to function definitions:
Input variables are like function arguments. Output values are like function return values. Local values are like a function’s temporary local variables.
Variables#
https://www.terraform.io/language/values/variables
Variable Syntax#
variable "name_label" {
type = string
description = "value"
default = "value"
sensitive = true
}
to reference the variable var.<name_label>
Note
我们可以使用 terraform console
去测试variable. https://www.terraform.io/cli/commands/console
Data Types#
https://www.terraform.io/language/expressions/types
String
number
boolean
variable "aws_secret_key" {
type = string
description = "aws secret key"
sensitive = true
}
variable "enable_dns_hostnames" {
type = bool
description = "enable dns hostname"
default = true
}
variable "volume_size" {
type = number
description = "volume size in gibibytes"
default = 10
}
to reference the values in terraform code, just var.<name_label>
, like var.aws_secret_key
> var.aws_secret_key
(sensitive)
> var.enable_dns_hostnames
true
> var.volume_size
10
List (list里的所有数据的数据类型必须是一样的,比如
list(string)
,list(number)
)
variable "aws_regions" {
type = list(string)
description = "Region to use for AWS"
default = ["us-east-1", "us-east-2", "us-west-1", "us-west-2"]
}
to reference collection values:
var.<name_labe>[<index>]
index will start 0.
> var.aws_regions
tolist([
"eu-central-1",
"us-east-1",
"us-east-2",
])
> var.aws_regions[1]
"us-east-1"
> var.aws_regions[0]
"eu-central-1"
map , a group of values identified by named labels, 数据类型需要一致.
variable "aws_instance_sizes" {
type = map(string)
description = "instance sizes"
default = {
small = "t2.micro"
medium = "t2.small"
large = "t2.large"
}
}
to reference var.<name_label>.<key_name>
or var.<name_label>["key_name"]
> var.aws_instance_sizes
tomap({
"large" = "t2.large"
"medium" = "t2.small"
"small" = "t2.micro"
})
>
> var.aws_instance_sizes.large
"t2.large"
> var.aws_instance_sizes["large"]
"t2.large"
>
类型不一致会进行类型转换
variable "student1" {
type = map(string)
description = "student information"
default = {
name = "xxxx"
age = 20
}
}
> var.student1
tomap({
"age" = "20"
"name" = "xxxx"
})
> var.student1.age
"20"
Tuple,对应List,不同之处是Tuple里的数据元素可以是不同的数据类型
variable "tuple_test" {
type = tuple
description = "tuple test"
default = ["a", 15, true]
}
$ terraform console
>
> var.tuple_test
[
"a",
15,
true,
]
> var.tuple_test[0]
"a"
> var.tuple_test[1]
15
> var.tuple_test[2]
true
>
object,对应Map,但是数值的类型可以不同
variable "db_port" {
type = object({
external = number
internal = number
protocol = string
})
default = {
external = 5432
internal = 5433
protocol = "tcp"
}
}
$ terraform console
>
> var.db_port
{
"external" = 5432
"internal" = 5433
"protocol" = "tcp"
}
> var.db_port.external
5432
> var.db_port["internal"]
5433
>
Supply variable values#
default value
-var flag
-var-file flag
tf var files
terraform.tfvars
terraform.tfvars.json
.auto.tfvars
.auto.tfvars.json
Environment variable name starts with
TF_VAR_
Demo#
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">= 1.2.0"
}
variable "aws_region" {
type = string
default = "eu-central-1"
}
variable "aws_access_key" {
type = string
description = "aws access key"
sensitive = true
}
variable "aws_secret_key" {
type = string
description = "aws secret key"
sensitive = true
}
variable "enable_dns_hostnames" {
type = bool
description = "enable dns hostname"
default = true
}
provider "aws" {
access_key = var.aws_access_key
secret_key = var.aws_secret_key
region = var.aws_region
}
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = var.enable_dns_hostnames
tags = {
Name = "my-vpc-demo"
}
}
terraform plan -var=aws_access_key="xxxxxxxxx" -var=aws_secret_key="xxxxxxxx"
for Linux and Mac
export TF_VAR_aws_access_key=xxxxxxxxxxxxxxxx
export TF_VAR_aws_secret_key=xxxxxxxxxxxxxxxx
for windows powershell
$env:TF_VAR_aws_access_key="xxxxxxxxxxxxxxxx"
$env:TF_VAR_aws_secret_key="xxxxxxxxxxxxxxxx"
create a file terraform.tfvars
aws_access_key="xxxxxxxxxxxxxxxx"
aws_secret_key="xxxxxxxxxxxxxxxx"
Locals#
https://www.terraform.io/language/values/locals
Syntax#
locals {
# Common tags to be assigned to all resources
common_tags = {
Company = "example.com"
Owner = "test"
}
}
How to Use locals#
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = var.enable_dns_hostnames
tags = local.common_tags
}
Output#
Syntax#
output "name_label" {
value = output_value
}
output "vpc_id" {
value = aws_vpc.vpc.id
}
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = var.enable_dns_hostnames
tags = local.common_tags
}
Demo#
output like
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
vpc_id = "vpc-0e77125c45cdf65c4"
sensitive#
Note
如果不想讓output的值被顯示出來,可以使用sensitive = true,这样就不会在CLI中显示了,但是可以在Terraform state文件中中查看。
output "secret" {
sensitive = true
value = "secret"
}
https://www.terraform.io/language/values/outputs#sensitive-suppressing-values-in-cli-output
AWS Command Line Interface#
Configure#
$ aws configure
AWS Access Key ID [None]: ****************
AWS Secret Access Key [None]: ************6**********lz91IDAm+2k
Default region name [None]: eu-central-1
Default output format [None]: json
$
$
$ ls ~/.aws/
config credentials
$ more ~/.aws/config
[default]
region = eu-central-1
output = json
$ more ~/.aws/credentials
[default]
aws_access_key_id = ****************
aws_secret_access_key = ************6**********lz91IDAm+2k
$
aws_cli for terraform#
实际上如果已经通过aws cli配置了,那么可以直接使用aws cli,不需要配置terraform里的aws provider。
$ aws configure list
Name Value Type Location
---- ----- ---- --------
profile <not set> None None
access_key ****************B4XB shared-credentials-file
secret_key ****************m+2k shared-credentials-file
region eu-central-1 config-file ~/.aws/config
也就是下面的 provider "aws"
可以删掉了。
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">= 1.2.0"
}
provider "aws" {
access_key = "xxxxxxxxxxxxxxx"
secret_key = "xxxxxxxxxxxxxx"
region = "eu-central-1"
}
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "my-vpc"
}
}
Deploy Web Server#
Create a vpc
Create subnets for different parts of the infrastructure
Attach an internet gateway to the VPC
Create a route table for a public subnet
Create security groups to allow specific traffic
Create ec2 instances on the subnets
Functions and Looping#
Looping
count (integer)
for each (map or set)
Functions
Expressions
Count#
Syntax#
resource "aws_vpc" "vpc" {
count = 2
cidr_block = "10.${count.index}.0.0/16"
enable_dns_hostnames = var.enable_dns_hostnames
tags = {
Name = "terraform-vpc-${count.index}"
}
}
terraform plan
> terraform plan
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_vpc.vpc[0] will be created
+ resource "aws_vpc" "vpc" {
+ arn = (known after apply)
+ cidr_block = "10.0.0.0/16"
+ 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_classiclink = (known after apply)
+ enable_classiclink_dns_support = (known after apply)
+ enable_dns_hostnames = true
+ enable_dns_support = true
+ 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 = {
+ "Company" = "example.com"
+ "Owner" = "test"
}
+ tags_all = {
+ "Company" = "example.com"
+ "Owner" = "test"
}
}
# aws_vpc.vpc[1] will be created
+ resource "aws_vpc" "vpc" {
+ arn = (known after apply)
+ cidr_block = "10.1.0.0/16"
+ 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_classiclink = (known after apply)
+ enable_classiclink_dns_support = (known after apply)
+ enable_dns_hostnames = true
+ enable_dns_support = true
+ 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 = {
+ "Company" = "example.com"
+ "Owner" = "test"
}
+ tags_all = {
+ "Company" = "example.com"
+ "Owner" = "test"
}
}
Plan: 2 to add, 0 to change, 0 to destroy.
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
Count References#
<resource_type>.<name_label>[index].<attribute>
aws_vpc.vpc[0].id
aws_vpc.vpc[*].id
# all instance
for example
output "vpc_id" {
value = aws_vpc.vpc[*].id
}
output:
Changes to Outputs:
+ vpc_id = [
+ (known after apply),
+ (known after apply),
]
for_each#
https://www.terraform.io/language/meta-arguments/for_each
The for_each meta-argument accepts a map or a set of strings, and creates an instance for each item in that map or set.
Each instance has a distinct infrastructure object associated with it, and each is separately created, updated, or destroyed when the configuration is applied.
In blocks where for_each is set, an additional each object is available in expressions, so you can modify the configuration of each instance. This object has two attributes:
each.key — The map key (or set member) corresponding to this instance.
each.value — The map value corresponding to this instance. (If a set was provided, this is the same as each.key.)
resource "aws_vpc" "vpc2" {
for_each = {
private = "10.1.0.0/16"
public = "192.168.0.0/16"
}
cidr_block = each.value
enable_dns_hostnames = var.enable_dns_hostnames
tags = {
Name = "terraform-vpc-${each.key}"
}
}
Terraform plan
$ terraform plan
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_vpc.vpc2["private"] will be created
+ resource "aws_vpc" "vpc2" {
+ arn = (known after apply)
+ cidr_block = "10.1.0.0/16"
+ 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_classiclink = (known after apply)
+ enable_classiclink_dns_support = (known after apply)
+ enable_dns_hostnames = true
+ enable_dns_support = true
+ 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" = "terraform-vpc-private"
}
+ tags_all = {
+ "Name" = "terraform-vpc-private"
}
}
# aws_vpc.vpc2["public"] will be created
+ resource "aws_vpc" "vpc2" {
+ arn = (known after apply)
+ cidr_block = "192.168.0.0/16"
+ 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_classiclink = (known after apply)
+ enable_classiclink_dns_support = (known after apply)
+ enable_dns_hostnames = true
+ enable_dns_support = true
+ 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" = "terraform-vpc-public"
}
+ tags_all = {
+ "Name" = "terraform-vpc-public"
}
}
Plan: 2 to add, 0 to change, 0 to destroy.
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run
"terraform apply" now.
Functions#
https://www.terraform.io/language/functions
func_name(arg1, arg2, ...)
Common Functions#
Numeric: min(42, 13, 7)
String: lower(“TEST”)
Collection: merge(map1, map2)
IP Network: cidrsubnet()
File system: file(path)
Type Conversion: toset()
Examples#
try terraform functions with terraform console
$ terraform console
> min(12, 23, 1, 30)
1
> lower("TEST")
"test"
>
> var.vpc_cidr_block
tomap({
"private" = "10.1.0.0/16"
"public" = "192.168.0.0/16"
})
> lookup(var.vpc_cidr_block, "public")
"192.168.0.0/16"
> lookup(var.vpc_cidr_block, "public1")
╷
│ Error: Error in function call
│
│ on <console-input> line 1:
│ (source code not available)
│
│ Call to function "lookup" failed: lookup failed to find key "public1".
╵
> lookup(var.vpc_cidr_block, "public1", "unknow")
"unknow"
>
> merge(var.vpc_cidr_block, {"public1": "1.1.1.1/32"})
{
"private" = "10.1.0.0/16"
"public" = "192.168.0.0/16"
"public1" = "1.1.1.1/32"
}
file and templatefile
$ terraform console
> file("${path.module}/main.tf")
<<EOT
# # use count
# resource "aws_vpc" "vpc" {
# count = 2
# cidr_block = "10.${count.index}.0.0/16"
# enable_dns_hostnames = var.enable_dns_hostnames
# tags = {
# Name = "terraform-vpc-${count.index}"
# }
# }
# # Create a Subnet
# output "vpc_id" {
# value = aws_vpc.vpc[0].id
# }
resource "aws_vpc" "vpc2" {
for_each = var.vpc_cidr_block
cidr_block = each.value
enable_dns_hostnames = var.enable_dns_hostnames
tags = {
Name = "terraform-vpc-${each.key}"
}
}
EOT
>
> file("${path.module}/test.tpl")
"hello world ${name}"
>
> templatefile("test.tpl", {"name": "terraform"})
"hello world terraform"
>
Expressions#
for
condition
for Expressions#
> var.aws_regions
tolist([
"eu-central-1",
"us-east-1",
"us-east-2",
])
> [for v in var.aws_regions: upper(v)]
[
"EU-CENTRAL-1",
"US-EAST-1",
"US-EAST-2",
]
> {for s in var.aws_regions : s => upper(s)}
{
"eu-central-1" = "EU-CENTRAL-1"
"us-east-1" = "US-EAST-1"
"us-east-2" = "US-EAST-2"
}
>
>
> [for i, v in var.aws_regions : "${i} is ${v}"]
[
"0 is eu-central-1",
"1 is us-east-1",
"2 is us-east-2",
]
>
Condition#
定义一个variable
variable "users" {
type = map(object({
is_admin = bool
name = string
}))
default = {
"admin" = {
is_admin = true
name = "admin"
}
"user" = {
is_admin = false
name = "user"
}
}
}
locals {
admin_users = {
for name, user in var.users : name => user
if user.is_admin
}
regular_users = {
for name, user in var.users : name => user
if !user.is_admin
}
}
> var.users
tomap({
"admin" = {
"is_admin" = true
"name" = "admin"
}
"user" = {
"is_admin" = false
"name" = "user"
}
})
> local.admin_users
{
"admin" = {
"is_admin" = true
"name" = "admin"
}
}
> local.regular_users
{
"user" = {
"is_admin" = false
"name" = "user"
}
}
>
alias: Multiple Provider Configurations#
https://www.terraform.io/language/providers/configuration#alias-multiple-provider-configurations
- 在provider里我们可以指定provider的相关配置,但是如果有多个provider怎么办,比如我们要在不同的region里创建资源,这时候就需要用到alias了,
alias
可以让我们在一个provider里配置多个不同的provider,比如下面的例子:
provider "aws" {
region = "us-east-1"
}
provider "aws" {
alias = "us-west-2"
region = "us-west-2"
}
resource "aws_vpc" "vpc1" {
provider = aws.us-west-2
cidr_block = "10.1.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "terraform-vpc1"
}
}
resource "aws_vpc" "vpc2" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "terraform-vpc2"
}
}
比如以上,我们只想在us-west-2里创建资源vpc1,我们就可以在resource里指定provider为 aws.us-west-2
,这样就可以在us-west-2里创建资源了。
vpc2并没有指定provider,所以默认使用的是第一个provider,也就是us-east-1。
Providers#
https://registry.terraform.io/browse/providers
Public and private registries
Official
Verified
Community
版本控制(重要考点之一)#
https://www.terraform.io/language/expressions/version-constraints#version-constraint-syntax
The following operators are valid:
= (or no operator)
: Allows only one exact version number. Cannot be combined with other conditions.!=
: Excludes an exact version number.>, >=, <, <=
: Comparisons against a specified version, allowing versions for which the comparison is true. “Greater-than” requests newer versions, and “less-than” requests older versions.~>
: Allows only the rightmost version component to increment. For example, to allow new patch releases within a specific minor release, use the full version number: ~> 1.0.4 will allow installation of 1.0.5 and 1.0.10 but not 1.1.0. This is usually called the pessimistic constraint operator.
Module#
What is a module?#
Module可以理解成Python里的library或者模块,主要是为了代码重用和防止重新造轮子。
Module可以是本地的,也可以是远程的。
远程module可以在https://registry.terraform.io/browse/modules 查看下载
远程module会在引用时,通过terraform init下载到本地。
远程module需要指定版本
发布一个Module#
https://www.terraform.io/registry/modules/publish
module的文件结构如下:
参考 https://www.terraform.io/language/modules/develop/structure
在github上创建一个repo
在repo里创建一个main.tf
在repo里创建一个variables.tf
在repo里创建一个outputs.tf
在repo里创建一个README.md
在repo里创建一个LICENSE
在repo里创建一个.gitignore
一个例子#
GitHub仓库 https://github.com/xiaopeng163/terraform-aws-vpc
Terraform Registry https://registry.terraform.io/modules/xiaopeng163/vpc/aws/latest
Managing State#
https://www.terraform.io/language/state
state is a file that contains a serialized representation of your infrastructure and configuration.
Backends determine where state is stored. For example, the local (default) backend stores state in a local JSON file on disk.
local state
默认情况下,Terraform会将状态保存在一个文件中,这个文件的名称是 terraform.tfstate
。
local state的文件和存储位置是可以修改的
terraform {
backend "local" {
path = "relative/path/to/terraform.tfstate"
}
}
remote state
Terraform writes the state data to a remote data store, which can then be shared between all members of a team. Terraform supports storing state in:
Terraform Cloud
HashiCorp Consul
Amazon S3
Azure Blob Storage
Google Cloud Storage
Alibaba Cloud OSS
and more
lock

State Command#
$ terraform state
Usage: terraform [global options] state <subcommand> [options] [args]
This command has subcommands for advanced state management.
These subcommands can be used to slice and dice the Terraform state.
This is sometimes necessary in advanced cases. For your safety, all
state management commands that modify the state create a timestamped
backup of the state prior to making modifications.
The structure and output of the commands is specifically tailored to work
well with the common Unix utilities such as grep, awk, etc. We recommend
using those tools to perform more advanced state tasks.
Subcommands:
list List resources in the state
mv Move an item in the state
pull Pull current state and output to stdout
push Update remote state from a local state file
replace-provider Replace provider in the state
rm Remove instances from the state
show Show a resource in the state
$ terraform state list
aws_vpc.vpc
$ terraform state show aws_vpc.vpc
# aws_vpc.vpc:
resource "aws_vpc" "vpc" {
arn = "arn:aws:ec2:eu-central-1:879589088447:vpc/vpc-0c872fe63a2a3a244"
assign_generated_ipv6_cidr_block = false
cidr_block = "10.0.0.0/16"
default_network_acl_id = "acl-06189d002ed790bcc"
default_route_table_id = "rtb-0b01d11a6ae442915"
default_security_group_id = "sg-04179c7d9dae848f1"
dhcp_options_id = "dopt-f207cf9a"
enable_classiclink = false
enable_classiclink_dns_support = false
enable_dns_hostnames = true
enable_dns_support = true
id = "vpc-0c872fe63a2a3a244"
instance_tenancy = "default"
ipv6_netmask_length = 0
main_route_table_id = "rtb-0b01d11a6ae442915"
owner_id = "879589088447"
tags = {
"Name" = "terraform-vpc"
}
tags_all = {
"Name" = "terraform-vpc"
}
}
Remote State S3#
This is a remote state that can be used to store and retrieve data from an S3 bucket.
Example Usage
Consul#
download and install consul https://www.consul.io/downloads
Macos/Linux#
vagrant@ubuntu-focal:~$ curl -OL https://releases.hashicorp.com/consul/1.13.1/consul_1.13.1_linux_amd64.zip
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 43.5M 100 43.5M 0 0 18.9M 0 0:00:02 0:00:02 --:--:-- 18.9M
vagrant@ubuntu-focal:~$ unzip consul_1.13.1_linux_amd64.zip
Archive: consul_1.13.1_linux_amd64.zip
inflating: consul
vagrant@ubuntu-focal:~$ sudo mv consul /usr/local/bin/
vagrant@ubuntu-focal:~$ consul version
Consul v1.13.1
Revision c6d0f9ec
Build Date 2022-08-11T19:07:00Z
Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents)
vagrant@ubuntu-focal:~$
vagrant@ubuntu-focal:~$ rm -rf consul_1.13.1_linux_amd64.zip
Prepare config#
$ mkdir data
vagrant@ubuntu-focal:~/github/learn-terraform-from-scratch/code/manage-state/consul$ ls
config data
vagrant@ubuntu-focal:~/github/learn-terraform-from-scratch/code/manage-state/consul$ more config/consul-config.hcl
## server.hcl
ui = true
server = true
bootstrap_expect = 1
datacenter = "dc1"
data_dir = "./data"
acl = {
enabled = true
default_policy = "deny"
enable_token_persistence = true
}
Start consul instance locally#
vagrant@ubuntu-focal:~/github/learn-terraform-from-scratch/code/manage-state/consul$ ls
config data
vagrant@ubuntu-focal:~/github/learn-terraform-from-scratch/code/manage-state/consul$
vagrant@ubuntu-focal:~/github/learn-terraform-from-scratch/code/manage-state/consul$ consul agent -bootstrap -config-file="config/consul-config.hcl" -bind="127.0.0.1"
==> Starting Consul agent...
Version: '1.13.1'
Build Date: '2022-08-11 19:07:00 +0000 UTC'
Node ID: '7305b65b-098d-2998-e887-4652a4d016b9'
Node name: 'ubuntu-focal'
Datacenter: 'dc1' (Segment: '<all>')
Server: true (Bootstrap: true)
Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: -1, DNS: 8600)
Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false, Auto-Encrypt-TLS: false
==> Log data will now stream in as it occurs:
......
consul将会在前台运行
Get Token#
$ consul acl bootstrap
AccessorID: 0e9b5506-6855-d9a9-2c49-5b764b343b0b
SecretID: 26efa357-2768-c72a-a120-0a38fefdf466
Description: Bootstrap Token (Global Management)
Local: false
Create Time: 2022-08-31 14:29:50.179459862 +0000 UTC
Policies:
00000000-0000-0000-0000-000000000001 - global-management
设置环境变量
Linux and Mac
$ export CONSUL_HTTP_TOKEN=26efa357-2768-c72a-a120-0a38fefdf466
Windows
$env:CONSUL_HTTP_TOKEN="26efa357-2768-c72a-a120-0a38fefdf466"
Terraform backend#
terraform {
backend "consul" {
address = "127.0.0.1:8500"
scheme = "http"
path = "test/terraform.tfstate"
}
}
查看state
$ consul kv get test/terraform.tfstate
Re-creation of Resources#
当terraform创建的资源因某些原因出了问题,我们想重建这些资源,可以使用terraform taint命令,例如:
terraform taint aws_instance.web
这个命令会将web实例标记为需要重建,然后执行terraform apply命令,terraform会重建web实例。
terraform apply
资源重建后,terraform会将新的资源信息保存到state文件中,同时资源会被标记为untainted.
如果标记完taint后,反悔了,可以通过terraform untaint命令将资源的taint的标记去掉,例如:
terraform untaint aws_instance.web
import#
有时候我们想把一个已经存在的resource,但并没有被terraform管理的,纳入到terraform中来管理,这时候就需要用到import命令。
文档 https://www.terraform.io/cli/commands/import
import命令的格式如下:
terraform import [options] ADDR ID
其中ADDR是resource的地址,ID是resource的ID,比如一个aws_instance的ID就是instance的ID,一个aws_security_group的ID就是security group的ID。
Demo#
- 我们在aws上创建一个VPC,然后用terraform import把它纳入到terraform的管理中来, 通过aws的web console可以看到这个VPC的ID,比如
vpc-0af5cb081c28cf2e3
新建一个文件夹,比如 import_demo
,然后在这个文件夹下新建一个 main.tf
文件,内容如下:
provider "aws" {
region = "us-east-1"
}
# create vpc
resource "aws_vpc" "my-vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "my-vpc"
}
}
$ terraform import aws_vpc.my-vpc vpc-0af5cb081c28cf2e3
aws_vpc.my-vpc: Importing from ID "vpc-0af5cb081c28cf2e3"...
aws_vpc.my-vpc: Import prepared!
Prepared aws_vpc for import
aws_vpc.my-vpc: Refreshing state... [id=vpc-0af5cb081c28cf2e3]
Import successful!
The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.
常用命令#
terraform state help
Usage: terraform [global options] state <subcommand> [options] [args]
This command has subcommands for advanced state management.
These subcommands can be used to slice and dice the Terraform state.
This is sometimes necessary in advanced cases. For your safety, all
state management commands that modify the state create a timestamped
backup of the state prior to making modifications.
The structure and output of the commands is specifically tailored to work
well with the common Unix utilities such as grep, awk, etc. We recommend
using those tools to perform more advanced state tasks.
Subcommands:
list List resources in the state
mv Move an item in the state
pull Pull current state and output to stdout
push Update remote state from a local state file
replace-provider Replace provider in the state
rm Remove instances from the state
show Show a resource in the state
$ terraform refresh -help
Usage: terraform [global options] refresh [options]
Update the state file of your infrastructure with metadata that matches
the physical resources they are tracking.
This will not modify your infrastructure, but it can modify your
state file to update metadata. This metadata might cause new changes
to occur when you generate a plan or call apply next.
$ terraform force-unlock --help
Usage: terraform [global options] force-unlock LOCK_ID
Manually unlock the state for the defined configuration.
This will not modify your infrastructure. This command removes the lock on the
state for the current workspace. The behavior of this lock is dependent
on the backend being used. Local state files cannot be unlocked by another
process.
Options:
-force Don't ask for input for unlock confirmation.
$ terraform show --help
Usage: terraform [global options] show [options] [path]
Reads and outputs a Terraform state or plan file in a human-readable
form. If no path is specified, the current state will be shown.
Options:
-no-color If specified, output won't contain any color.
-json If specified, output the Terraform plan or state in
a machine-readable form.
Data sources#
https://www.terraform.io/language/data-sources
Data sources allow Terraform to use information defined outside of Terraform, defined by another separate Terraform configuration, or modified by functions.
Resources are data sources
Providers have data sources
Alternative data sources
Templates
HTTP
External
Consul
Http data sources#
https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http
data "http" "example" {
url = "https://checkpoint-api.hashicorp.com/v1/check/terraform"
# Optional request headers
request_headers = {
Accept = "application/json"
}
}
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = jsondecode(data.http.example.response_body)["product"]
}
}
templates data source#
https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/file
Cloud-Init#
Collaboration#
如何在terraform中多人协作, 有下面的几个基本问题需要解决:
如何共享state文件 (remote state)
如何共享变量
普通变量
密码变量
如何管理多环境(dev, test, prod)
不同环境的state
不同环境的变量
多文件/文件夹的形式#
然后在执行的时候,指定不同的变量文件和state文件
// Dev Environment
terraform apply -var-file=common.tfvars -var-file=dev/terraform.tfvars -state=dev/terraform.tfstate
// QA Environment
terraform apply -var-file=common.tfvars -var-file=qa/terraform.tfvars -state=qa/terraform.tfstate
// Prod Environment
terraform apply -var-file=common.tfvars -var-file=prod/terraform.tfvars -state=prod/terraform.tfstate
Workspace#
Variables |
dev |
qa |
prod |
---|---|---|---|
cidr_block |
10.0.0.0/16 |
10.1.0.0/16 |
10.2.0.0/16 |
subnet_count |
1 |
1 |
2 |
code example code/multi-env-demo
Create workspaces#
默认workspace是default, 可以通过下面的命令创建新的workspace
terraform workspace new dev
terraform workspace new qa
terraform workspace new prod
创建新的workspace后,会自动切换到新的workspace, 可以通过下面的命令查看当前的workspace
terraform workspace show
查看当前所有的workspace
terraform workspace list
切换到指定的workspace
terraform workspace select dev
删除指定的workspace
terraform workspace delete dev
workspace的使用#
可以在``tf文件``中使用``terraform.workspace``来获取当前的workspace, 从而区分不同的Workspace
locals {
common_tags = {
Environment = "${terraform.workspace}"
}
}
Terraform Cloud#
https://cloud.hashicorp.com/products/terraform

Create organization and workspace#
Workspace types#
CLI-driven workflow
VCS-driven workflow
API-driven workflow
Troubleshooting#
Validating Configurations
Enable verbose logging
Resource taints
Crash logs
Types of Errors#
Command Errors
Syntax validation
Provider validation
Deployment errors
Command Errors#
$ terraform plan -auto
╷
│ Error: Failed to parse command-line flags
│
│ flag provided but not defined: -auto
╵
For more help on using this command, run:
terraform plan -help
Synctax Validation#
Terraform init first
check syntax and logic
does not check state
manual or automatic
automation checks in pipelines
一般发生在 terraform init
和 terraform validate
时
Note
The Terraform configuration must be valid before initialization so that Terraform can determine which modules and providers need to be installed.
例如:
$ terraform init
There are some 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: Argument or block definition required
│
│ On network.tf line 6: An argument or block definition is required here. To set an argument, use the equals sign "=" to introduce the argument value.
Provider Validation and Deployment Errors ~~~~~~~~~~~~~~~~~~——————————
一般发生在plan和apply时
$ terraform plan
╷
│ Error: Unsupported attribute
│
│ on network.tf line 10, in resource "aws_subnet" "my_subnet":
│ 10: vpc_id = aws_vpc.my_vpc.ids
│
│ This object has no argument, nested block, or exported attribute named "ids". Did you mean "id"?
Deployment errors
比如一些只有deploy时才会发现的错误。例如,S3 bucket name must be unique across all existing bucket names in Amazon S3.
Verbose Logging#
https://www.terraform.io/internals/debugging
TF_LOG=TRACE
TF_LOG_PATH
You can set TF_LOG
to one of the log levels (in order of decreasing verbosity) TRACE
, DEBUG
, INFO
, WARN
or ERROR
to change the verbosity of the logs.
$ export TF_LOG=DEBUG
$ terraform plan
2022-09-06T19:47:22.678Z [INFO] Terraform version: 1.2.8
2022-09-06T19:47:22.678Z [DEBUG] using github.com/hashicorp/go-tfe v1.0.0
2022-09-06T19:47:22.678Z [DEBUG] using github.com/hashicorp/hcl/v2 v2.12.0
2022-09-06T19:47:22.678Z [DEBUG] using github.com/hashicorp/terraform-config-inspect v0.0.0-20210209133302-4fd17a0faac2
2022-09-06T19:47:22.678Z [DEBUG] using github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734
2022-09-06T19:47:22.678Z [DEBUG] using github.com/zclconf/go-cty v1.11.0
2022-09-06T19:47:22.678Z [INFO] Go runtime version: go1.18.1
2022-09-06T19:47:22.678Z [INFO] CLI args: []string{"/usr/local/bin/terraform_1.2.8", "plan"}
...
...
...
关闭logging
$ export TF_LOG=
$ terraform plan
...
terraform taints#
About the Exam#
一些链接和注意事项#
考试介绍和注册 https://www.hashicorp.com/certification/terraform-associate
考试时间:60分钟
- 题型:60道题:
单选
多选 (会提示你选几个)
填空 (一般很简单,比如问terraform默认生成的本地state文件名是什么,答案就是terraform.tfstate)
判断 (特殊的选择题,选择True或者False)
考试的难度: 个人认为难度不大,好好准备考80分以上应该没问题。
考试的大概过程#
你会收到一封邮件,里面有考试的链接注意事项,一定要认真阅读
考试可以提前30分钟launch,但是你必须在60分钟内完成考试,否则会被自动退出
- 提前launch考试是因为你需要做很多检查,比如但不限于
下载安装一个考试专用浏览器
检查你的电脑是否满足考试的要求,关闭不允许的软件
通过摄像头展示你的ID
通过摄像头展示你的桌面,周边环境
考试的协调人员会跟你进行online chat,告诉你考试的注意事项等
考试开始。题目可以跳跃,标记,收藏,可以任意返回之前标记的题目重新作答
点击结束考试,会马上告诉你考试结果。你也会收到一封邮件,告诉你考试的结果
Azure with Terraform#
Azure command line interface#
Install the Azure command line interface (CLI) on your machine. You can find the instructions here: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest
Azure CLI is a command line tool that allows you to manage Azure resources. It is available for Windows, Linux, and macOS. You can use it to create and manage Azure resources from the command line. You can also use it to create and manage Azure resources from scripts.
Authenticating with Azure CLI#
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/azure_cli
az login
This will open a browser window and ask you to login to your Azure account. After you login, you will be asked to select the Azure subscription you want to use.
Authenticating using a Service Principal with a Client Secret#
Creating a Service Principal using the Azure CLI#
首先先 az login 登录。拿到 subscription id
az account show
创建一个 service principal
az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/<SUBSCRIPTION_ID>"
这个命令会返回一个 json,里面包含了 client_id, client_secret, tenant_id, 其中:
appId
就是client_id
password
就是client_secret
tenant
就是tenant_id
Warning
以上的password一定要找个地方记下来,不要忘记
再加上我们的 subscription id,就可以在 terraform 中使用了。设置环境变量如下:
export ARM_CLIENT_ID="00000000-0000-0000-0000-000000000000"
export ARM_CLIENT_SECRET="00000000-0000-0000-0000-000000000000"
export ARM_SUBSCRIPTION_ID="00000000-0000-0000-0000-000000000000"
export ARM_TENANT_ID="00000000-0000-0000-0000-000000000000"
如果是Windows,可以是如下设置
$env:ARM_CLIENT_ID="00000--0000-00-0-000"
$env:ARM_CLIENT_SECRET="00-00000-000000-000000"
$env:ARM_SUBSCRIPTION_ID="00-00000-000000-000000"
$env:ARM_TENANT_ID="00-00000-000000-000000"
Warning
以上信息如果不小心泄露了,可以通过下面的命令删除重建
1) 通过命令 az ad sp list --display-name azure-cli
可以找到我们创建的service principal的 id
2) 通过命令 az ad sp delete --id <id>
可以删除
Creating a Service Principal in the Azure Portal#
create azure storage for terraform remote state#
需要创建以下几个东西,resource group,storage account,blob container
如果是Linux或者Mac
RESOURCE_GROUP_NAME=tfstate
STORAGE_ACCOUNT_NAME=tfstate$RANDOM
CONTAINER_NAME=tfstate
# Create resource group
az group create --name $RESOURCE_GROUP_NAME --location westeurope
# Create storage account
az storage account create --resource-group $RESOURCE_GROUP_NAME --name $STORAGE_ACCOUNT_NAME --sku Standard_LRS --encryption-services blob
# Create blob container
az storage container create --name $CONTAINER_NAME --account-name $STORAGE_ACCOUNT_NAME
About Me#

网名 麦兜搞IT
资深网络运维工程师,现居 荷兰
,在某银行数据中心网络部门担任资深网络运维工程师,负责Net DevOps的落地实施
此前先后曾在 Cisco
、KPN
等公司工作10年之久,对运维自动化,DevOps有着丰富的实战经验。17年开始涉足在线教育,现有学生超过4万人。
🔭 I’m currently working as a 🛠 Network DevOps engineer @ing-bank Netherlands.
📚 I like creating tech training videos online (Udemy, YouTube, WeChat)
💬 How to reach me: GitHub, Twitter, LinkedIn
也欢迎中国大陆的朋友关注我的微信公众号,会不定期分享一些Docker/k8s的技术文章
