Open27

Terraform備忘録

でんちゅーでんちゅー
  • TerraformはIaC(Infrastructure as Code)の一種で、インフラ構成をコードで管理することができるツール
  • インフラ構成を管理するだけなので、内部のユーザー設定等はいじることができない(多分)
  • 設定の記述は独自の.tfファイルを用いる
でんちゅーでんちゅー

基本的に認証は以下のように記述する

provider "aws" {
  region = var.region
  access_key = var.access_key
  secret_key = var.secret_key
}

variable "region" {}
variable "access_key" {}
variable "secret_key" {}
  • 環境変数に"TF_VAR_"で始まる変数を設定しておくと、Terraform側から読み込める
    • ここではTF_VAR_region, TF_VAR_access_key, TF_VAR_secret_keyの3つを設定し、tfファイルで読み込んでいる
でんちゅーでんちゅー
  • gitに載せない認証情報(AccessTokenやSecretKey等)は一般的にterraform.tfvarsファイル内に記述し、*.tfvarsをgitignoreする。
  • .tfvarsファイル内で書かれた変数と値は、別ファイルで同じ変数名を定義することで値が変数に代入される。
terraform.tfvars
region = "ap-northeast-1"
access_key = "YOUR_ACCESS_KEY"
secret_key = "YOUR_SECRET_KEY"
providers.tf
variable "region" {}
variable "access_key" {}
variable "secret_key" {}

provider "aws" {
  region = "${var.region}"
  access_key = "${var.access_key}"
  secret_key = "${var.secret_key}"
}
  • .tfvarsファイルに書いた値を変数として読み込ませたい場合、planやapplyの際に、-var-fileオプションを使用する必要がある。
terraform apply -var-file terraform.tfvars
でんちゅーでんちゅー

てかそもそもapplyについて書き忘れたが、TerraformでConfigurationを書き終えた後に行う処理とそのコマンドを以下に示しておく

  1. terraform init: Terraformファイルを含む作業ファイルを初期化し、必要なプラグインや小モジュール等を読み込んでくれる
  2. terraform plan: Terraformの実行計画を確認できる
  3. terraform apply: TerraformのConfigurationに従って、リソースの作成、変更、削除を行う
  4. terraform destroy: Terraformで作成したリソースを全て削除する
  • 基本的にはConfigurationを書き、terraform initを実行、terraform planで実行計画を確認し、問題なかった場合terraform applyを実行してプラットフォームにリソースを作成する。
  • 注意点として、Configurationの不要な部分を削除したい場合は、その部分のConfigurationコードを削除してterraform applyを行うのが正解。terraform destroyしてしまうと、消す必要のない他のファイルまで消えてしまう。

▼参考
https://zenn.dev/monicle/articles/1aada9da2e742b

でんちゅーでんちゅー
  • アクセスできているか確認するのに最も適しているのがs3バケットの作成
    ➞ 作るだけなら金がかからないから
  • s3バケットを作成するConfigurationの最小構成は以下の通り
resource "aws_s3_bucket" "example" {
    bucket = "my-tf-test-bucket"
}
  • "aws_s3_bucket": リソースタイプ
  • `"example": リソース名。極端な話aws側では不必要だが、Terraform側がリソースを一意に識別するために必要。
  • bucket: バケット名。これはaws側で必要な値で、グローバルで一意である必要がある
でんちゅーでんちゅー
  • 一回実行すると、tfファイルを変更してterraform initしても前の値が使用される場合がある
  • これはterraform.tfstateという状態をもつファイルにキャッシュ(?)されているからである
terraform state list #現在の状態リストの確認
terraform state rm <state名> #指定した状態の破棄
  • 上記処理を行った後にterraform initterraform planを実行すると、最新のConfiturationが反映されている
でんちゅーでんちゅー

initやapply実行すると生まれる.tfstateファイルに、現状のインフラ構成が保持されている
➞ ローカルにしかないので複数人でterraformを管理できない
➞ terraform cloudを使用することで解決できる

でんちゅーでんちゅー

Terraform-Cloudでのプロジェクト管理を行うには、ローカルのTerraformで、Terraform-Cloudへのログイン処理をする必要がある。

  1. CLIでterraform loginを実行する
  2. CLIでyesと打つ
  3. 自動でブラウザが開くので、GenerateToken的な青いボタンを押す
  4. アクセストークンが生成されるのでコピーする
  5. CLIに戻り、1回だけペーストする(セキュリティ上ペーストしても何も表示されないので注意!)
  6. Welcome to Terraform Cloud!と出たら成功
でんちゅーでんちゅー

tfstateをs3で管理する

  • tfstateを保存するs3バケットを作成
main.tf
terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "~>4.16"
    }
    required_version = ">= 1.2.0"
  }
}
s3.tf
resource "aws_s3_bucket" "sample_tf_state" {
  bucket = "sample_tf_state"
}

resource "aws_s3_bucket_versioning" "sample_tf_state" {
  bucket = aws_s3_bucket.sample_tf_state.bucket
  versioning_configuration {
    status = "Enabled"
  }
}
  • terraform init, plan, applyを実行
bash
terraform init
terraform plan
terraform apply -auto-approve
  • terraformブロックにbackend項目を追加
main.tf
terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "~>4.16"
    }
    required_version = ">= 1.2.0"
    # ここから追記
    backend "s3" {
      bucket = "sample_tf_state"
      region = "ap-northeast-1"
      key = "terraform.tfstate"
      encrypt = true
    }
    # ここまで追記
  }
}
  • terraform initを実行
bash
terraform init

https://hisuiblog.com/terraform-tfstate-manage-in-s3/

でんちゅーでんちゅー

VPCの構築

terraformファイルの例

vpc.tf
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "sample-vpc"
  }
}

項目説明

resource "aws_vpc" "main" {...}

  • resource: Terraformで管理するリソースを宣言します。リソースはAWSのようなプロバイダーによって提供される、管理したい実体(インスタンス、ネットワークなど)です。
  • aws_vpc: これはAWSプロバイダーが提供するリソースタイプで、AWSのVPC(Virtual Private Cloud)を指します。VPCは分離されたネットワーク空間をAWSクラウド内に提供し、その中でAWSリソースを起動できます。
  • "main": このリソースの名前または識別子です。Terraformの中でこのVPCリソースを参照する際に使用します。これにより、同じタイプの他のリソース(別のVPCなど)と区別できます。

cidr_block = "10.0.0.0/16"

  • cidr_block: VPC内で使用されるIPアドレスの範囲を指定します。この例では、10.0.0.0/16が指定されており、これは10.0.0.0から10.0.255.255までの約65,536個のIPアドレスを含む範囲に相当します。CIDR表記法を使用しています。

tags = { Name = "sample-vpc" }

  • tags: リソースにメタデータを付加するためのキー/値ペアのマップです。ここでは、Nameタグに"sample-vpc"という値を設定しています。これはAWSのコンソールやCLI、APIを通じてリソースを識別しやすくするためによく使用されます。

まとめ

この定義により、指定されたCIDRブロックを持ち、"sample-vpc"という名前のタグが付いたVPCがAWSに作成されます。このVPCはその後、サブネットの作成、セキュリティグループの設定、インターネットゲートウェイの接続など、さらなるネットワーク構成の基盤として使用できます。

でんちゅーでんちゅー

Subnetの構築

terraformファイルの例

subnets.tf
resource "aws_subnet" "public" {
  vpc_id = aws_vpc.main.id
  cidr_block = "10.0.1.0/24"
  availability_zone = "ap-northeast-1a"

  tags = {
    Name = "sample-public-subnet"
  }
}

resource "aws_subnet" "private" {
  vpc_id = aws_vpc.main.id
  cidr_block = "10.0.10.0/24"
  availability_zone = "ap-northeast-1a"

  tags = {
    Name = "sample-private-subnet"
  }
}

項目説明

resource "aws_subnet" "public" {...}

  • resource: Terraformで管理されるリソースを宣言します。ここではAWSのサブネットを作成しています。
  • aws_subnet: AWSプロバイダーが提供するリソースタイプで、VPC内のサブネットを指します。
  • "public": このリソースの名前または識別子です。Terraform内でこのサブネットリソースを参照する際に使用します。

vpc_id = aws_vpc.main.id

  • vpc_id: このサブネットが属するVPCのIDを指定します。ここでは、aws_vpc.main.idを使用して、同じTerraform設定内のaws_vpcリソース(mainと名付けられたVPC)のIDを参照しています。

availability_zone = "ap-northeast-1a"

  • availability_zone: サブネットを配置するAWSのアベイラビリティゾーン(AZ)を指定します。ap-northeast-1aは、アジアパシフィック(東京)リージョンの特定のAZを示します。この値を設定することで、サブネットの物理的な場所がこのAZに固定されます。

tags = { Name = "sample-public-subnet" }

  • tags: サブネットに関連付けられたメタデータ(タグ)をキー/値ペアで指定します。ここではNameタグに"sample-public-subnet"という値を設定しており、これによりサブネットを容易に識別できるようになります。

まとめ

この定義により、指定されたVPC内に、指定されたCIDRブロックとアベイラビリティゾーンに配置されたパブリックサブネットが作成されます。パブリックサブネットは、インターネットゲートウェイを介してインターネットへの直接的なアクセスが可能なサブネットです。サブネットの「パブリック」または「プライベート」の性質は、そのルーティングと、インターネットゲートウェイやNATゲートウェイへの接続によって決まります。

備考

  • Subnet作成時にpublicかprivateかは特に指定しない
  • シンプルにInternet Gatewayとの直接的な繋がりがあるかどうかを自動で判別してpublicかprivateかが決められるため
でんちゅーでんちゅー

Internet Gatewayの構築

terraformファイルの例

igw.tf
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "sample-igw"
  }
}

項目説明

resource "aws_internet_gateway" "main" {...}

  • resource: Terraformによって管理されるリソースを宣言します。このケースでは、AWSのインターネットゲートウェイを作成しています。
  • aws_internet_gateway: これはAWSプロバイダーによって提供されるリソースタイプで、VPCにインターネットアクセス機能を提供するためのインターネットゲートウェイを指します。
  • "main": このリソースの名前または識別子です。Terraform内でこのインターネットゲートウェイリソースを参照する際に使用します。

vpc_id = aws_vpc.main.id

  • vpc_id: このインターネットゲートウェイを関連付けるVPCのIDを指定します。aws_vpc.main.idは、同じTerraform設定内で定義されたaws_vpcリソース(mainと名付けられたVPC)のIDを参照しています。

tags = { Name = "sample-igw" }

  • tags: インターネットゲートウェイに関連付けられたメタデータ(タグ)をキー/値ペアで指定します。この例ではNameタグに"sample-igw"という値を設定しており、AWSのコンソールやCLI、APIを通じてインターネットゲートウェイを容易に識別できるようにしています。

まとめ

  • この定義により、指定されたVPCに"sample-igw"という名前のインターネットゲートウェイが関連付けられ、作成されます。インターネットゲートウェイの存在によって、VPC内のリソース(パブリックサブネット内のリソースなど)はインターネットへの直接的なアクセスが可能になります。これは、外部のインターネットからのアクセスを必要とするウェブサーバーや、外部サービスへのアクセスを必要とするアプリケーションで特に重要です。

備考

  • Internet Gatewayが未接続のvpcは外部との通信ができない。
でんちゅーでんちゅー

Elastic IP & NAT Gatewayの構築

  • NAT Gatewayには1つのElastic IPを紐付ける必要があるため、Elastic IPも同時に作成する

terraformファイルの例

natgw.tf
resource "aws_eip" "eip" {
  vpc = true

  tags = {
    Name = "sample-eip"
  }
}

resource "aws_nat_gateway" "nat" {
  allocation_id = aws_eip.eip.id
  subnet_id = aws_subnet.public.id

  tags = {
    Name = "sample-natgw"
  }
}

Elastic IP 項目説明

resource "aws_eip"

  • resource: Terraformによって管理されるリソースを宣言します。このケースでは、AWSのElastic IP(EIP)を作成しています。
  • "aws_eip": AWSプロバイダーによって提供されるリソースタイプで、静的なIPv4アドレスを提供します。このアドレスはインターネット上で一意に識別可能です。

vpc = true

  • vpc: このプロパティがtrueに設定されると、Elastic IPがVPC用に作成されることを意味します。VPC内のリソースに割り当てることができます。

tags = { Name = "sample-eip" }

  • tags: リソースに関連付けられたメタデータ(タグ)をキー/値ペアで指定します。この例ではNameタグに"sample-eip"という値を設定しており、AWSのコンソールやCLI、APIを通じてElastic IPを容易に識別できるようにしています。

NAT Gateway 項目説明

resource "aws_nat_gateway" "nat" {...}

  • resource: Terraformによって管理されるリソースを宣言します。このケースでは、AWSのNATゲートウェイを作成しています。
  • "aws_nat_gateway" "nat": AWSプロバイダーが提供するリソースタイプで、VPC内のプライベートサブネットからのインターネットアクセスを可能にするためのNATゲートウェイを指します。

subnet_id = aws_subnet.public.id

  • subnet_id: NATゲートウェイを配置するサブネットのIDを指定します。aws_subnet.public.idは、同じTerraform設定内で定義されたパブリックサブネットリソースのIDを参照しています。NATゲートウェイは、インターネットへのアウトバウンド接続を提供するためにパブリックサブネットに配置されます。

allocation_id = aws_eip.eip.id

  • allocation_id: NATゲートウェイに割り当てるElastic IPの割り当てIDを指定します。aws_eip.eip.idは、同じTerraform設定内で定義されたElastic IPリソースのIDを参照しています。

tags = { Name = "sample-natgw" }

  • tags: リソースに関連付けられたメタデータ(タグ)をキー/値ペアで指定します。この例ではNameタグに"sample-natgw"という値を設定しており、AWSのコンソールやCLI、APIを通じてNATゲートウェイを容易に識別できるようにしています。

備考

  • NAT Gatewayはネットワークアドレスを変換するサービス。
  • Private Subnetから外部へ通信するために必要
でんちゅーでんちゅー

Route Tableの構築

  • 通信を疎通させるための経路の構築

  • Route Tableは3つのリソースで構成される

  • aws_route_table

    • 経路情報を格納する箱
  • aws_route

    • 経路情報をaws_route_tableに追加する
  • aws_route_table_association

    • aws_route_tableとaws_subnetを紐づける

Terraformファイルの例

  1. Internet GatewayとPublic Subnetとの経路
route_tables.tf
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "sample-public-rtb"
  }
}

resource "aws_route" "public" {
  destination_cidr_block = "0.0.0.0/0"
  route_table_id = aws_route_table.public.id
  gateway_id = aws_internet_gateway.main.id
}

resource "aws_route_table_association" "public" {
  subnet_id = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}
  1. NAT GatewayとPrivate Subnetとの経路
route_tables.tf
resource "aws_route_table" "private" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "sample-private-rtb"
  }
}

resource "aws_route" "private" {
  destination_cidr_block = "0.0.0.0/0"
  route_table_id = aws_route_table.private.id
  nat_gateway_id = aws_nat_gateway.nat.id
}

resource "aws_route_table_association" "private" {
  subnet_id = aws_subnet.private.id
  route_table_id = aws_route_table.private.id
}

項目説明

aws_route_table

  • 目的:
    • AWS VPC内にパブリックルートテーブルを作成します。
  • パラメータ:
    • vpc_id: ルートテーブルが関連付けられるVPCのIDを指定します。ws_vpc.main.idは、別の場所で定義されたVPCリソースのIDを参照しています。
    • tags: リソースにメタデータ(タグ)を追加します。ここではNameタグにsample-public-rtbという値を設定しています。

aws_route

  • 目的:
    • インターネットへのルート(デフォルトゲートウェイ)をパブリックルートテーブルに追加します。
  • パラメータ:
    • destination_cidr_block: このルートの宛先として指定するCIDRブロック。0.0.0.0/0は全てのIPアドレスを意味し、インターネットへのデフォルトルートを指定します。
    • route_table_id: このルートを追加するルートテーブルのID。aws_route_table.public.idは、上で定義したパブリックルートテーブルのIDを参照しています。
    • gateway_id: このルートが利用するゲートウェイのID。aws_internet_gateway.main.idは、インターネットゲートウェイのリソースIDを参照しており、このゲートウェイを通じてインターネットにアクセスします。

aws_route_table_association

  • 目的:
    • 特定のサブネットをパブリックルートテーブルに関連付けます。これにより、そのサブネット内のインスタンスがインターネットにアクセスできるようになります。
  • パラメータ:
    • subnet_id: 関連付けるサブネットのID。aws_subnet.public.idは、パブリックサブネットのリソースIDを参照しています。
    • route_table_id: 関連付けるルートテーブルのID。この場合、aws_route_table.public.idは先に作成したパブリックルートテーブルのIDを参照しています。
でんちゅーでんちゅー

Internet GatewayとNAT Gatewayの違い

  • Internet Gateway(IGW)とNAT Gateway(NAT GW)は、AWS環境内で異なる目的と機能を持つ2つの重要なコンポーネントです。それぞれの基本的な違いと用途を以下に説明します。

Internet Gateway

  • 目的:Internet Gatewayは、AWS VPCとインターネットとの間のゲートウェイです。これを使用すると、VPC内のインスタンスがインターネットに直接アクセスできるようになります(ただし、適切なセキュリティグループとNACLの設定が必要です)。
  • 機能:
    • VPCに接続されたリソースがインターネットから直接アクセス可能になります。
    • 両方向のインターネットトラフィックを可能にします(受信と送信)。
    • 主にパブリックサブネット内のリソース(例: EC2インスタンス)がインターネットからの入力トラフィックに応答し、インターネットに対して直接トラフィックを送信できるようにするために使用されます。

NAT Gateway

  • 目的:NAT Gatewayは、プライベートサブネット内のインスタンスがインターネットにアクセスできるようにするためのサービスですが、インターネットからこれらのインスタンスに直接アクセスすることはできません。
  • 機能:
    • プライベートサブネット内のリソースがインターネットにアクセス(例えば、ソフトウェアの更新をダウンロード)できるようにしますが、インターネットからの直接的な入力トラフィックは許可しません。
    • 送信トラフィックのみをサポートし、プライベートサブネットのインスタンスに対する直接的な受信トラフィックを許可しません。
    • NAT GWは、複数のインスタンスからのトラフィックを一つのIPアドレス(NAT GWのIP)にマッピングすることで、プライベートサブネット内のインスタンスがインターネットにアクセスする際の中継ポイントとして機能します。

まとめ

  • Internet Gatewayは、VPC内のリソースがインターネットとの双方向通信を行うために使用されます。主にパブリックサブネットで使用され、インスタンスがインターネットに直接公開される場合に適しています。
  • NAT Gatewayは、プライベートサブネット内のインスタンスがインターネットにアクセスするために使用されますが、インターネットからの直接アクセスは防ぎます。主にプライバシーとセキュリティを重視する場合に適しています。
でんちゅーでんちゅー

NACL(Network Access Control List)

  • 定義: NACLは、AWS VPC内のサブネットレベルで動作するオプショナルなレイヤーで、入力および出力トラフィックを制御するためのルールセットです。各サブネットは、VPC内の任意の数のNACLに関連付けることができますが、1つのサブネットが同時に関連付けられるNACLは1つだけです。
  • 機能: NACLはステートレスであり、入力と出力のトラフィックルールは独立しています。つまり、特定の入力ルールにマッチしたトラフィックが許可された場合でも、対応する出力ルールが設定されていないと、レスポンストラフィックがブロックされる可能性があります。
  • 用途: NACLは、特定のIPアドレス範囲からのアクセスを許可または拒否するなど、サブネットレベルでの粗い粒度のアクセス制御に使用されます。これはネットワークセキュリティの一環として機能します。

Cognitoを使用した認証

  • 定義: AWS Cognitoは、ウェブやモバイルアプリケーションに対する認証、認可、ユーザー管理を提供するサービスです。Cognitoを使用すると、ユーザーがサインアップやサインインを行い、安全にリソースにアクセスできるようになります。
  • 機能: Cognitoは、ユーザープールを使用してアプリケーションのユーザー管理を行い、IDプールを使用してAWSリソースへのアクセスを安全に管理します。ユーザーがアプリケーションにサインインすると、Cognitoはトークンを発行し、このトークンを使用してユーザーが認証されます。
  • 用途: Cognitoはアプリケーションレベルでのセキュリティ対策であり、ユーザーが誰であるか(認証)と、ユーザーが何ができるか(認可)を管理します。FastAPIなどのバックエンドでCognitoの認証トークンを検証することで、エンドポイントへのアクセスを制御します。

NACLとCognito認証の違い

  • NACLはネットワークレベルでのアクセス制御を提供し、VPC内のサブネットへのトラフィックフローを管理します。
  • Cognito認証はアプリケーションレベルでのユーザー認証と認可を提供し、ユーザーがAPIなどのリソースに安全にアクセスできるようにします。
  • これらは異なるセキュリティ対策であり、互いに補完的な役割を果たします。- NACLによるネットワークセキュリティと、Cognitoによるアプリケーションセキュリティを組み合わせることで、より堅牢なセキュリティ体制を構築できます。
でんちゅーでんちゅー

環境変数の設定

  • variables.tfファイルを作成する
variables.tf
variable "project" {
  type = string
}

variable "vpc_cidr" {
    type = string
    description = "vpc cidrblock"
}

variable "public_subnet_cidr" {
    type = string
    description = "public subnet cidrblock"
}

variable "private_subnet_cidr" {
  type = string
  description = "private subnet cidrblock"
}
  • .tfvarsファイルを作成する
.tfvars
project = "sapmle-project"
vpc_cidr = "10.0.0.0/16"
public_subnet_cidr = "10.0.1.0/24"
private_subnet_cidr = "10.0.10.0/24"
  • これで.tfvarsファイルからvariables.tfに値が読み込まれ、他のtfファイルで使用できる
  • .tfvars.gitignoreに追加しておく
  • 使う方法はvar.<変数名>でアクセスできる
igw.tf

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = {
      Name = "${var.project}-internet-gateway"
  }
}
でんちゅーでんちゅー

Security Groupの構築

Terraformファイルの例

security_group.tf
resource "aws_security_group" "main" {
  name = "${var.project}-sg"
  description = "security group"
  vpc_id = aws_vpc.main.id

  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 = "${var.project}-sg"
    Project = var.project
  }
}

項目説明

respirce "aws_security_group" "main" {...}

  • aws_security_groupリソースを作成します。"main"はこのリソースの名前(または識別子)です。

name = "${var.project}-sg"

  • name: セキュリティグループの名前を設定します。${var.project}-sgは、var.project変数の値に-sgを追加したものになります。これにより、プロジェクト固有のセキュリティグループ名を動的に生成します。

description = "security group"

  • description: セキュリティグループの説明を設定します。

vpc_id = aws_vpc.main.id

  • vpc_id: このセキュリティグループが所属するVPCのIDを指定します。aws_vpc.main.idは、同じTerraform設定内で定義されているVPCリソースmainのIDを参照します。

ingress {...}

  • ingress: セキュリティグループの入力ルールを定義します。この例では、ポート22(SSH)でのTCPトラフィックを、任意のIPアドレス(0.0.0.0/0)から許可します。

engress {...}

  • egress: セキュリティグループの出力ルールを定義します。この例では、任意のポート(from_port = 0, to_port = 0)での全てのプロトコル(protocol = "-1")に対するトラフィックを、任意のIPアドレス(0.0.0.0/0)へ許可します。これにより、外部への全ての接続が許可されます。

tags = {...}

  • tags: セキュリティグループにタグを付けます。Nameタグにはセキュリティグループの名前として${var.project}-sgが設定され、Projectタグにはvar.projectの値が設定されます。これにより、リソースを分類しやすくなり、管理が容易になります。

まとめ

  • このコードを使用すると、AWS上にセキュリティグループが作成され、特定のVPC内のリソースが外部からのSSH接続を受け入れるように設定されます。また、セキュリティグループのリソースはプロジェクトごとに名前が付けられ、簡単に識別できるようにタグ付けされます。
でんちゅーでんちゅー

EC2にssh接続する用のsshキーの生成

  • ユーザールートディレクトリ直下の.sshディレクトリに移動
cd ~/.ssh
  • 存在しない場合は作成して移動
mkdir .ssh
cd ~/.ssh
  • 以下のコマンドでキーペアを作成する
ssh-keygen -t rsa -f sapmle-ec2-keypair
でんちゅーでんちゅー

AMI IDの取得

  • AMI ID: EC2インスタンスを作成する際に必要なID
  • DataSource機能を使用してIDを取得する
  • 詳細は公式ドキュメントを参照

Terraformコード例

data.tf
data "aws_ami" "amazonlinux" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "architecture"
    values = ["x86_64"]
  }

  filter {
    name   = "root-device-type"
    values = ["ebs"]
  }

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  filter {
    name   = "block-device-mapping.volume-type"
    values = ["gp2"]
  }
}

項目説明

data "aws_ami" "amazonlinux" {...}

  • data "aws_ami" "amazonlinux": aws_amiデータソースを使用して、特定の条件に合致するAmazon Machine Image (AMI)を検索します。このデータソースの結果は、"amazonlinux"という名前で参照できます。

most_recent = true

  • most_recent = true: 複数のAMIが条件に合致する場合、最も最近作成されたAMIを選択します。

owners = ["amazon"]

  • owners = ["amazon"]: AMIの所有者を指定します。この場合、Amazonが所有するAMIのみが検索対象となります。

filter {...}

  • filter: AMIを検索する際のフィルター条件を設定します。
  • 基本的にfilterは複数設定し、上から順に適用されていきます。

まとめ

  • このコードは、最も最近のAmazon Linux 2 AMIで、アーキテクチャがx86_64、ルートデバイスタイプがEBS、仮想化タイプがHVM、ブロックデバイスマッピングのボリュームタイプがgp2であるものを検索しています。
でんちゅーでんちゅー

Public SubnetでのEC2インスタンスの作成

Terraformコード例

ec2.tf
resource "aws_key_pair" "ssh_key" {
  key_name   = "ssh_key"
  public_key = file("~/.ssh/sample-ec2-keypair.pub")
}

resource "aws_instance" "main" {
  ami                         = data.aws_ami.amazonlinux.id
  instance_type               = "t3.micro"
  subnet_id                   = aws_subnet.public.id
  associate_public_ip_address = true
  vpc_security_group_ids      = [aws_security_group.main.id]
  key_name                    = aws_key_pair.ssh_key.key_name

  tags = {
    Name    = "${var.project}-ec2"
    Project = var.project
  }
}

項目説明

resource "aws_instance" "main" {...}

  • aws_instance: AWS上にEC2インスタンスを作成します。

ami = data.aws_ami.amazonlinux.id

  • ami: 使用するAMIのIDを指定します。data.aws_ami.amazonlinux.idは、別途定義したデータソースからAmazon LinuxのAMI IDを取得しています。

instance_type = "t3.micro"

  • instance_type: インスタンスのタイプを指定します。ここでは"t3.micro"を使用しています。

subnet_id = aws_subnet.public.id

  • subnet_id: インスタンスを配置するサブネットのIDを指定します。
  • aws_subnet.public.idは、パブリックサブネットのIDを参照しています。

associate_public_ip_address = true

  • associate_public_ip_address: インスタンスにパブリックIPアドレスを割り当てるかどうかを指定します。trueに設定することで、パブリックIPアドレスが割り当てられます。

vpc_security_group_ids = [aws_security_group.main.id]

  • vpc_security_group_ids: インスタンスに関連付けるセキュリティグループのIDを配列で指定します。aws_security_group.main.idは、別途定義したセキュリティグループのIDを参照しています。

key_name = aws_key_pair.ssh_key.key_name

  • key_name: インスタンスで使用するキーペアの名前を指定します。
  • aws_key_pair.ssh_key.key_nameは、上で定義したキーペアの名前を参照しています。

tags = {...}

  • tags: インスタンスに関連付けるタグをマップで定義します。Nameタグにはプロジェクト名に"-ec2"を追加した値が、Projectタグにはvar.projectの値がそれぞれ設定されます。

まとめ

  • このコードを使用することで、指定された設定でEC2インスタンスを作成し、SSHキーペアを関連付けて、外部からのSSHアクセスを可能にします。また、インスタンスは指定されたパブリックサブネット内に配置され、適切なセキュリティグループが適用されます。
でんちゅーでんちゅー

EC2に接続したらやるべきこと

  • gitのインストール
  • pyenvのインストール
  • ~/.bashrcにpyenvのパスを通す
  • pythonのインストール
  • pipenvのインストール
  • sshキーを生成し、githubと接続可能にする
  • リポジトリのクローン
でんちゅーでんちゅー

terraformで管理せずに手動作成の方がいいものの例

tfstate用のs3バケット

  • terraform destroyコマンドで毎回エラー吐くので消されないが、そもそもdestroyで消えたら元も子もないのでこれは手動作成してterraformの管理下に置かないようにした方が良さそう

route53のホストゾーン

  • ホストゾーンを削除して再作成した場合、世界中のDNSサーバーに反映されるのに最大2
    日間かかってしまう
  • 特に実験・開発段階等で頻繁にterraform destroyする場合などは、消すたびに毎回最大2日間待つ羽目になるので、ホストゾーン自体は手動で作成した方が良い
  • ていうかそもそもドメインを登録したときに作成されるホストゾーンを消さずに参照するのが一番。
  • やらかし記事が無限に出てくるので、みんな踏む道なんだろう。
  • 手動作成したホストゾーンをterraformコードから参照する際は、tfstateにそもそも含まれないので、作成や変更、削除の対象外となる
  • ただ一応削除対象から外すという処理を書きたい場合は以下のようにlifecycleブロックを活用することで実現可能。
route53.tf
resource "aws_route53_zone" "route53_zone" {
  name = var.domain
  force_destroy = false

  tags = {
    Name = "${var.project}-domain"
    Project = var.project
  }

  lifecycle {
    prevent_destroy = true
  }
}