インターネットに接続できない私(EC2)でも Terraform で構築したい

記事タイトルとURLをコピーする

こんにちは、末廣です。 みなさん、普段どのようにして Terraform 環境を構成し、デプロイしているでしょうか?

developer.hashicorp.com

自分の PC、オフィスにある PC、開発用 EC2、tfaction、CodeBuild !?

blog.serverworks.co.jp

いずれの環境でも基本的にはアウトバウンドでインターネットに接続できる状態であるはずです。 では、インターネットに接続できないような環境で可能でしょうか?

本ブログは terraform init ~ terraform apply をインターネットに接続できないオフラインの AWS 環境(EC2)でやってみたものをまとめたものになります。

※ AWS 内のパブリックネットワークは経由する想定です。

構成

以下図が Terraform を実行する AWS 環境の構成図になります。

構成図

VPC を作成した時に何も考えずに作成する Internet Gateway が存在しません。パブリックサブネットも存在しません。 代わりに、EC2 に人がログインするための経路を残すため、Systems Manager のエンドポイントを配置します。

docs.aws.amazon.com

Systems Manager エンドポイント

EC2 は Amazon Linux 2023 を使います。当然この OS には Terraform のパッケージが入っていないため、どこかしらからパッケージをインストールする必要があります。 そのために S3 バケットを配置し、Gateway Endpoint 経由で EC2 が接続できるようにします。

※ オブジェクトを格納する作業自体はインターネット経由で実施します)

Terraform のインストール経路

また、AWS サービスを作成するために、サービスごとのインターフェースエンドポイントも必要となります。

例えば VPC では com.amazonaws.region.ec2 が必要です。また、Terraform AWS プロバイダーは、認証情報の検証のために(terraform planterraform applyする時に)STS API(GetCallerIdentity など)を呼び出すため、com.amazonaws.ap-northeast-1.sts エンドポイントも必要となります。

サービスを作成する時のインターフェースエンドポイント

docs.aws.amazon.com

では、実際に作業していきましょう。

セットアップ

Terraform パッケージのダウンロード

apt-get terraform yum install terraform brew install terraform dnf install terraform … もちろんできません。インターネットに接続できないのです。 binary の zip ファイルを S3 にいれます。

↓↓↓↓ こちらからダウンロード ↓↓↓↓

developer.hashicorp.com

Amazon Linux の x86 で検証したので対象は画像の通りです。

Amazon Linux 用 Terraform zip ファイル

Terraform Provider パッケージのダウンロード

AWS にデプロイするためには provider の plugin も必要となります。

↓↓↓↓ こちらからダウンロード ↓↓↓↓

releases.hashicorp.com

上記と合わせて x86 用の terraform-provider-aws_6.0.0_linux_386.zip をダウンロードします。

マネジメントコンソール等から(ここは御愛嬌)これらの zip ファイルを S3 に格納しましょう。

EC2 での作業

セッションマネージャー経由でログインし、任意のユーザにスイッチします。

zip ファイルのダウンロードと解凍

適当なディレクトリにそれぞれ zip ファイルを S3 からダウンロードします。

$ aws s3 cp s3://terraform-plugins-mirror-xxxxxxxxxxxx/terraform_1.12.2_linux_386.zip ./
# 結果
download: s3://terraform-plugins-mirror-xxxxxxxxxxxx/terraform_1.12.2_linux_386.zip to ./terraform_1.12.2_linux_386.zip
aws s3 cp s3://terraform-plugins-mirror-xxxxxxxxxxxx/terraform-provider-aws_6.0.0_linux_386.zip ./
# 結果
download: s3://terraform-plugins-mirror-xxxxxxxxxxxx/terraform-provider-aws_6.0.0_linux_386.zip to ./terraform-provider-aws_6.0.0_linux_386.zip

unzip します

$ unzip terraform-provider-aws_6.0.0_linux_386.zip
-bash: unzip: command not found

!!!

Amazon Linux 2023 には unzip はいないようです。 でも大丈夫です。Amazon Linux リポジトリは、S3 バケットでホストされているので、ここからインストールできます。

repost.aws

$ sudo dnf install zip
terraform command を打てるまで

terraform_1.12.2_linux_386.zip を解凍すると出現する terraform (binary ファイル) を /usr/local/bin/ 配下に移動します。

ls -la
total 111208
drwxr-xr-x. 3 ec2-user ec2-user       96 Jun 30 13:20 .
drwx------. 5 ec2-user ec2-user      103 Jun 30 13:06 ..
-rw-r--r--. 1 ec2-user ec2-user     4922 Jun 11 10:22 LICENSE.txt
-rwxr-xr-x. 1 ec2-user ec2-user 87740600 Jun 11 10:22 terraform
-rw-r--r--. 1 ec2-user ec2-user 26122131 Jun 25 20:28 terraform_1.12.2_linux_386.zip
$ sudo mv terraform /usr/local/bin/

これで terraform コマンドが実行できるようになったはずです!

$ terraform version
# 結果
Terraform v1.12.2
on linux_386

やりました!

とりあえずこの状態で、いつものごとく試しに providers.tf を作って terraform init してみます。

provider "aws" {
  region = "ap-northeast-1"
}
$ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v6.0.0...
╷
│ Error: Failed to install provider
│
│ Error while installing hashicorp/aws v6.0.0: provider binary not found:
│ could not find executable file starting with terraform-provider-aws

aws provider がないと怒られました。まあ当然です。続けて作業進めましょう。

terraform init が成功するまで

上記エラーの通り provider の binary ファイルがないので既にダウンロード、解凍した provider の binary ファイルを該当ディレクトリを作成し、移動します。

$ ls
LICENSE.txt
terraform-provider-aws_6.0.0_linux_386.zip
terraform-provider-aws_v6.0.0_x5
$ mkdir -p ~/.terraform.d/plugins/registry.terraform.io/hashicorp/aws/6.0.0./linux_386
$ mv terraform-provider-aws_v6.0.0_x5 ~/.terraform.d/plugins/registry.terraform.io/hashicorp/aws/6.0.0./linux_386/

プロバイダーの取得方法を .terraformrc に記載します

developer.hashicorp.com

provider_installation {
    filesystem_mirror {
        path = "/home/ec2-user/.terraform.d/plugins"
    }
    direct {
        exclude = ["registry.terraform.io/hashicorp/*"]
    }
}

これで準備は完了です。

terraform 実行

providers.tf に以下を記載します。

terraform {
    required_providers {
        aws = "6.0.0"
    }
}

init します。

$ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "6.0.0"...
- Installing hashicorp/aws v6.0.0...
- Installed hashicorp/aws v6.0.0 (unauthenticated)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

╷
│ Warning: Incomplete lock file information for providers
│
│ Due to your customized provider installation methods,
│ Terraform was forced to calculate lock file checksums
│ locally for the following providers:
│   - hashicorp/aws
│
│ The current .terraform.lock.hcl file only includes
│ checksums for linux_386, so Terraform running on another
│ platform will fail to install these providers.
│
│ To calculate additional checksums for another platform,
│ run:
│   terraform providers lock -platform=linux_amd64
│ (where linux_amd64 is the platform to generate)
╵
Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

成功!?

何やら警告が出てきていますが、オフラインで実行している現環境では気にしなくてよいでしょう。ほかプラットフォーム(ARM など)でチェックサムに失敗するとの内容ですが、今回はあえて Linux x86 用のものだけをインストールしているため、関係のない内容となっています。

つまり、成功です!

構築してみる

簡単な VPC を作成してみます。vpc.tf を作り…

resource "aws_vpc" "this" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "offline-kara-tsukutta"
  }
}

terraform plan し…

$ terraform plan

Terraform used the selected providers to generate the
following execution plan. Resource actions are indicated with
the following symbols:
  + create

Terraform will perform the following actions:

  # aws_vpc.this will be created
  + resource "aws_vpc" "this" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.0.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_dns_hostnames                 = true
      + enable_dns_support                   = true
      + enable_network_address_usage_metrics = (known after apply)
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + region                               = "ap-northeast-1"
      + tags                                 = {
          + "Name" = "offline-kara-tsukutta"
        }
      + tags_all                             = {
          + "Name" = "offline-kara-tsukutta"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

─────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so
Terraform can't guarantee to take exactly these actions if
you run "terraform apply" now.

terraform apply !

$ terraform apply

Terraform used the selected providers to generate the
following execution plan. Resource actions are indicated with
the following symbols:
  + create

Terraform will perform the following actions:

  # aws_vpc.this will be created
  + resource "aws_vpc" "this" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.0.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_dns_hostnames                 = true
      + enable_dns_support                   = true
      + enable_network_address_usage_metrics = (known after apply)
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + region                               = "ap-northeast-1"
      + tags                                 = {
          + "Name" = "offline-kara-tsukutta"
        }
      + tags_all                             = {
          + "Name" = "offline-kara-tsukutta"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_vpc.this: Creating...
aws_vpc.this: Still creating... [00m10s elapsed]
aws_vpc.this: Creation complete after 11s [id=vpc-0ac3f9edaadcd2422]

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

できました!

最後に注意点

サービスを作成する時に、必要なインターフェースエンドポイントが必要と記載しましたが、かなり重要な注意すべき点があります。

↓ 再掲

サービスを作成する時のインターフェースエンドポイント

docs.aws.amazon.com

com.amazonaws.iam と名のついてるエンドポイントをよく見てください。region がついていません。グローバルサービスであることが理由ですが、これは東京リージョン等で作成することが不可となっています。バージニア北部では問題なく作成できるため、IAM リソースを作成する場合は本環境をバージニア北部で作成する必要があります。

com.amazonaws.iam を作成

また、同様に CloudFront 等のリソースはインターフェースエンドポイントが存在しないため、本環境からの構築は難しいと考えられます。

まとめ

本ブログでは、インターネットに接続できない EC2 環境から Terraform のセットアップ、実行をしてみました。

最後の記載した点の制約がある部分や、Terraform アップデート運用が大変であるという点から、あまり実用的ではない構成ではあるとは思います。 要件としては少ないかもしれませんが、最低限の構成をを最大のセキュリティで実施することはできるでしょう。

末廣 満希(執筆記事の一覧)

2022年新卒入社です。ここに何かかっこいい一言を書くことができるエンジニアになれるように頑張ります。