インターネット接続なしでTerraformを動かす実践テクニック 〜 プライベート環境のIaC環境構築 〜 

# はじめに
- Terraformは、2025年3月20日現在、hashicorp社(Terraform開発元)公式として37個のProviderを提供しています。
  - [Terraform Providers](https://registry.terraform.io/browse/providers)
- 多くはパブリッククラウドに対してリソースを作成するもののため、Terraform実行環境からインターネットを経由して、各種パブリッククラウドにアクセスする必要があります。
- しかし一部Providerに関しては、Terraform実行環境から、リソースを作成する環境に対してインターネットを経由してアクセスができない、何ならTerraformを実行したい環境自体がインターネットアクセスができない、ということがしばしば起こります。
- 例えば以下Providerが、そういったインターネットアクセスができない環境で利用される可能性があると考えています。
  - [vsphere](https://registry.terraform.io/providers/hashicorp/vsphere/latest)
  - [kubernetes](https://registry.terraform.io/providers/hashicorp/kubernetes/latest)
  - [helm](https://registry.terraform.io/providers/hashicorp/helm/latest)
- Terraformを実行したい環境がインターネットアクセスができない場合、Terraformを実行することは諦めるしか無いのでしょうか？
- **いいえ、そんなことはありません。希望はあります。** 
- 本記事では、RHEL8をプライベート環境に構築し、Terraformの実行環境を用意する手順を紹介します。
# Terraformを利用するために
## Terraformを利用するためのステップ
- Terraformを利用するためには、以下のステップが必要になります。
  1. ★Terraformを実行したい環境にTerraformをインストールする
  2. 設定ファイル(.tf)を用意する
  3. ★"terraform init"を実行する
  4. "terraform plan"を実行する
  5. "terraform apply"を実行する
- 上記ステップのうち、★を付けた箇所が、 **本来インターネットからパッケージやファイルを取得する必要がある工程** となります。
- 逆に言うと、工程4-5に関しては、Terraformで操作する環境(例えばvsphereであればvCenterやESXi、kubernetesやhelmであればkubernetesクラスタ)にアクセスできればよい、ということになります。
- 工程2に関しては、、、、、各自で頑張りましょう٩( 'ω' )و
## Terraformをインターネットアクセスができない環境で利用するための手順概要
- 難しいことはしていません。
- 本来インターネットからコマンドで取得するパッケージやファイルを、インターネットにアクセスできる環境で取得しておき、Terraformを実行したい環境に転送するだけです。
- 今回、ローカルPCで必要パッケージやファイルを取得しておき、S3バケットを経由して、インターネットアクセスができない環境(プライベートサブネット上のEC2)に配置するという手順をご紹介します。
# 本記事で紹介する環境
![private.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1276335/b990be17-4640-4f58-b522-d686de3b5a7d.png)
# プライベート環境でTerraform実行環境を用意する手順
## 概要
※今回はAWSにEC2インスタンスをプライベートに作成するという都合上、S3にファイル配置、aws cliでコピーを行っていますが、オンプレミス環境で再現する場合はscpなどをご使用ください。

1. 環境構築
    - プライベート環境をAWSに構築しています。
    - 構築はTerraformで行っていますが、これはインターネットアクセス可能な環境から実行する必要があります。
2. (オプション)aws cliをRHEL8にインストールする
    - 利用するAMIにaws cliがプリインストールされていなかったため、インストール手順を追加しています。
    - インストール済みの環境を利用する場合は、skipいただいて問題ありません。
3. TerraformをRHEL8にインストールする
4. "terraform init"を実行する
## Step1.環境構築
- 今回環境作成のために使用した設定ファイルは以下です
    <details><summary>RHEL8環境構築</summary>

    ```hcl
    terraform {
    required_providers {
        aws = {
        source = "hashicorp/aws"
        version = "5.91.0"
        }
    }
    }

    provider "aws" {
    region = "ap-northeast-1"
    }

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

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

    resource "aws_route_table" "private" {
    vpc_id = aws_vpc.main.id
    }

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

    # Endpoint
    ## Endpoint for S3 gateway endpoint
    resource "aws_vpc_endpoint" "s3" {
    vpc_id          = aws_vpc.main.id
    service_name    = "com.amazonaws.ap-northeast-1.s3"
    route_table_ids = [aws_route_table.private.id]
    vpc_endpoint_type = "Gateway"
    }

    ## Interface endpoint for SSM
    resource "aws_vpc_endpoint" "ssm" {
    vpc_id            = aws_vpc.main.id
    service_name      = "com.amazonaws.ap-northeast-1.ssm"
    vpc_endpoint_type = "Interface"
    subnet_ids        = [aws_subnet.private.id]
    security_group_ids = [aws_security_group.ssm_endpoint.id]
    private_dns_enabled = true
    }

    ## VPC endpoint for SSM messages
    resource "aws_vpc_endpoint" "ssmmessages" {
    vpc_id            = aws_vpc.main.id
    service_name      = "com.amazonaws.ap-northeast-1.ssmmessages"
    vpc_endpoint_type = "Interface"
    subnet_ids        = [aws_subnet.private.id]
    security_group_ids = [aws_security_group.ssm_endpoint.id]
    private_dns_enabled = true
    }

    # security group
    resource "aws_security_group" "ssm_endpoint" {
    name        = "ssm-endpoint-sg"
    description = "Allow TLS inbound traffic for SSM endpoints"
    vpc_id      = aws_vpc.main.id

    ingress {
        description = "TLS from VPC"
        from_port   = 443
        to_port     = 443
        protocol    = "tcp"
        cidr_blocks = [aws_vpc.main.cidr_block]
    }

    egress {
        from_port   = 0
        to_port     = 0
        protocol    = "-1"
        cidr_blocks = ["0.0.0.0/0"]
    }
    }

    # security group for EC2 instance
    resource "aws_security_group" "ec2_sg" {
    name        = "ec2-sg"
    description = "Security group for EC2 instance"
    vpc_id      = aws_vpc.main.id

    egress {
        from_port   = 0
        to_port     = 0
        protocol    = "-1"
        cidr_blocks = ["0.0.0.0/0"]
    }
    }

    # authorize
    ## SSM IAM role
    resource "aws_iam_role" "ssm_role" {
    name = "ec2-ssm-role"

    assume_role_policy = jsonencode({
        Version = "2012-10-17"
        Statement = [
        {
            Action = "sts:AssumeRole"
            Effect = "Allow"
            Principal = {
            Service = "ec2.amazonaws.com"
            }
        }
        ]
    })
    }

    # attach policies
    resource "aws_iam_role_policy_attachment" "ssm_policy" {
    role       = aws_iam_role.ssm_role.name
    policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
    }

    ## policy for S3 access
    resource "aws_iam_role_policy_attachment" "s3_policy" {
    role       = aws_iam_role.ssm_role.name
    policy_arn = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
    }

    # Instance profile for EC2
    resource "aws_iam_instance_profile" "ssm_profile" {
    name = "ec2-ssm-profile"
    role = aws_iam_role.ssm_role.name
    }

    # EC2 isntance
    ## create EC2 instance
    resource "aws_instance" "main" {
        ami                    = "ami-062921cd4e5cbddc1" # RHEL-8.9.0_HVM-20240327-x86_64-4-Hourly2-GP3
    instance_type          = "t2.micro"
    subnet_id              = aws_subnet.private.id
    vpc_security_group_ids = [aws_security_group.ec2_sg.id]
    iam_instance_profile   = aws_iam_instance_profile.ssm_profile.name

    root_block_device {
        volume_size = 50
        volume_type = "gp3"
    }

    user_data = <<-EOF
                #!/bin/bash
                cd /tmp
                sudo dnf --disablerepo="*" install -y https://s3.ap-northeast-1.amazonaws.com/amazon-ssm-ap-northeast-1/latest/linux_amd64/amazon-ssm-agent.rpm
                sudo systemctl enable amazon-ssm-agent
                sudo systemctl start amazon-ssm-agent
                EOF
    }

    # S3 bucket
    resource "aws_s3_bucket" "main" {
    bucket = "terraform-private-env"
    force_destroy = true

    tags = {
        Name        = "My bucket"
        Environment = "Dev"
    }
    }

    # output
    output "ec2_instance_id" {
    description = "EC2 Instance ID"
    value       = aws_instance.main.id
    }

    output "s3_bucket_name" {
    description = "S3 Bucket Name"
    value       = aws_s3_bucket.main.bucket
    }
    ```

    </details>
- 事前準備ファイル一覧
  - [Terraformインストーラー DLページ](https://releases.hashicorp.com/terraform/)
    - 今回は2025年3月20日現在GAとなっている最新版である、以下 1.11.2のlinux版を使用します。
      - [terraform_1.11.2_linux_amd64.zip](https://releases.hashicorp.com/terraform/1.11.2/terraform_1.11.2_linux_amd64.zip)
  - [providerファイル DLページ](https://releases.hashicorp.com/)
    - 今回はvsphereの2025年3月20日現在最新版である、以下 2.11.1のlinux版を使用します。
      - [terraform-provider-vsphere_2.11.1_linux_amd64.zip](https://releases.hashicorp.com/terraform-provider-vsphere/2.11.1/terraform-provider-vsphere_2.11.1_linux_amd64.zip)
  - [aws cliインストーラー DLページ](https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip)
## Step2. (オプション)aws cliをRHEL8にインストールする
1. "awscli-exe-linux-x86_64.zip"をS3にアップロード
2. 【CloudShellで操作】以下コマンドを実行し、ダウンロード
    - コマンド

        ```shell
        cd /tmp
        aws s3 cp s3://terraform-private-env/awscli-exe-linux-x86_64.zip .
        ```

    - 実行例

        ```shell
        ~ $ cd /tmp
        tmp $ aws s3 cp s3://terraform-private-env/awscli-exe-linux-x86_64.zip .
        download: s3://terraform-private-env/awscli-exe-linux-x86_64.zip to ./awscli-exe-linux-x86_64.zip
        tmp $ 
        ```

3. 【CloudShellで操作】zipの解凍とtar.gzで圧縮
    - コマンド

        ```shell
        unzip awscli-exe-linux-x86_64.zip
        tar -zcvf awscli-exe.tar.gz aws
        ```

    - 実行例

        ```shell
        tmp $ unzip awscli-exe-linux-x86_64.zip
        (略)
        tmp $ tar -zcvf awscli-exe.tar.gz aws
        (略)
        tmp $ ls -l awscli-exe.tar.gz
        -rw-r--r--. 1 cloudshell-user cloudshell-user 64947201 Mar 20 09:42 awscli-exe.tar.gz
        tmp $  
        ```

4. 【CloudShellで操作】S3バケットへのアップロードと署名付きURLの発行
    - ※presignコマンドのデフォルト、1時間の有効期限でURLを発行しています。
    - コマンド

        ```shell
        aws s3 cp awscli-exe.tar.gz s3://terraform-private-env/
        aws s3 presign s3://terraform-private-env/awscli-exe.tar.gz --region ap-northeast-1
        ```

    - 実行例

        ```shell
        tmp $ aws s3 cp awscli-exe.tar.gz s3://terraform-private-env/
        upload: ./awscli-exe.tar.gz to s3://terraform-private-env/awscli-exe.tar.gz
        tmp $ aws s3 presign s3://terraform-private-env/awscli-exe.tar.gz --region ap-northeast-1
        https://terraform-private-env.s3.ap-northeast-1.amazonaws.com/awscli-exe.tar.gz?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIA3VAYNP5DO3EQLTSQ%2F20250320%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Date=20250320T094318Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEDIaDmFwLW5vcnRoZWFzdC0xIkcwRQIgaH7EuX%2BuuRHg6xAt9KqcSfMPp65STXrOcURlJdq5Am8CIQC2l3scYzgwC5axO7xyq2I7R1NnAGAPjRhriOcdBJu6xyqgAwiL%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8BEAQaDDgwMTA2MjYxNjkwMiIM7pNjE3FG5Iz%2BWyr0KvQCP3FxM5H%2FoDh3nOrSslcypLaKDNv7H7Ky1u%2B0fzbkCubsvAgZ8Sz2UTKoyR1tkSaPI3eQhWNB4qcVwBVwbIuTORgXvs9f1jEf4aXXv6bF1tum8PQrfjiByMUTxvJQefqa5MpoDk%2FyiXQRZ4aDoiaL1YYBJDNrRefKp8aevFjrGjY%2B%2FSTX5FpHSdYm1Mm7sLl2hfcWCYlvGviCGG1vC08%2FkKTG1QbvhjMlzk5B%2FRQz0%2FtM3OGysRuym4N8CAi%2B%2ByqFrAQDkU6uOuczazJ37zDhhU9D7e1i73EAVsivbmVoe6dAU9r%2Br2lwOIM%2FmUFiw1bZqpAMmpCJMLGng2nURctIjZvso4%2FVAIza%2BlJIFn6fk%2BoXdwuXR3qh3rCPuDWygGz7Y58dmUoEC1In7aHbPaR3xnhKAwD6uJgZ%2Bd2MuePm0uyYkhR6RpNgAHXyKlQ5TXzVpOJEvMoYga%2BQD9tw5HKujpiP7pwCZMNhRYw88bTfj88enlAFMMih7r4GOrMC%2BMh9lMxbffQmrXO%2F681Y5xhBLYJ2lBt67FUegexuRcP2s3vlM7e5ALYsVgsxMWE7xUdlVIKKDgLbpHCtIz4ZCBYGTdd0h7PGawu3FmHMZQzKtJNypFp0wLel1dyDDdJ7T%2BQ%2B5Vuv9OCqAU58DKMxNRmg%2FV%2FtKUyEnkGi%2FC0Nj6xWTOFYf9yLa5FWvn1GhOnX6gA%2BBHifLepAId5mELQXjZnqal7U5SrbD9351i8UxGcQwlDQbh3%2BqBO3prPh3cRNxXFMDXeoTriz0nNQIUSCKNWTm3n0EvNT6UrqWDqBkumBT8kfn0j3Zil1eLHqAkMz8GpH9YR5X9iAVk1TGt0h2Gernno1cjngPQB4SgJcurvHV8ww7eWTfJYPt4PXTF%2FwgDHC3HIjCdH6TKF97fOlb0NpGA%3D%3D&X-Amz-Signature=6386fec9666fe396e7f76f2de659b00c18585dba26406e5c2147bbb67c4d2107
        tmp $ 
        ```

6. ssmでEC2インスタンスにアクセス
7. 【EC2で操作】aws-cliのダウンロードと解凍
    - コマンド

        ```shell
        bash
        cd
        sudo curl "<工程4で作成したaws cli用署名付きURL>" -o awscli-exe.tar.gz
        tar -zxvf awscli-exe.tar.gz
        ```

    - 実行例

        ```shell
        ssm-user@ip-10-0-0-115 bin]$ cd
        [ssm-user@ip-10-0-0-115 ~]$ ll
        total 0
        [ssm-user@ip-10-0-0-115 ~]$ sudo curl "https://terraform-private-env.s3.ap-northeast-1.amazonaws.com/awscli-exe.tar.gz?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIA3VAYNP5DO3EQLTSQ%2F20250320%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Date=20250320T094318Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEDIaDmFwLW5vcnRoZWFzdC0xIkcwRQIgaH7EuX%2BuuRHg6xAt9KqcSfMPp65STXrOcURlJdq5Am8CIQC2l3scYzgwC5axO7xyq2I7R1NnAGAPjRhriOcdBJu6xyqgAwiL%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8BEAQaDDgwMTA2MjYxNjkwMiIM7pNjE3FG5Iz%2BWyr0KvQCP3FxM5H%2FoDh3nOrSslcypLaKDNv7H7Ky1u%2B0fzbkCubsvAgZ8Sz2UTKoyR1tkSaPI3eQhWNB4qcVwBVwbIuTORgXvs9f1jEf4aXXv6bF1tum8PQrfjiByMUTxvJQefqa5MpoDk%2FyiXQRZ4aDoiaL1YYBJDNrRefKp8aevFjrGjY%2B%2FSTX5FpHSdYm1Mm7sLl2hfcWCYlvGviCGG1vC08%2FkKTG1QbvhjMlzk5B%2FRQz0%2FtM3OGysRuym4N8CAi%2B%2ByqFrAQDkU6uOuczazJ37zDhhU9D7e1i73EAVsivbmVoe6dAU9r%2Br2lwOIM%2FmUFiw1bZqpAMmpCJMLGng2nURctIjZvso4%2FVAIza%2BlJIFn6fk%2BoXdwuXR3qh3rCPuDWygGz7Y58dmUoEC1In7aHbPaR3xnhKAwD6uJgZ%2Bd2MuePm0uyYkhR6RpNgAHXyKlQ5TXzVpOJEvMoYga%2BQD9tw5HKujpiP7pwCZMNhRYw88bTfj88enlAFMMih7r4GOrMC%2BMh9lMxbffQmrXO%2F681Y5xhBLYJ2lBt67FUegexuRcP2s3vlM7e5ALYsVgsxMWE7xUdlVIKKDgLbpHCtIz4ZCBYGTdd0h7PGawu3FmHMZQzKtJNypFp0wLel1dyDDdJ7T%2BQ%2B5Vuv9OCqAU58DKMxNRmg%2FV%2FtKUyEnkGi%2FC0Nj6xWTOFYf9yLa5FWvn1GhOnX6gA%2BBHifLepAId5mELQXjZnqal7U5SrbD9351i8UxGcQwlDQbh3%2BqBO3prPh3cRNxXFMDXeoTriz0nNQIUSCKNWTm3n0EvNT6UrqWDqBkumBT8kfn0j3Zil1eLHqAkMz8GpH9YR5X9iAVk1TGt0h2Gernno1cjngPQB4SgJcurvHV8ww7eWTfJYPt4PXTF%2FwgDHC3HIjCdH6TKF97fOlb0NpGA%3D%3D&X-Amz-Signature=6386fec9666fe396e7f76f2de659b00c18585dba26406e5c2147bbb67c4d2107" -o awscli-exe.tar.gz
        % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                        Dload  Upload   Total   Spent    Left  Speed
        100 61.9M  100 61.9M    0     0  82.0M      0 --:--:-- --:--:-- --:--:-- 81.9M
        [ssm-user@ip-10-0-0-115 ~]$ tar -zxvf awscli-exe.tar.gz
        (略)
        [ssm-user@ip-10-0-0-115 ~]$ 
        ```

8. 【EC2で操作】aws-cliのインストール
    - コマンド

        ```shell
        sudo ./aws/install
        aws --version
        ```

    - 実行例

        ```shell
        [ssm-user@ip-10-0-0-115 ~]$ sudo ./aws/install
        You can now run: /usr/local/bin/aws --version
        [ssm-user@ip-10-0-0-115 ~]$
        [ssm-user@ip-10-0-0-115 ~]$ aws --version
        aws-cli/2.24.27 Python/3.12.9 Linux/4.18.0-513.24.1.el8_9.x86_64 exe/x86_64.rhel.8
        [ssm-user@ip-10-0-0-115 ~]$
        ```

## Step3. TerraformをRHEL8にインストールする
1. "terraform_1.11.2_linux_amd64.zip"をS3にアップロード
2. ssmでEC2インスタンスにアクセス
2. 【CloudShellで操作】以下コマンドを実行し、ダウンロード
    - コマンド

        ```shell
        cd /tmp
        aws s3 cp s3://terraform-private-env/terraform_1.11.2_linux_amd64.zip .
        ```

    - 実行例

        ```shell
        ~ $ cd /tmp
        tmp $ aws s3 cp s3://terraform-private-env/terraform_1.11.2_linux_amd64.zip .
        download: s3://terraform-private-env/terraform_1.11.2_linux_amd64.zip to ./terraform_1.11.2_linux_amd64.zip
        tmp $ 
        ```

3. 【CloudShellで操作】zipの解凍とtar.gzで圧縮
    - コマンド

        ```shell
        unzip terraform_1.11.2_linux_amd64.zip
        tar -zcvf terraform.tar.gz terraform
        ```

    - 実行例

        ```shell
        tmp $ unzip terraform_1.11.2_linux_amd64.zip
        Archive:  terraform_1.11.2_linux_amd64.zip
        inflating: LICENSE.txt             
        inflating: terraform               
        tmp $ tar -zcvf terraform.tar.gz terraform
        terraform
        tmp $ 
        ```

4. 【CloudShellで操作】S3バケットへのアップロード
    - コマンド

        ```shell
        aws s3 cp terraform.tar.gz s3://terraform-private-env/
        ```

    - 実行例

        ```shell
        tmp $ aws s3 cp terraform.tar.gz s3://terraform-private-env/
        upload: ./terraform.tar.gz to s3://terraform-private-env/terraform.tar.gz
        ```

5. ssmでEC2インスタンスにアクセス
6. 【EC2で操作】terraformのダウンロードと解凍
    - コマンド

        ```shell
        bash
        cd
        aws s3 cp s3://terraform-private-env/terraform.tar.gz .
        tar -zxvf terraform.tar.gz
        ```

    - 実行例

        ```shell
        sh-4.4$ bash
        [ssm-user@ip-10-0-0-115 bin]$ cd
        [ssm-user@ip-10-0-0-115 ~]$ aws s3 cp s3://terraform-private-env/terraform.tar.gz .
        download: s3://terraform-private-env/terraform.tar.gz to ./terraform.tar.gz
        [ssm-user@ip-10-0-0-115 ~]$ tar -zxvf terraform.tar.gz
        terraform
        [ssm-user@ip-10-0-0-115 ~]$
        ```

7. 【EC2で操作】以下コマンドで解凍と配置
    - コマンド

        ```shell
        sudo mv terraform /usr/local/bin/
        ```

    - 実行例

        ```shell
        [ssm-user@ip-10-0-0-115 ~]$ sudo mv terraform /usr/local/bin/
        [ssm-user@ip-10-0-0-115 ~]$
        ```

8. 【EC2で操作】以下コマンドでインストールを確認
    - コマンド

        ```shell
        terraform version
        ```
        
    - 実行例

        ```shell
        [ssm-user@ip-10-0-0-115 ~]$ terraform version
        Terraform v1.11.2
        on linux_amd64
        [ssm-user@ip-10-0-0-115 ~]$
        ```

## Step4. "terraform init"を実行する
1. "terraform-provider-vsphere_2.11.1_linux_amd64.zip"をS3にアップロード
2. 【CloudShellで操作】以下コマンドを実行し、ダウンロード
    - コマンド

        ```shell
        cd /tmp
        aws s3 cp s3://terraform-private-env/terraform-provider-vsphere_2.11.1_linux_amd64.zip .
        ```

    - 実行例

        ```shell
        ~ $ cd /tmp
        tmp $ aws s3 cp s3://terraform-private-env/terraform-provider-vsphere_2.11.1_linux_amd64.zip .
        download: s3://terraform-private-env/terraform-provider-vsphere_2.11.1_linux_amd64.zip to ./terraform-provider-vsphere_2.11.1_linux_amd64.zip
        tmp $ 
        tmp $ 
        ```

3. 【CloudShellで操作】zipの解凍とtar.gzで圧縮
    - コマンド

        ```shell
        unzip terraform-provider-vsphere_2.11.1_linux_amd64.zip
        tar -zcvf terraform-provider-vsphere.tar.gz terraform-provider-vsphere_v2.11.1_x5
        ```
        
    - 実行例

        ```shell
        tmp $ unzip terraform-provider-vsphere_2.11.1_linux_amd64.zip
        Archive:  terraform-provider-vsphere_2.11.1_linux_amd64.zip
        inflating: LICENSE.txt             
        inflating: terraform-provider-vsphere_v2.11.1_x5  
        tmp $ 
        tmp $ tar -zcvf terraform-provider-vsphere.tar.gz terraform-provider-vsphere_v2.11.1_x5
        terraform-provider-vsphere_v2.11.1_x5
        tmp $ 
        ```

4. 【CloudShellで操作】S3バケットへのアップロード
    - コマンド

        ```shell
        aws s3 cp terraform-provider-vsphere.tar.gz s3://terraform-private-env/
        ```

    - 実行例

        ```shell
        tmp $ aws s3 cp terraform-provider-vsphere.tar.gz s3://terraform-private-env/
        upload: ./terraform-provider-vsphere.tar.gz to s3://terraform-private-env/terraform-provider-vsphere.tar.gz
        tmp $ 
        ```

5. ssmでEC2インスタンスにアクセス
6. 【EC2で操作】以下コマンドで配置先ディレクトリを作成
    - コマンド

        ```shell
        mkdir -p ~/.terraform.d/plugins/registry.terraform.io/hashicorp/vsphere/2.11.1/linux_amd64/
        ```

    - 実行例

        ```shell
        [ssm-user@ip-10-0-0-115 ~]$ mkdir -p ~/.terraform.d/plugins/registry.terraform.io/hashicorp/vsphere/2.11.1/linux_amd64/
        [ssm-user@ip-10-0-0-115 ~]$ ls -ld ~/.terraform.d/plugins/registry.terraform.io/hashicorp/vsphere/2.11.1/linux_amd64/
        drwxrwxr-x. 2 ssm-user ssm-user 6 Mar 20 10:14 /home/ssm-user/.terraform.d/plugins/registry.terraform.io/hashicorp/vsphere/2.11.1/linux_amd64/
        [ssm-user@ip-10-0-0-115 ~]$
        ```

7. 【EC2で操作】配置先ディレクトリに以下コマンドでS3からダウンロード
    - コマンド

        ```shell
        aws s3 cp s3://terraform-private-env/terraform-provider-vsphere.tar.gz ~/.terraform.d/plugins/registry.terraform.io/hashicorp/vsphere/2.11.1/linux_amd64/
        ```

    - 実行例

        ```shell
        [ssm-user@ip-10-0-0-115 ~]$ aws s3 cp s3://terraform-private-env/terraform-provider-vsphere.tar.gz ~/.terraform.d/plugins/registry.terraform.io/hashicorp/vsphere/2.11.1/linux_amd64/
        download: s3://terraform-private-env/terraform-provider-vsphere.tar.gz to .terraform.d/plugins/registry.terraform.io/hashicorp/vsphere/2.11.1/linux_amd64/terraform-provider-vsphere.tar.gz
        [ssm-user@ip-10-0-0-115 ~]$ ls -l ~/.terraform.d/plugins/registry.terraform.io/hashicorp/vsphere/2.11.1/linux_amd64/
        total 9580
        -rw-rw-r--. 1 ssm-user ssm-user 9809280 Mar 20 10:14 terraform-provider-vsphere.tar.gz
        [ssm-user@ip-10-0-0-115 ~]$
        ```

8. 【EC2で操作】以下コマンドで解凍と配置
    - コマンド

        ```shell
        cd ~/.terraform.d/plugins/registry.terraform.io/hashicorp/vsphere/2.11.1/linux_amd64
        tar -zxvf terraform-provider-vsphere.tar.gz
        mv terraform-provider-vsphere_v2.11.1_x5 ~/.terraform.d/plugins/registry.terraform.io/hashicorp/vsphere/2.11.1/linux_amd64/terraform-provider-vsphere/
        ```

    - 実行例

        ```shell
        [ssm-user@ip-10-0-0-115 ~]$ cd ~/.terraform.d/plugins/registry.terraform.io/hashicorp/vsphere/2.11.1/linux_amd64
        [ssm-user@ip-10-0-0-115 linux_amd64]$ tar -zxvf terraform-provider-vsphere.tar.gz
        terraform-provider-vsphere_v2.11.1_x5
        [ssm-user@ip-10-0-0-115 linux_amd64]$ ll
        total 41692
        -rw-rw-r--. 1 ssm-user ssm-user  9809280 Mar 20 10:14 terraform-provider-vsphere.tar.gz
        -rwxr-xr-x. 1 ssm-user ssm-user 32878744 Feb  3 19:52 terraform-provider-vsphere_v2.11.1_x5
        [ssm-user@ip-10-0-0-115 linux_amd64]$ mv terraform-provider-vsphere_v2.11.1_x5 ~/.terraform.d/plugins/registry.terraform.io/hashicorp/vsphere/2.11.1/linux_amd64/terraform-provider-vsphere/
        [ssm-user@ip-10-0-0-115 linux_amd64]$ ls -l ~/.terraform.d/plugins/registry.terraform.io/hashicorp/vsphere/2.11.1/linux_amd64terraform-provider-vsphere/
        -rwxr-xr-x. 1 ssm-user ssm-user 32878744 Feb  3 19:52 /home/ssm-user/.terraform.d/plugins/registry.terraform.io/hashicorp/vsphere/2.11.1/linux_amd64/terraform-provider-vsphere
        [ssm-user@ip-10-0-0-115 linux_amd64]$
        ```

9. 【EC2で操作】"~/.terraformrc"を作成し、ローカルミラー設定
    - コマンド

        ```shell
        cd 
        vi ~/.terraformrc
        cat ~/.terraformrc
        ```

    - "~/.terraformrc"の中身

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

    - 実行例

        ```shell
        [ssm-user@ip-10-0-0-115 linux_amd64]$ cd
        [ssm-user@ip-10-0-0-115 ~]$ vi ~/.terraformrc
        [ssm-user@ip-10-0-0-115 ~]$ cat ~/.terraformrc
        provider_installation {
            filesystem_mirror {
                path = "/home/ssm-user/.terraform.d/plugins"
            }
            direct {
                exclude = ["registry.terraform.io/hashicorp/*"]
            }
        }
        [ssm-user@ip-10-0-0-115 ~]$
        ```

10. 【EC2で操作】terraform用設定ファイルを作成
    - コマンド

        ```shell
        cd 
        mkdir terraform
        cd terraform
        vi main.tf
        cat main.tf
        ```

    - 設定ファイル

        ```hcl
        # initを実行するためだけのため、以下内容で問題ない。
        terraform {
            required_providers {
                vsphere = {
                source = "hashicorp/vsphere"
                version = "2.11.1"
                }
            }
        }

        provider "vsphere" {
            # Configuration options
        }
        ```

    - 実行例

        ```shell
        [ssm-user@ip-10-0-0-115 ~]$ cd
        [ssm-user@ip-10-0-0-115 ~]$ mkdir terraform
        [ssm-user@ip-10-0-0-115 ~]$ cd terraform
        [ssm-user@ip-10-0-0-115 terraform]$ vi main.tf
        [ssm-user@ip-10-0-0-115 terraform]$ cat main.tf
        # initを実行するためだけのため、以下内容で問題ない。
        terraform {
            required_providers {
                vsphere = {
                source = "hashicorp/vsphere"
                version = "2.11.1"
                }
            }
        }

        provider "vsphere" {
            # Configuration options
        }
        [ssm-user@ip-10-0-0-115 terraform]$
        ```

11. 【EC2で操作】"terraform init"を実行
    - コマンド

        ```shell
        terraform init
        ```

    - 実行例

        ```shell
        [ssm-user@ip-10-0-0-115 terraform]$ terraform init
        Initializing the backend...
        Initializing provider plugins...
        - Finding hashicorp/vsphere versions matching "2.11.1"...
        - Installing hashicorp/vsphere v2.11.1...
        - Installed hashicorp/vsphere v2.11.1 (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/vsphere
        │
        │ The current .terraform.lock.hcl file only includes checksums for linux_amd64, 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.
        [ssm-user@ip-10-0-0-115 terraform]$
        ```

# おわりに
- 上記の通り、クローズドな環境であってもTerraform実行環境を用意することができます。
- 「Terraformを利用するためのステップ」で記載しｍ、"terraform plan/apply"を実行する際には、必ずTerraformで操作する環境にアクセスできる必要がありますのでご注意ください。
# 参考
- [Private SubnetのRHEL EC2インスタンスにSSM Agentをインストールする方法](https://dev.classmethod.jp/articles/ssm-agent-private-install/)
- [AWS CLI の最新バージョンのインストールまたは更新](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html)