AWS入門:Terraformで構築したEC2環境にSSH接続する

スポンサーリンク
AWS入門:Terraformで構築したEC2環境にSSH接続する 環境構築
AWS入門:Terraformで構築したEC2環境にSSH接続する
この記事は約17分で読めます。
よっしー
よっしー

こんにちは。よっしーです(^^)

今日は、Terraformで構築したEC2環境にSSH接続する方法についてご紹介します。

スポンサーリンク

前提

この記事は下記の記事をベースにしています。

背景

先日、terraformでEC2環境を構築しましたが、その際、EC2インスタンスにログインする設定ができていなかったので、SSH接続できる設定を追加しました。

AWSのIAMユーザに、terraformというユーザを作成して、PowerUserAccessのポリシーを許可しています。

修正内容

下記のファイルを更新、もしくは、作成します。下記の各セクションに各ファイルの修正内容を記載しています。

        new file:   key.tf
        new file:   security.tf
        modified:   ec2.tf
        modified:   output.tf
        modified:   variables.tf

key.tf

下記の内容で新規作成します。

# 秘密鍵のアルゴリズム設定
resource "tls_private_key" "learn_ec2_private_key" {
  algorithm = "RSA"
  rsa_bits  = 2048
}

# 上記で作成した公開鍵をAWSのKey pairにインポート
resource "aws_key_pair" "learn_ec2_keypair" {
  key_name   = var.key_name
  public_key = tls_private_key.learn_ec2_private_key.public_key_openssh
}

resource "aws_secretsmanager_secret" "learn_ec2_secret" {
  name = var.key_name
}

resource "aws_secretsmanager_secret_version" "learn_ec2_secret" {
  secret_id     = aws_secretsmanager_secret.learn_ec2_secret.id
  secret_string = tls_private_key.learn_ec2_private_key.private_key_pem
}

security.tf

下記の内容で新規作成します。


resource "aws_security_group" "learn_ec2_sg" {
  name   = var.tag_name
  vpc_id = aws_vpc.learn_ec2_vpc.id
}

resource "aws_security_group_rule" "learn_ec2_ingress" {
  type              = "ingress"
  from_port         = "22"
  to_port           = "22"
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.learn_ec2_sg.id
}

resource "aws_security_group_rule" "learn_ec2_egress" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "all"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.learn_ec2_sg.id
}

ec2.tf

下記の緑色の箇所を追加します。

   ami                         = data.aws_ssm_parameter.learn_ec2_ami.value
   instance_type               = "t2.micro"
   availability_zone           = var.az_a
+  vpc_security_group_ids      = [aws_security_group.learn_ec2_sg.id]
   subnet_id                   = aws_subnet.learn_ec2_public_subnet_1a.id
   associate_public_ip_address = "true"
+  key_name                    = var.key_name
   tags = {
     Name = "${var.tag_name}"
   }

output.tf

下記の緑色の箇所を最終行に追記します。

+output "secretsmanager_secret" {
+  value = aws_secretsmanager_secret.learn_ec2_secret.id
+}
+
+output "secretsmanager_secret_version" {
+  value = aws_secretsmanager_secret_version.learn_ec2_secret.id
+}

variables.tf

下記の緑色の箇所を最終行に追記します。

+variable "key_name" {
+  default = "learn-awc-ec2-keypair"
+}

EC2環境構築

下記のコマンドを実行します。

# プロジェクトの初期化
terraform init

# フォーマット
terraform fmt

# バリデーション
terraform validate

上記のコマンドが問題なければ、下記のコマンドを実行します。

# AWSリソースの作成
terraform apply

下記のような出力になっていれば成功です。

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

Outputs:
ec2_global_ips = [
        "xxx.xxx.xxx.xxx",
    ]
secretsmanager_secret = "xxx"
secretsmanager_secret_version = "xxx"

EC2へのSSH接続

下記のコマンドを入力します。

export AWS_PROFILE=terraform
aws secretsmanager get-secret-value --secret-id learn-awc-ec2-keypair | jq -r .SecretString > learn-awc-ec2-keypair.pem

learn-awc-ec2-keypair.pem という名前のファイルができていれば成功です。

このファイルを使用して、ssh接続します。xxx.xxx.xxx.xxx は、terraform apply の実行結果に出力されていた ec2_global_ipsの値を指定します。

# パーミッションの変更
chmod 600 learn-awc-ec2-keypair.pem

# ssh接続
ssh -i learn-awc-ec2-keypair.pem ec2-user@xxx.xxx.xxx.xxx

下記のような表示になっていれば成功です。

% ssh -i learn-awc-ec2-keypair.pem ec2-user@3.112.36.144

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

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

解説

key.tf

このコードは、Terraformというインフラストラクチャコード管理ツールを使用して、AWS上にEC2インスタンスで使用するための秘密鍵(RSA形式)を生成し、AWS Secrets Managerを介してシークレットとして保存する処理を行っています。

以下にコードの各部分の説明を示します:

  1. tls_private_key リソース: このリソースは、Terraformの tls_private_key プロバイダを使って秘密鍵を生成するための設定です。algorithm パラメータは秘密鍵のアルゴリズムを指定し、”RSA” として設定されています。rsa_bits パラメータは秘密鍵のビット長を指定し、ここでは2048ビットに設定されています。生成された秘密鍵は、後述のAWS Key PairリソースとAWS Secrets Managerリソースで使用されます。
  2. aws_key_pair リソース: これはAWS上にKey Pairを作成するためのリソースです。key_name パラメータはKey Pairの名前を指定します。public_key パラメータには、先に生成した tls_private_key リソースで作成された公開鍵をOpenSSH形式で指定しています。これにより、TerraformがAWSにKey Pairを作成し、公開鍵を登録します。
  3. aws_secretsmanager_secret リソース: これはAWS Secrets Managerを使ってシークレットを作成するためのリソースです。name パラメータはシークレットの名前を指定します。この例では、変数 var.key_name の値がシークレット名として使用されます。
  4. aws_secretsmanager_secret_version リソース: このリソースは、AWS Secrets Managerシークレットのバージョンを作成するためのリソースです。secret_id パラメータには、作成したシークレットのID(aws_secretsmanager_secretリソースの id 属性)を指定します。secret_string パラメータには、先に生成した tls_private_key リソースで作成された秘密鍵をPEM形式で指定しています。TerraformはこれをAWS Secrets Managerに登録します。

このコード全体では、まず tls_private_key リソースを使って秘密鍵を生成し、その公開鍵を aws_key_pair リソースを使ってAWSにインポートします。次に、秘密鍵を aws_secretsmanager_secret リソースと aws_secretsmanager_secret_version リソースを使ってAWS Secrets Managerに登録します。これにより、秘密鍵がシークレットとして安全に保存され、必要な場合に利用できるようになります。

security.tf

このコードは、AWS上に新しいセキュリティグループを作成するためのTerraformコードです。セキュリティグループは、EC2インスタンスなどのリソースに対して入力と出力のトラフィックを制御するために使用されます。

以下にコードの各部分の説明を示します:

  1. aws_security_group リソース: これはAWS上に新しいセキュリティグループを作成するためのリソースです。name パラメータには、変数 var.tag_name の値がセキュリティグループの名前として使用されます。vpc_id パラメータには、セキュリティグループを作成するVPCのIDを指定します。この例では、aws_vpc.learn_ec2_vpc.id を使って先に定義されたVPCのIDを指定しています。
  2. aws_security_group_rule リソース (Ingress Rule): これはセキュリティグループの入力トラフィックを制御するためのルールを作成するリソースです。type パラメータには、トラフィックの方向を “ingress” と指定します。from_port パラメータには、入力トラフィックの開始ポート番号を指定します。この例ではSSH接続を許可するためにポート22を指定しています。to_port パラメータには、入力トラフィックの終了ポート番号を指定します。protocol パラメータには、トラフィックのプロトコルを指定します。この例ではTCPを指定しています。cidr_blocks パラメータには、許可するソースIPアドレスの範囲をCIDR形式で指定します。ここでは”0.0.0.0/0″を指定して、どのIPアドレスからでも接続を許可しています。security_group_id パラメータには、このセキュリティグループを紐付けるためのセキュリティグループのIDを指定しています。
  3. aws_security_group_rule リソース (Egress Rule): これはセキュリティグループの出力トラフィックを制御するためのルールを作成するリソースです。type パラメータには、トラフィックの方向を “egress” と指定します。from_port パラメータには、出力トラフィックの開始ポート番号を指定します。この例では0を指定して、すべてのポートを対象としています。to_port パラメータには、出力トラフィックの終了ポート番号を指定します。protocol パラメータには、トラフィックのプロトコルを指定します。この例では”all”を指定して、すべてのプロトコルを許可しています。cidr_blocks パラメータには、許可する宛先IPアドレスの範囲をCIDR形式で指定します。ここでは”0.0.0.0/0″を指定して、どのIPアドレスへの接続も許可しています。security_group_id パラメータには、このセキュリティグループを紐付けるためのセキュリティグループのIDを指定しています。

これらのリソースを適用することで、aws_security_group リソースによって指定されたセキュリティグループが作成され、それに対してSSH接続を許可する入力ルールと、すべてのトラフィックを許可する出力ルールが設定されます。ただし、セキュリティグループの設定はセキュリティを考慮して適切に行う必要があります。実際の使用に際しては、より詳細なセキュリティポリシーが必要になることがあります。

ec2.tf

vpc_security_group_ids: EC2インスタンスに適用するセキュリティグループのIDのリストを指定します。この例では、先に定義された aws_security_group.learn_ec2_sg.id の値をリストに含めています。

key_name: EC2インスタンスに割り当てるSSHキーペアの名前を指定します。

output.tf

このコードは、Terraformコードの出力を定義しています。出力定義を使用することで、Terraformの実行結果から重要な情報を取得できます。この例では、AWS Secrets Managerに保存された秘密鍵のシークレットとシークレットのバージョンのIDを出力しています。

以下に各出力定義の説明を示します:

  1. output "secretsmanager_secret": この出力定義では、AWS Secrets Managerに保存された秘密鍵のシークレットのIDを出力します。value パラメータには、出力したい値を指定します。aws_secretsmanager_secret.learn_ec2_secret.id は、先に定義された aws_secretsmanager_secret リソースの id 属性を参照しています。
  2. output "secretsmanager_secret_version": この出力定義では、AWS Secrets Managerに保存された秘密鍵のシークレットのバージョンのIDを出力します。value パラメータには、出力したい値を指定します。aws_secretsmanager_secret_version.learn_ec2_secret.id は、先に定義された aws_secretsmanager_secret_version リソースの id 属性を参照しています。

これらの出力定義をTerraformコードに追加することで、Terraformの実行結果によってAWS Secrets Managerに保存された秘密鍵のシークレットとシークレットのバージョンのIDを取得できるようになります。出力結果はTerraformの実行後に表示されるため、他のTerraformコードやスクリプトでこれらの値を使用することができます。例えば、別のリソースにこれらのIDを渡すことで、秘密鍵を他のリソースに関連付けたり、必要に応じて使用できるようにすることができます。

variables.tf

このコードは、Terraformコード内で使用する変数 key_name を定義しています。変数を使用することで、Terraformコードのパラメータを柔軟に設定することができます。

以下に変数定義の説明を示します:

  1. variable "key_name": これは変数を定義するためのTerraformの構文です。key_name は変数の名前を表します。この変数は、後で var.key_name のようにして参照されることになります。
  2. default = "learn-awc-ec2-keypair": default パラメータを使用して、変数 key_name のデフォルト値を指定しています。この例では、デフォルト値として “learn-awc-ec2-keypair” が設定されています。変数が外部から値を指定されなかった場合には、このデフォルト値が適用されます。

この変数定義をTerraformコードに追加することで、Terraform実行時に key_name 変数が使われる場所で、値が指定されていない場合にはデフォルト値が適用されます。例えば、前述のコードでは key_name 変数が使われていましたが、実行時に値が指定されなかった場合には “learn-awc-ec2-keypair” が使用されることになります。外部から key_name 変数に異なる値を渡すことで、別のSSHキーペアを利用することも可能になります。

トラブルシューティング

creating Secrets Manager Secret: InvalidRequestException:

terraform apply 時に下記のエラーになることがありました。

creating Secrets Manager Secret: InvalidRequestException: You can't create this secret because a secret with this name is already scheduled for deletion.

このエラーは、AWS Secrets Managerでシークレットを作成しようとした際に発生したものです。エラーメッセージによれば、このシークレットの名前を持つシークレットがすでに削除スケジュールされているため、新しいシークレットを作成することができないという意味です。

このエラーの原因は、AWS Secrets Managerでシークレットを削除した後に、そのシークレットが完全に削除されるまで一定期間が必要なためです。この期間内に同じ名前のシークレットを再作成しようとすると、上記のエラーが発生します。

シークレットの削除スケジュール期間は、AWS Secrets Managerのポリシーや設定によって異なりますが、通常数日から数週間の間です。削除スケジュールが終了した後、同じ名前のシークレットを再作成することができます。

このエラーを回避するためには、以下のいずれかの方法を選択します:

  1. 待機: 削除スケジュールが完了するまで待機してから、新しいシークレットを作成します。削除が完了するまでの期間は、シークレットの規模や設定によって異なりますので、必要な期間を確認して待つ必要があります。
  2. 別の名前を使用する: 同じ名前のシークレットを再作成できない場合は、別の名前を使って新しいシークレットを作成します。例えば、”learn-ec2-secret-v2″ のようにバージョンを示す接尾辞を付けるなどの方法が考えられます。
  3. 既存のシークレットを復元する: 削除スケジュール中に誤ってシークレットを削除した場合は、一部のAWSリージョンではシークレットを回復できることがあります。AWSサポートにお問い合わせいただくか、AWSコンソールで削除したシークレットの “回復” オプションを確認してください。

適切な方法は、状況によって異なりますので、現在の状況と要件に基づいて選択してください。

今回は下記のコマンドを利用して、削除スケジュール期間なしで削除しました。

export AWS_PROFILE=terraform
aws secretsmanager delete-secret --secret-id learn-awc-ec2-keypair --force-delete-without-recovery

おわりに

今日は、Terraformで構築したEC2環境にSSH接続する方法についてご紹介しました。

SSH接続することができたので、次回は、nginxをインストールして見たいと思います。

本記事でご紹介したソースは、下記のリポジトリにあります。

よっしー
よっしー

何か質問や相談があれば、遠慮なくコメントしてください。また、エンジニア案件についても、いつでも相談にのっていますので、お気軽にお問い合わせください。

それでは、また明日お会いしましょう(^^)

コメント

タイトルとURLをコピーしました