1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Terraform入門】SSH接続できるEC2を簡単に作成する

Last updated at Posted at 2022-05-20

はじめに

EC2で何かすぐ検証したい場合などにTerraformを使ってEC2を作成してSSH接続できると便利です。
手動作成に頼っていると手順確認に時間がかかったり操作ミスをしたりゴミが残ったりしてしまいがちですが、Terraformだとその不安を限りなく小さくできます。
多少知識は必要になるため、本記事ではサンプルコードを用意してすぐにEC2を起動してSSHできるよう手順を簡潔にまとめてみました。Terraformを触ったことがない方でもわかるように導入手順からまとめたつもりです。

環境

本記事の手順は以下の環境で動作確認を行っています。

  • OS: macOS Monterey 12.3.1
  • Terraform: 1.1.9
  • AWS CLI: 2.5.4

Terraformのインストール

Homebrewを使って、以下だけでインストールできます。

$ brew tap hashicorp/tap
$ brew install hashicorp/tap/terraform

手順の最新情報は以下を参照して下さい。

AWS CLIのインストール

以下からpkgファイルをダウンロードしてGUIで簡単にインストールできます。

手順の最新情報は以下を参照して下さい。

インストール後、AWS CLIにAWSアクセスキーの設定が必要です。
アクセスキーをIAMで取得してaws configureで設定します。
以下のように--profileを指定してプロファイルを作成しておくのがおすすめです。

$ aws configure --profile my-profile-name

AWS Access Key ID [None]: my-access-key
AWS Secret Access Key [None]: my-secret-key
Default region name [None]: ap-northeast-1
Default output format [None]: json

環境変数に上で設定したプロファイル名を設定します。

$ export AWS_DEFAULT_PROFILE=my-profile-name

環境変数を毎回設定するのが面倒な場合は、ログインシェルの設定ファイル(.zshrcなど)に登録すると良いと思います。

Terraformのセットアップ

私が作成したサンプルコードがGitHubにあります。

これをクローンして準備します。
terraform initではTerraformの実行に必要なバイナリが.terraform配下にダウンロードされます。

$ git clone https://github.com/inayuky/terraform-sampler
$ cd terraform-sampler/ec2-ssh
$ terraform init

鍵の作成

SSH接続に必要な秘密鍵と公開鍵を作成します。
以下のコマンドだけで作成できます。
秘密鍵のec2_keyと公開鍵のec2_key.pubという2つのファイルが作成されます。

$ ssh-keygen -N "" -f ec2_key

以上で準備は完了です。

インスタンス作成

terraform planでどんなリソースが作成されるかを確認できます。
エラーが出力されないことを確認します。

$ terraform plan

terraform applyで作成できます。

$ terraform apply

本当に実行していいか確認が求められるので、yesを入力します。
(ここで課金が発生することになるので注意してください。デフォルトではt3.microという小さいインスタンスタイプを指定しているため、1時間あたり2円未満のはずですが。)

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

作成が完了すると以下のような出力が表示されます。

Apply complete! Resources: 11 added, 0 changed, 0 destroyed.

Outputs:

public_ip = "13.112.146.55"
ssh_command = "ssh -i ec2_key ec2-user@13.112.146.55"

表示されたSSHコマンドをそのまま入力するとSSH接続できます。(接続できるまで数秒かかるかもしれません)
以下のように表示されるのでyesと入力します。

inayuky ec2-ssh % ssh -i ec2_key ec2-user@13.112.146.55
The authenticity of host '13.112.146.55 (13.112.146.55)' can't be established.
ED25519 key fingerprint is SHA256:btJ3uBhFnw1O+LyvHnpmFNwGmvYl/Hv0MdvdPj99RZo.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? 

これでインスタンスが作成され、SSH接続できることを確認できました。

Warning: Permanently added '13.112.146.55' (ED25519) to the list of known hosts.

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
4 package(s) needed for security, out of 4 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-10-0-0-137 ~]$ 

インスタンス削除

作成するとインスタンスは起動状態になり、課金が発生するので(停止したとしてもEBSボリュームの料金はかかりますが)、不要になったら削除します。

$ terraform destroy

サンプルコードの解説

コメントにある程度わかるように情報を記載したつもりですが、もう少し解説しておこうと思います。

変数

  • 鍵の名前とリソース名は変数で定義している
  • デフォルト値を設定しているので、なにも指定しない場合この値が採用される
  • 値を変更する場合、ソースを直接変更する以外にもterraform apply -var 'key_name=my_key' -var 'resource_name=sample2'のようにコマンド実行時に指定することもできる。
variables.tf
# 鍵の名前
# 秘密鍵のファイル名に相当する名前
variable "key_name" {
  type    = string
  default = "ec2_key"
}

# リソース名
# 簡単のため全リソースでここで設定した共通の名前を使用する
variable "resource_name" {
  type    = string
  default = "sample1"
}

EC2

  • 最新AmazonLinux2のイメージ(AMI)を使用して起動する
  • インスタンスタイプはt3.microを指定しているが、任意に変更できる
  • ElasticIP(EIP)はコメントを外すだけで使える(EIPはインスタンス停止時に課金が発生するので注意)
  • sg.tfでセキュリティグループを追加した場合は、インスタンス側でも指定する必要がある
  • EBSは指定しなくてもデフォルトのものが使用される。コメントを外して任意のサイズとタイプの設定も可能。
ec2.tf
# 最新のAmazonLinux2のイメージ
data "aws_ami" "latest_amzn2" {
  owners      = ["amazon"]
  most_recent = true
  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

# キーペア
# 変数で指定した値を設定
resource "aws_key_pair" "this" {
  key_name   = var.key_name
  public_key = file("${var.key_name}.pub")
}

# EIPを使う場合は以下を追加する
# resource "aws_eip" "this" {
#   instance = aws_instance.this.id
#   vpc      = true
# }

# インスタンス
resource "aws_instance" "this" {
  instance_type = "t3.micro" # インスタンスタイプは任意に設定する

  vpc_security_group_ids = [aws_security_group.ssh.id]
  # httpのSGを追加する場合は以下のようにする
  # vpc_security_group_ids = [aws_security_group.ssh.id, aws_security_group.http.id]

  # EBSのサイズとタイプを指定する場合は以下のように追加する
  # root_block_device {
  #   volume_size = 30 # 単位GB
  #   volume_type = "gp3" # gp2がデフォルト。 他はstandard, gp3, io1, io2, sc1, st1。
  # }

  ami = data.aws_ami.latest_amzn2.id

  subnet_id = aws_subnet.public.id

  key_name = aws_key_pair.this.key_name

  tags = {
    Name = var.resource_name # インスタンス名
  }

  lifecycle {
    ignore_changes = [
      ami # インスタンスに変更を加えようとしたら、AMIが新しくなっていてインスタンス再作成が要求されるのを防止するため
    ]
  }
}

セキュリティグループ

  • SSH接続用のSGのみ作成している
  • HTTPなど追加したい場合は、コメントを参照
sg.tf
# SSHのセキュリティグループ
resource "aws_security_group" "ssh" {
  name   = "ssh_sg"
  vpc_id = aws_vpc.this.id
}

resource "aws_security_group_rule" "ingress" {
  type              = "ingress"
  from_port         = 22
  to_port           = 22
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"] # 接続元を限定する場合は変更する
  security_group_id = aws_security_group.ssh.id
}

resource "aws_security_group_rule" "egress" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.ssh.id
}

# セキュリティグループの追加方法
# たとえば、httpのセキュリティグループが必要な場合は以下のように追加する
# resource "aws_security_group" "http" {
#   name   = "http_sg"
#   vpc_id = aws_vpc.this.id
# }

# resource "aws_security_group_rule" "http_ingress" {
#   type              = "ingress"
#   from_port         = 80
#   to_port           = 80
#   protocol          = "tcp"
#   cidr_blocks       = ["0.0.0.0/0"]
#   security_group_id = aws_security_group.http.id
# }

# resource "aws_security_group_rule" "http_egress" {
#   type              = "egress"
#   from_port         = 0
#   to_port           = 0
#   protocol          = "-1"
#   cidr_blocks       = ["0.0.0.0/0"]
#   security_group_id = aws_security_group.http.id
# }

VPC

  • EC2を起動してSSH接続するのに必要な最低限のリソースを作成している
  • VPCはCIDRが同一のものが存在しても複数作成が可能であり、VPCが異なれば他の環境と干渉することはない(あえてそういう設定を加えなければ)ため、安心して作成/削除ができる
network.tf
# VPC
resource "aws_vpc" "this" {
  cidr_block           = "10.0.0.0/16" # 値が既存VPCと重複しても作成可能
  enable_dns_support   = true          # AWSのDNSサーバによる名前解決を有効
  enable_dns_hostnames = true          # VPC内のリソースにパブリックDNSホスト名を自動的に割り当てる

  tags = {
    Name = var.resource_name
  }
}

# サブネット
resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.this.id
  cidr_block              = "10.0.0.0/24"
  map_public_ip_on_launch = true              # 起動したインスタンスにパブリックIPを自動割当
  availability_zone       = "ap-northeast-1a" # AZ指定

  tags = {
    Name = var.resource_name
  }
}

# インターネットゲートウェイ
resource "aws_internet_gateway" "this" {
  vpc_id = aws_vpc.this.id

  tags = {
    Name = var.resource_name
  }
}

# ルートテーブル
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.this.id

  tags = {
    Name = var.resource_name
  }
}

# ルートテーブルのエントリ
resource "aws_route" "public" {
  route_table_id         = aws_route_table.public.id
  gateway_id             = aws_internet_gateway.this.id
  destination_cidr_block = "0.0.0.0/0"
}

# サブネットとルートテーブルの関連付け
resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}

output

  • apply時などにターミナルに出力する値
  • 構成変更してもたまに値が更新されないことがあったが、terraform refreshterraform apply -refresh-onlyを実行すると更新される模様
output.tf
# パブリックIPを出力
output "public_ip" {
  value = aws_instance.this.public_ip
}

# sshコマンドを出力
output "ssh_command" {
  value = "ssh -i ${var.key_name} ec2-user@${aws_instance.this.public_ip}"
}

provider

  • リージョン等を指定する。(リージョン指定しなくてもAWS CLIのデフォルトリージョンで動作はすると思う)
  • AWSの認証情報も指定できたりするが、ソースに入れるとやっかいなことが多いので、認証情報は環境変数やAWS CLI側に持たせるのが好み
provider.tf
provider "aws" {
  region = "ap-northeast-1" # 東京リージョン
}

関連Tips

関連して覚えておくと便利なTipsを以下に記載します。

EC2の状態確認や電源操作をコマンドから簡単に実行する方法

以下の記事で、簡単にEC2の状態確認や電源操作をする方法を書いたので、Terraformと合わせて使用すると、EC2の作成/状態確認/起動停止/削除までコマンドで簡単にできて便利なので、参考にしてください。

作成したリソースの確認方法

以下で、作成したリソースの一覧が確認できます。

$ terraform show

これだと全てのリソースが表示されるので、リソース毎に確認したい場合は以下です。

まず、以下でリソース名一覧を表示します。

$ terraform state list

実行例

inayuky ec2-ssh % terraform state list
data.aws_ami.latest_amzn2
aws_instance.this
aws_internet_gateway.this
aws_key_pair.this
aws_route.public
aws_route_table.public
aws_route_table_association.public
aws_security_group.ssh
aws_security_group_rule.egress
aws_security_group_rule.ingress
aws_subnet.public
aws_vpc.this

以下のようにリソース名を指定して確認できます。

$ terraform state show aws_instance.this

実行例

inayuky ec2-ssh % terraform state show aws_instance.this
# aws_instance.this:
resource "aws_instance" "this" {
    ami                                  = "ami-0f9a314ce79311c88"
    arn                                  = "arn:aws:ec2:ap-northeast-1:123454912345:instance/i-09cac7dead23465a76"
    associate_public_ip_address          = true
    availability_zone                    = "ap-northeast-1a"
    cpu_core_count                       = 1
    cpu_threads_per_core                 = 2
    disable_api_termination              = false
    ebs_optimized                        = false
    get_password_data                    = false
    hibernation                          = false
    id                                   = "i-09cac7dead23465a76"
・・・以下省略・・・

SSH接続時の確認を省略する方法

SSHのデフォルトの動作だと新しいIPアドレスに接続するたびに確認を求められると思いますが、~/.ssh/configに以下の設定を追加しておくと省略できます。

host *
        StrictHostKeyChecking no
        UserKnownHostsFile=/dev/null

一応以下のようにSSHコマンド実行時にオプションで渡すこともできます。

ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null host_name

参考情報

https://booth.pm/ja/items/1318735
Pragmatic Terraform on AWS

https://qiita.com/koki_develop/items/45cdde3d27bd75f1bfd5
TerraformでVPC・EC2インスタンスを構築してssh接続する

https://blog.serverworks.co.jp/automate-latest-ami-ec2
【CloudFormation / Terraform】EC2の最新版のAMI IDを自動的に取得、構築する (Windows / Linux)

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?