Terraform1.5で追加されたimportブロックを試す
importブロックは宣言的に既存のリソースをTerraform管理下にできるTerraformの構文です。今まではterraform import
で既存のリソースをTerraform管理下にできましたが、一つ一つのリソースしかインポートできずに面倒でした。また、今まではできなかったHCLのコードを生成することも可能になっています。
developer.hashicorp.com
実践
EC2のインスタンスをAWSマネジメントコンソールから作成し、それをimportブロックでTerraform管理下にします。
まず、importブロックのコードを書きます。to
にはインポート先となるresourceブロックのIDを指定します。
id
にはインポートする個々のリソースを識別するIDを設定します。これはリソースの種類によって異なります。EC2の場合はインスタンスIDになります。
imports.tf
import { to = aws_instance.example id = "i-00000000000000000" }
この状態でterraform plan
を実行すると、to
にあたるインポート先がないためにエラーになります。そして、terraform plan -generate-config-out=generated.tf
でコードを生成することができる旨のメッセージが表示されます。親切です。これに従って、HCLのコードを生成します。
EC2の場合は、コードを生成すると、設定がコンフリクトしている旨のエラーが出力されます。
│ Error: Conflicting configuration arguments │ │ with aws_instance.example, │ on generated.tf line 14: │ (source code not available) │ │ "ipv6_address_count": conflicts with ipv6_addresses ╵ ╷ │ Error: Conflicting configuration arguments │ │ with aws_instance.example, │ on generated.tf line 15: │ (source code not available) │ │ "ipv6_addresses": conflicts with ipv6_address_count
ipv6_address_count
とipv6_addresses
はどちらか一方あればよい値です。コード生成をした場合はどちらも出力されてしまいます。
どちらかの記述を削除して、terraform plan
とterraform apply
を実行します。そうすると、importすることができます。
コードの生成は完璧ではありませんが、かなりの部分のコードを補完してくれるため、便利です。
感想
terraform import
で行っていた既存リソースのインポートをTerraformのコードでできるようになり、操作性がよくなったと思います。また、コードの生成も便利なので、これからはimportブロックを使った書き方を採用していきたいと思います。
おわりに
Udemy講座を公開しています。AWSやTerraformの入門から実践的な内容までを学べるものになっています。ぜひ受講ください。
ローカル上のAWS LambdaのコードをTerraform化する
ローカル上のAWS LambdaのコードをTerraform化する方法について説明します。
ローカルにあるコードをTerraform化するにあたってはコードのzipファイル化が必要など、そのあたりのポイントを説明します。
説明
以下が全体のコードになります。
lambda.tf
resource "aws_lambda_function" "example" { function_name = "lambda-terraform-example" handler = "main.lambda_handler" runtime = "python3.8" filename = data.archive_file.lambda_function_payload.output_path source_code_hash = data.archive_file.lambda_function_payload.output_base64sha256 role = aws_iam_role.lambda_exec.arn } resource "aws_iam_role" "lambda_exec" { name = "lambda_exec_example" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "lambda.amazonaws.com" } } ] }) } resource "aws_iam_role_policy_attachment" "lambda_exec_basic" { policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" role = aws_iam_role.lambda_exec.name } data "archive_file" "lambda_function_payload" { type = "zip" source_dir = "${path.module}/src" output_path = "${path.module}/lambda_function_payload.zip" }
src/main.py
import json def lambda_handler(event, context): return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
aws_lambda_functionのリソースにfilenameとsource_code_hashのパラメータを指定しているのがポイントです。こちらにはLambda関数コードが含まれるZIPアーカイブファイルのパスとハッシュを指定します。Lambda関数のソースコードを含むZIPアーカイブは、data archive_fileブロックで作成され、filenameおよびsource_code_hashパラメータに渡されます。
aws_iam_roleリソースは、Lambda関数が使用するIAMロールを作成します。このロールには、AWS Lambdaによる関数の実行に必要なポリシーが含まれます。最後に、aws_iam_role_policy_attachmentリソースを使用して、作成したIAMロールにAWSLambdaBasicExecutionRoleポリシーをアタッチしています。これは、Lambda関数が必要な最小限の権限を持つようにするためのものです。 path.module はTerraformで使用される予約済み変数の一つで、現在のモジュールのルートディレクトリへの絶対パスを表します。
これでローカル上のAWS LambdaのコードをTerraform化することができます。
おわりに
以下のUdemy講座を公開しています。AWSやTerraformの入門から実践的な内容までを学べるものになっています。ぜひ受講ください。
Amazon ECS 入門ハンズオンの環境をTerraformで作成する
本記事では、以下のAmazon ECS 入門ハンズオンの環境をTerraformで作成する具体的な手順を紹介します。
catalog.us-east-1.prod.workshops.aws
AWSでコンテナを用いたシステムを構築する際にはECSが有力な選択肢であるため、有用なハンズオンになっていると思います。 なお、環境構築をする際のオプション的な要素であるため、作業環境となるAWS Cloud9については環境構築の対象外としています。
全体のソースコードは以下にあります
手順
コンテナイメージの作成
最初に以下の内容の通りにECSで動かす対象となるコンテナのイメージを作成します。
コンテナイメージの作成
ECRにアップロード
コンテナイメージをアップロードする先になるECRのリポジトリを作成します。
リポジトリを作成するためのTerraformのコードは以下です。
ecr.tf
resource "aws_ecr_repository" "main" { name = "h4b-ecs-helloworld" force_delete = true } output "repository_url" { value = aws_ecr_repository.main.repository_url }
Terraformで環境を作成する際には、初回のみterraform init
を実行し、環境を更新するたびにterraform apply
を実行します。
$ terraform init $ terraform apply Outputs: repository_url = "xxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/h4b-ecs-helloworld"
リポジトリのURLが出力されますので、このURLに合わせてコンテナイメージのタグ名を変更して、ECRのリポジトリにアップロードできるようにします。
$ docker build -t xxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/h4b-ecs-helloworld:0.0.1 .
コンテナイメージをリポジトリにアップロード(push)するために、docker login が必要です。成功した場合、Login Succeededという出力があります。
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin xxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com Login Succeeded
コンテナイメージをECRにアップロードします。
$ docker push xxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/h4b-ecs-helloworld:0.0.1
VPCの作成
ECS クラスターを作成するために、新しくVPC を作成します。Terraformのコードは以下です。
VPCとパブリックサブネットを2つ作成します。また、デフォルトのセキュリティグループの設定を変更し、HTTPのプロトコルでのアクセスを許可するようにします。
vpc.tf
resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" enable_dns_support = true enable_dns_hostnames = true tags = { Name = "h4b-ecs" } } resource "aws_subnet" "public1" { vpc_id = aws_vpc.main.id cidr_block = "10.0.0.0/20" availability_zone = "ap-northeast-1a" tags = { Name = "h4b-ecs-public1-ap-northeast-1a" } } resource "aws_subnet" "public2" { vpc_id = aws_vpc.main.id cidr_block = "10.0.16.0/20" availability_zone = "ap-northeast-1c" tags = { Name = "h4b-ecs-public2-ap-northeast-1c" } } resource "aws_internet_gateway" "main" { vpc_id = aws_vpc.main.id tags = { Name = "h4b-ecs" } } resource "aws_route_table" "public" { vpc_id = aws_vpc.main.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.main.id } tags = { Name = "h4b-ecs-rtb-public" } } resource "aws_route_table_association" "public1" { subnet_id = aws_subnet.public1.id route_table_id = aws_route_table.public.id } resource "aws_route_table_association" "public2" { subnet_id = aws_subnet.public2.id route_table_id = aws_route_table.public.id } resource "aws_default_security_group" "default" { vpc_id = aws_vpc.main.id ingress { protocol = -1 self = true from_port = 0 to_port = 0 } ingress { protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] from_port = 80 to_port = 80 } egress { protocol = -1 cidr_blocks = ["0.0.0.0/0"] from_port = 0 to_port = 0 } }
ECSクラスターの作成
ECSクラスターは、コンテナを動かすための論理的なグループです。ECSを操作するために、まずは ECSクラスターを作成していきます。Terraformのコードは以下です。
ecs.tf
resource "aws_ecs_cluster" "main" { name = "h4b-ecs-cluster" }
タスク定義の作成
タスク定義とは、アプリケーションを動かすために、どのようにコンテナを動かすか、コンテナの動作を定義したものです。Terraformのコードは以下です。
ecs.tf
locals { container_name = "apache-helloworld" } resource "aws_ecs_task_definition" "main" { family = "h4b-ecs-task-definition" cpu = 512 memory = 1024 requires_compatibilities = ["FARGATE"] execution_role_arn = aws_iam_role.ecs_task_execution_role.arn network_mode = "awsvpc" container_definitions = <<-EOS [ { "name": "${local.container_name}", "image": "${aws_ecr_repository.main.repository_url}:0.0.1", "cpu": 0, "portMappings": [ { "name": "${local.container_name}", "containerPort": 80, "hostPort": 80, "protocol": "tcp", "appProtocol": "http" } ], "essential": true, "environment": [], "environmentFiles": [], "mountPoints": [], "volumesFrom": [], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-create-group": "true", "awslogs-group": "/ecs/h4b-ecs-task-definition", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "ecs" } } } ] EOS runtime_platform { cpu_architecture = "X86_64" operating_system_family = "LINUX" } }
タスク定義ではタスク実行ロールというIAMロールを設定する必要があります。以下のTerraformのコードでそのIAMロールを作成します。
このIAMロールにより、ECRリポジトリからコンテナイメージを取得する、CloudWatch Logsにコンテナログを送信するといった許可を与えます。
iam.tf
resource "aws_iam_role" "ecs_task_execution_role" { name = "h4b-ecs-task-execution-role" assume_role_policy = <<-EOS { "Version": "2008-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOS } resource "aws_iam_policy" "ecs_task_execution_role_policy" { name = "h4b-ecs-task-execution-role-policy" policy = <<-EOS { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ecr:GetAuthorizationToken", "ecr:BatchCheckLayerAvailability", "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage", "logs:CreateLogStream", "logs:PutLogEvents", "logs:CreateLogGroup" ], "Resource": "*" } ] } EOS } resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_policy" { role = aws_iam_role.ecs_task_execution_role.name policy_arn = aws_iam_policy.ecs_task_execution_role_policy.arn }
サービスの作成
サービスを使用すると、ECSクラスターで、指定した数のコンテナ群(タスク)を維持できます。一般的には、長期間動かすアプリケーションに適しています。
Terraformのコードは以下です。
ecs.tf
resource "aws_ecs_service" "main" { name = "h4b-ecs-service" cluster = aws_ecs_cluster.main.id task_definition = aws_ecs_task_definition.main.arn launch_type = "FARGATE" desired_count = 2 network_configuration { subnets = [ aws_subnet.public1.id, aws_subnet.public2.id ] security_groups = [ aws_default_security_group.default.id ] assign_public_ip = true } load_balancer { target_group_arn = aws_lb_target_group.main.arn container_name = local.container_name container_port = 80 } }
サービスを作成する際にロードバランサーも必要になります。 ロードバランサーにアクセスが来ると、ロードバランサーを経由してECSサービスで起動するタスクに接続される形になります。
lb.tf
resource "aws_lb" "main" { name = "h4b-ecs-alb" internal = false load_balancer_type = "application" security_groups = [aws_default_security_group.default.id] subnets = [aws_subnet.public1.id, aws_subnet.public2.id] } resource "aws_lb_listener" "main" { load_balancer_arn = aws_lb.main.arn port = "80" protocol = "HTTP" default_action { type = "forward" target_group_arn = aws_lb_target_group.main.arn } } resource "aws_lb_target_group" "main" { name = "h4b-ecs-targetgroup" port = 80 protocol = "HTTP" vpc_id = aws_vpc.main.id target_type = "ip" }
これで環境作成は完了です。
サービス→ ネットワーキング→ロードバランサーのDNS名にアクセスするとHello World! と表示されてアクセスできるはずです。
まとめ
この記事では、Amazon ECS 入門ハンズオンの環境をTerraformで作成する手順を紹介しました。
Terraformを使用することで、以下のメリットがあります。
- インフラのコード化: 環境構築の手順をコードで管理することで、再現性と柔軟性が向上します。
- バージョン管理: インフラの変更履歴をGitなどのバージョン管理システムで追跡できます。
- 環境の再利用: 同じ環境を複数のプロジェクトやチームで再利用できます。
- 簡易化されたデプロイ: Terraformを使うことで、インフラのデプロイが簡単になり、ミスを減らすことができます。
これらのメリットを活かし、ECSを利用したシステム構築を効率的に行うことができます。
宣伝
以下のUdemy講座を公開しています。AWSやTerraformの入門から実践的な内容までを学べるものになっています。ぜひ受講ください。