こんにちは。よっしーです(^^)
今日は、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を介してシークレットとして保存する処理を行っています。
以下にコードの各部分の説明を示します:
tls_private_key
リソース: このリソースは、Terraformのtls_private_key
プロバイダを使って秘密鍵を生成するための設定です。algorithm
パラメータは秘密鍵のアルゴリズムを指定し、”RSA” として設定されています。rsa_bits
パラメータは秘密鍵のビット長を指定し、ここでは2048ビットに設定されています。生成された秘密鍵は、後述のAWS Key PairリソースとAWS Secrets Managerリソースで使用されます。aws_key_pair
リソース: これはAWS上にKey Pairを作成するためのリソースです。key_name
パラメータはKey Pairの名前を指定します。public_key
パラメータには、先に生成したtls_private_key
リソースで作成された公開鍵をOpenSSH形式で指定しています。これにより、TerraformがAWSにKey Pairを作成し、公開鍵を登録します。aws_secretsmanager_secret
リソース: これはAWS Secrets Managerを使ってシークレットを作成するためのリソースです。name
パラメータはシークレットの名前を指定します。この例では、変数var.key_name
の値がシークレット名として使用されます。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インスタンスなどのリソースに対して入力と出力のトラフィックを制御するために使用されます。
以下にコードの各部分の説明を示します:
aws_security_group
リソース: これはAWS上に新しいセキュリティグループを作成するためのリソースです。name
パラメータには、変数var.tag_name
の値がセキュリティグループの名前として使用されます。vpc_id
パラメータには、セキュリティグループを作成するVPCのIDを指定します。この例では、aws_vpc.learn_ec2_vpc.id
を使って先に定義されたVPCのIDを指定しています。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を指定しています。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を出力しています。
以下に各出力定義の説明を示します:
output "secretsmanager_secret"
: この出力定義では、AWS Secrets Managerに保存された秘密鍵のシークレットのIDを出力します。value
パラメータには、出力したい値を指定します。aws_secretsmanager_secret.learn_ec2_secret.id
は、先に定義されたaws_secretsmanager_secret
リソースのid
属性を参照しています。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コードのパラメータを柔軟に設定することができます。
以下に変数定義の説明を示します:
variable "key_name"
: これは変数を定義するためのTerraformの構文です。key_name
は変数の名前を表します。この変数は、後でvar.key_name
のようにして参照されることになります。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のポリシーや設定によって異なりますが、通常数日から数週間の間です。削除スケジュールが終了した後、同じ名前のシークレットを再作成することができます。
このエラーを回避するためには、以下のいずれかの方法を選択します:
- 待機: 削除スケジュールが完了するまで待機してから、新しいシークレットを作成します。削除が完了するまでの期間は、シークレットの規模や設定によって異なりますので、必要な期間を確認して待つ必要があります。
- 別の名前を使用する: 同じ名前のシークレットを再作成できない場合は、別の名前を使って新しいシークレットを作成します。例えば、”learn-ec2-secret-v2″ のようにバージョンを示す接尾辞を付けるなどの方法が考えられます。
- 既存のシークレットを復元する: 削除スケジュール中に誤ってシークレットを削除した場合は、一部の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をインストールして見たいと思います。
本記事でご紹介したソースは、下記のリポジトリにあります。
何か質問や相談があれば、遠慮なくコメントしてください。また、エンジニア案件についても、いつでも相談にのっていますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント