[Terraform] Terraform入門(4) - 利用Terraform來操作AWS EC2

鼠年全馬鐵人挑戰 - WEEK 09

前言

本篇為Terraform系列的第四篇,主要實作用Terraform遠端建立並連接EC2,代替之前手動點擊aws console,IAM的部分會選擇Amazon Linux AMI,閱讀本篇建議要先了解AWS EC2的相關知識。

Hands on Lab

建立Security Group

屬性可詳見官方給的sample

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Configure the AWS Provider
provider "aws" {
region = "us-east-2" #選擇你要的區域
version = "~> 2.55"
}

// HTTP Server ->Security Group
// Security Group(即fire wall)=> 80 port : TCP , 22 port : TCP, CIDR: ["0.0.0.0/0"]
resource "aws_security_group" "http_server_sg" {
name = "http_server_sg"
vpc_id = "VPC ID" # 貼上AWS 預設的VPC ID
ingress{ # ingress為入口的限制規則
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress{ # ingress為入口的限制規則
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress{ # 出口規則
from_port = 0
to_port = 0
protocol = -1
cidr_blocks = ["0.0.0.0/0"]
}

tags = {
name = "http_server_sg"
}

}

依照上述程式碼執行terraform apply,即可成功建立Security Group

有關Security Group設定的rule,範例的設定不是比較安全的做法,如ingress設定。
NOTE: Setting protocol = “all” or protocol = -1 with from_port and to_port will result in the EC2 API creating a security group rule with all ports open. This API behavior cannot be controlled by Terraform and may generate warnings in the future.
上述為其中一個有關安全設定的提醒,更多安全建議可參考官方文件

務必要看清楚當前VPC所在的Region,別像我一樣第一次實作把不同Region的VPC ID貼上去,結果一直跳出The vpc ID does not exist的錯誤!

到AWS EC2的管理介面,左方列表找到Security Groups即可看見剛剛新建的Security Group

下載key pair

至AWS EC2的管理介面,左方列表找到Key Pairs,進入頁面點擊Create key pair
取名為ec2-terraform,選擇pem

完成建立的同時,會下載一組key pair到本地,將此檔移入專案資料夾中,接著更改檔案權限

1
chmod 400 ec2-terraform.pem

chmod 400表示只有擁有者才能夠修改的權限,非擁有者只能夠讀取

建立EC2

接續上面的範例程式碼,接著新增aws_instance這個resource

1
2
3
4
5
6
7
resource "aws_instance" "http_server" {
ami = "ami-0e01ce4ee18447327" # 對應 Amazon Linux 2 AMI
key_name = "ec2-terraform" # 下載至本地的pem檔檔名
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.http_server_sg.id]
subnet_id = "subnet-16ee127d"
}

點擊Launch Instance,選擇AMI為Amazon Linux 2 AMI,Instance Type為t2.micro
AMI對應id的部分可以在Choose AMI的管理介面中看到每個IAM專屬的ID

而Instance Type的部分可以參考AWS提供的type,不一定要按照上述範例

vpc_security_group_ids的話,其實可以從 .tfstate 檔案中找到security group 的id,不過建議靈活一點,不要直接寫死,還是透過aws_security_group.http_server_sg.id來取得id會比較好。

subnet_id的話,回到VPC管理介面,左方欄位有subnet,可找到想要的subnet id

確認沒問題之後可以先下terraform validate進行驗證,接著執行terraform apply就可以囉!

連接EC2

現在要進行遠端連接EC2,我的作法在專案目錄下新增一資料夾,名為aws_key_pair,並原先下載的key pair移入此資料夾下,再設一變數aws_key_pair,裡面存放key pair的位置路徑。
接著新增resource設定connection屬性。

  • type = "ssh": 表我們採用SSH的方式連接
  • host = self.public_ip: 表主機對應的ip
  • private_key = file(var.aws_key_pair): 私鑰從本地專案夾內進行路徑搜尋,找到下載下來的.pem

接著設定provisioner屬性,"remote-exec"表進行遠端操作下達的指令,給定inline陣列,裡面存放要下達的指令,每行指令,區隔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
variable "aws_key_pair" {
default = "./aws_key_pair/ec2-terraform.pem"
}

resource "aws_instance" "http_server" {
ami = "ami-0e01ce4ee18447327" # 對應 Amazon Linux 2 AMI
key_name = "ec2-terraform" # 下載至本地的pem檔檔名
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.http_server_sg.id]
subnet_id = "subnet-16ee127d"

connection { # 遠端連接EC2
type = "ssh"
host = self.public_ip # 公有ip
user = "ec2-user"
private_key = file(var.aws_key_pair)
}
provisioner "remote-exec" { # 遠端執行指令
inline = [ # 連接遠端時,會開始執行一系列的指令
"sudo yum install httpd -y", # install httpd
"sudo service httpd start", # start
# 印出字串訊息至index.html 檔
"echo Welcome to virtual server which is at ${self.public_dns} | sudo tee /var/www/html/index.html", # copy file
]
}
}

新增連接EC2的程式碼後,須先將EC2 terminate才會執行remote-exec的指令,所以先執行terraform destroy,再執行terraform apply

在終端機上大概會看到上述的執行畫面。
接著從.tfstate檔中找到public_dns對應的網址,貼上網址之後就可以順利訪問剛剛的內容囉!

進階:選用預設的VPC

直接使用預設(default)的VPC,將它改為動態的,用來取代原本寫死的vpc_id,Terraform會自動根據當前Provider設定的Region,找該Region下預設的VPC ID,這樣就不會發生像我上述犯下的錯誤: 找錯Region的VPC ID
執行下方指令

1
terraform apply -target=aws_default_vpc.default

並修改vpc_id給定的值

1
2
# vpc_id = "vpc-4dbe6a26" # 貼上AWS 預設的VPC ID
vpc_id = aws_default_vpc.default.id # 使用預設的VPC ID

執行下方指令

1
terraform apply -refresh=false

完整程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# Configure the AWS Provider
provider "aws" {
region = "us-east-2"
version = "~> 2.55"
}

resource "aws_default_vpc" "default" {

}
// HTTP Server ->Security Group
// Security Group(即fire wall)=> 80 port : TCP , 22 port : TCP, CIDR: ["0.0.0.0/0"]
resource "aws_security_group" "http_server_sg" {
name = "http_server_sg"
# vpc_id = "vpc-4dbe6a26" # 貼上AWS 預設的VPC ID
vpc_id = aws_default_vpc.default.id # 使用預設的VPC ID
ingress { // ingress為入口的限制規則
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress { // ingress為入口的限制規則
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress { # 讓任何人都能連進來
from_port = 0
to_port = 0
protocol = -1
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
name = "http_server_sg"
}
}

variable "aws_key_pair" {
default = "./aws_key_pair/ec2-terraform.pem"
}
resource "aws_instance" "http_server" {
ami = "ami-0e01ce4ee18447327"
key_name = "ec2-terraform"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.http_server_sg.id]
subnet_id = "subnet-16ee127d"

connection { # 遠端連接EC2
type = "ssh"
host = self.public_ip # 公有ip
user = "ec2-user"
private_key = file(var.aws_key_pair)
}
provisioner "remote-exec" { # 遠端執行指令
inline = [ # 連接遠端時,會開始執行一系列的指令
"sudo yum install httpd -y", # install httpd
"sudo service httpd start", # start
# 印出字串訊息至index.html 檔
"echo Welcome to virtual server which is at ${self.public_dns} | sudo tee /var/www/html/index.html", # copy file
]
}
}

另外值得一提的是,官方有提到aws_default_vpc不同於其他的resource,Terraform並不會額外建立這項資源,而是直接將它納入管理。

The aws_default_vpc behaves differently from normal resources, in that Terraform does not create this resource, but instead “adopts” it into management.
官方文件

Comments