

こちらの記事では、普段はあまり意識することのないインスタンスプロファイルについて解説します。
インスタンスプロファイルは、TerraformなどのIaCツールを使用してEC2を構築する際に、必ず意識しなければならない重要な要素です。
AWSのマネジメントコンソールからIAM Roleを作成する場合、インスタンスプロファイルは自動で作成されるため普段意識することはありません。TerraformなどのIaCツールを使用する場合、インスタンスプロファイルを明示的に作成しないとEC2インスタンスが構築できません。
本記事では、インスタンスプロファイルとは何か?について説明し、Terraformでの構築手順を詳しく解説します。
Terraformの基本
はじめにTerraformの基本について解説します。
Terraformは、インフラ環境をコードで定義して構築するIaC(Infrastructure as Code)ツールの一つで、アメリカのソフトウェア企業であるHashiCorp社が開発したオープンソースツールです。HCL(HashiCorp Configuration Language)と呼ばれる独自の言語を使用しており、JSONやYAMLと同じような形式で記述をすることができ、読みやすく保守しやすいといった特徴があります。
本記事の後半にはTerraformを使用してインスタンスプロファイルの作成などを行います。Terraformの環境構築については、以下の記事で解説しています。
https://envader.plus/article/162
インスタンスプロファイルとは?

インスタンスプロファイルとは、EC2インスタンスの起動時にアタッチしたいIAM Roleの情報を起動するインスタンスに渡す役割を果たします。公式ドキュメントでは、インスタンスプロファイルはIAM Roleのコンテナであると表現されています。
マネジメントコンソールからIAM Roleを作成した場合、インスタンスプロファイルは作成したIAM Roleと同じ名前のインスタンスプロファイルを作成します。そのため、インスタンスプロファイルを普段は意識することが少ないです。
AWS Management Console を使用して Amazon EC2 のロールを作成する場合、コンソールはインスタンスプロファイルを自動的に作成し、そのインスタンスプロファイルにロールと同じ名前を付けます。
IaCではインスタンスプロファイルを個別に作成する必要がある
自動的にインスタンスプロファイルを作成してくれる機能は非常に便利ですが、注意しなければいけません。自動でインスタンスプロファイルを作成してくれるのは、あくまでマネジメントコンソールでIAM Roleを作成した場合のみです。
AWS CLIやIaCツールを使用してIAM Roleを作成し、EC2インスタンスへアタッチするためには明示的にインスタンスプロファイルを作成する必要があります。この点を見落とし、TerraformなどのIaCツールからIAM RoleをアタッチしてEC2インスタンスを起動しようとするとエラーが発生します。
AWS CLIまたはAWS API からロールを管理する場合、ロールとインスタンスプロファイルを別個のアクションとして作成します。ロールとインスタンスプロファイルには異なる名前を使用できるため、インスタンスプロファイルの名前とこれらのプロファイルに含まれるロールの名前を知っている必要があります。これにより、EC2 インスタンスを起動するときに適切なインスタンスプロファイルを選択できます。
出典: インスタンスプロファイルの管理 (AWS CLI または AWS API)
Terraformハンズオン

ここからはTerraformを使って、インスタンスプロファイル、EC2インスタンスを作成します。
今回のファイル構成
今回解説していくTerraformのファイル構成は、理解しやすいようリソースに関する記述はmain.tfの中に全てまとめています。Terraformとプロバイダーの設定のみversions.tfとしてファイルを分けて管理しています。
versions.tfの作成
versions.tfを作成し、以下の内容を記述します。
# versions.tf
terraform {
  required_version = ">= 1.0.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.3.0"
    }
  }
}
provider "aws" {
  region = "ap-northeast-1"
}main.tfファイルの全容
はじめにファイルの全容を記載します。
# main.tf
# ------------------------------
# locals variable
# ------------------------------
locals {
  role_name     = "ec2ssm-role"
  instance_type = "t2.micro"
  vpc_cidr      = "10.0.0.0/16"
  subnet_cidr   = "10.0.1.0/24"
}
# ------------------------------
# Refer to the Data Sources
# ------------------------------
# IAM Policy Document
data "aws_iam_policy_document" "assumerole_ec2" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }
  }
}
# EC2 AMI
data "aws_ami" "amlx2_latest" {
  owners      = ["amazon"]
  most_recent = true
  filter {
    name = "name"
    values = ["amzn2-ami-hvm-2.0.*.0-x86_64-gp2"]
  }
}
# ------------------------------
# VPC
# ------------------------------
resource "aws_vpc" "main" {
  cidr_block           = local.vpc_cidr
  enable_dns_support   = true
  enable_dns_hostnames = true
  tags = {
    Name = "my_vpc"
  }
}
# ------------------------------
# IGW
# ------------------------------
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.main.id
  tags = {
    Name = "main-igw"
  }
}
# ------------------------------
# Subnet
# ------------------------------
resource "aws_subnet" "public" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = local.subnet_cidr
  availability_zone = "ap-northeast-1a"
  tags = {
    Name = "public-subnet"
  }
}
# ------------------------------
# Route Table
# ------------------------------
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }
  tags = {
    Name = "public-route-table"
  }
}
resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}
# ------------------------------
# IAM Role
# ------------------------------
resource "aws_iam_role" "ssm" {
  name               = local.role_name
  path               = "/"
  assume_role_policy = data.aws_iam_policy_document.assumerole_ec2.json
  tags = {
    Roles = "ssm"
  }
}
resource "aws_iam_role_policy_attachment" "ssm" {
  role       = aws_iam_role.ssm.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
# ------------------------------
# Instance Profile
# ------------------------------
resource "aws_iam_instance_profile" "ssm" {
  name = local.role_name
  role = aws_iam_role.ssm.name
  tags = {
    Roles = "ssm"
  }
}
# ------------------------------
# EC2
# ------------------------------
resource "aws_instance" "main" {
  ami                         = data.aws_ami.amlx2_latest.id
  instance_type               = local.instance_type
  iam_instance_profile        = aws_iam_role.ssm.name
  subnet_id                   = aws_subnet.public.id
  vpc_security_group_ids      = [aws_security_group.allow_ssm.id]
  associate_public_ip_address = true
  tags = {
    Name = "main-ec2"
  }
}
# ------------------------------
# Security Group
# ------------------------------
resource "aws_security_group" "allow_ssm" {
  name        = "allow_ssm"
  description = "Allow SSM connections"
  vpc_id      = aws_vpc.main.id
  egress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  tags = {
    Name = "allow-ssm-sg"
  }
}locals変数
locals {
  role_name     = "ec2ssm-role"
  instance_type = "t2.micro"
  vpc_cidr      = "10.0.0.0/16"
  subnet_cidr   = "10.0.1.0/24"
}main.tfファイルで使用するlocal変数を定義しています。Terraformの変数にはinput、local、outputの3つがありますが、今回はlocal変数で指定します。変数については以下の記事にて解説しています。
https://envader.plus/article/190
https://envader.plus/article/191
インスタンスプロファイルの作成
今回インスタンスプロファイルは以下のように定義しました。
resource "aws_iam_instance_profile" "ssm" {
  name = local.role_name
  role = aws_iam_role.ssm.name
  tags = {
    Roles = "ssm"
  }
}aws_iam_instance_profileを定義することで、インスタンスプロファイルを作成することができます。roleで後述するIAM Role名を指定しています。
TerraformでEC2インスタンスにIAM Roleをアタッチする場合、このように明示的にインスタンスプロファイルを作成しないとplanは通るがapplyしたときにエラーが発生する。という事態におちいるため注意が必要です。
インスタンスプロファイルを作成しなかった場合、以下のエラーが発生します。
Error: creating EC2 Instance: InvalidParameterValue: Value (ec2ssm-role) for parameter iamInstanceProfile.name is invalid. Invalid IAM Instance Profile name
│ 	status code: 400, request id: 0cdac60b-0ce6-42d4-a3f9-409e8d938e8b
│
│   with aws_instance.main,
│   on main.tf line 134, in resource "aws_instance" "main":
│  134: resource "aws_instance" "main" {IAM Roleの作成
IAM Roleを作成します。
resource "aws_iam_role" "ssm" {
  name               = local.role_name
  path               = "/"
  assume_role_policy = data.aws_iam_policy_document.assumerole_ec2.json
  tags = {
    Roles = "ssm"
  }
}assume_role_policyでは、このIAM Roleを引き受けることができる対象を定義しています。今回はdataブロックのidentifiersで定義したEC2を対象として指定しています。
data "aws_iam_policy_document" "assumerole_ec2" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }
  }
}IAM PolicyをIAM Roleへアタッチする
続いてIAM Policyをアタッチします。IAM Policyを作成したIAM Roleにアタッチするには、aws_iam_role_policy_attachmentを使用します。
resource "aws_iam_role_policy_attachment" "ssm" {
  role       = aws_iam_role.ssm.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}roleの部分でどのIAM Roleにアタッチするかを指定しています。policy_arnではアタッチされるIAM PolicyのARNを指定しています。今回はAWS Systems Managerを利用して、EC2を管理するために必要なポリシーをアタッチしています。
IAM Roleの基本については以下の記事にて解説しています。
https://envader.plus/article/246
また応用的な考え方や、TerraformでのIAM Role、IAM Policyの作成方法については以下の記事をご覧ください。
https://envader.plus/article/247
EC2の作成
EC2を作成します。
resource "aws_instance" "main" {
  ami                         = data.aws_ami.amlx2_latest.id
  instance_type               = local.instance_type
  iam_instance_profile        = aws_iam_role.ssm.name
  subnet_id                   = aws_subnet.public.id
  vpc_security_group_ids      = [aws_security_group.allow_ssm.id]
  associate_public_ip_address = true
  tags = {
    Name = "main-ec2"
  }
}ここでは、iam_instance_profileでアタッチしたいIAM Roleを指定しています。
今回はSSMで管理できるインスタンスを作成するため、associate_public_ip_addressをtrueに設定しパブリックIPアドレスを付与しています。
TerraformでEC2を作成する際には、associate_public_ip_addressをtrueに設定しないとパブリックIPアドレスが付与されません。IAM RoleをインスタンスにアタッチしてもSSM接続できなくなってしまうため注意が必要です。
また、SSMをEC2インスタンスで利用する場合、セキュリティグループはアウトバウンドの443ポートを許可する必要があります。そのため、今回はインバウンドルールは設定せずにアウトバウンドの443ポートのみを許可しています。
resource "aws_security_group" "allow_ssm" {
  name        = "allow_ssm"
  description = "Allow SSM connections"
  vpc_id      = aws_vpc.main.id
  egress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  tags = {
    Name = "allow-ssm-sg"
  }
}まとめ
コンソール上で環境構築をする際には意識することのないインスタンスプロファイルですが、IaCで環境構築する際には明示的に作成する必要があります。
TerraformなどのIaCツールを使うことで、今まで意識しなくても良かったことに気付けることもあるため、こういった気付きもまた楽しみの一つではないでしょうか?
今回のサンプルコードを元に、インスタンスプロファイルを作成する場合と作成しない場合の比較を行ってみるのも楽しいと思います。
【番外編】USBも知らなかった私が独学でプログラミングを勉強してGAFAに入社するまでの話

プログラミング塾に半年通えば、一人前になれると思っているあなた。それ、勘違いですよ。「なぜ間違いなの?」「正しい勉強法とは何なの?」ITを学び始める全ての人に知って欲しい。そう思って書きました。是非読んでみてください。
「フリーランスエンジニア」
近年やっと世間に浸透した言葉だ。ひと昔まえ、終身雇用は当たり前で、大企業に就職することは一種のステータスだった。しかし、そんな時代も終わり「優秀な人材は転職する」ことが当たり前の時代となる。フリーランスエンジニアに高価値が付く現在、ネットを見ると「未経験でも年収400万以上」などと書いてある。これに釣られて、多くの人がフリーランスになろうとITの世界に入ってきている。私もその中の1人だ。数年前、USBも知らない状態からITの世界に没入し、そこから約2年間、毎日勉学を行なった。他人の何十倍も努力した。そして、企業研修やIT塾で数多くの受講生の指導経験も得た。そこで私は、伸びるエンジニアとそうでないエンジニアをたくさん見てきた。そして、稼げるエンジニア、稼げないエンジニアを見てきた。
「成功する人とそうでない人の違いは何か?」
私が出した答えは、「量産型エンジニアか否か」である。今のエンジニア市場には、量産型エンジニアが溢れている!!ここでの量産型エンジニアの定義は以下の通りである。
比較的簡単に学習可能なWebフレームワーク(WordPress, Rails)やPython等の知識はあるが、ITの基本概念を理解していないため、単調な作業しかこなすことができないエンジニアのこと。
多くの人がフリーランスエンジニアを目指す時代に中途半端な知識や技術力でこの世界に飛び込むと返って過酷な労働条件で働くことになる。そこで、エンジニアを目指すあなたがどう学習していくべきかを私の経験を交えて書こうと思った。続きはこちらから、、、、
エンベーダー編集部
エンベーダーは、ITスクールRareTECHのインフラ学習教材として誕生しました。 「遊びながらインフラエンジニアへ」をコンセプトに、インフラへの学習ハードルを下げるツールとして運営されています。

関連記事

2024.03.25
AWSとAzureのストレージサービス徹底比較
本記事では、AWSとAzureが提供するストレージサービスの違いを明らかにし、各サービスの特徴、適用シナリオ、利点を解説します。クラウドストレージの初心者や選定を行う方々向けに、プロジェクトやビジネスニーズに最適なストレージ選択の決定に役立つ知識を提供します。
- AWS
- Azure

2024.02.07
AWSでLogstashの環境構築と使い方を学ぶ
Logstashという強力なツールを用いて、効果的なログ収集、加工、および可視化の方法を学びます。AWS上でのLogstashの設定から、基本的な使い方まで、実践的な知識を提供することを目指しています。
- インフラエンジニア
- AWS

2024.09.29
GitHub ActionsでAWSへのCI/CDを実現!初心者向けハンズオン
本記事では、CI/CDの基本概念と、それを簡単に実現できるツール GitHub Actionsを使って、実際にAWSにアプリケーションをデプロイする手順を紹介します。
- AWS
- ハンズオン

2023.12.26
AWS Route 53とは?DNSの基本も解説
この記事では、インターネット上で不可欠な役割を果たす「DNS(Domain Name System)」と、Amazon Web Services(AWS)が提供するDNSサービス「Route 53」について解説します。
- AWS


