はじめに

こんにちは、そしてこんばんは!
クラウドインテグレーション事業部の大嵩です。
この記事は『クラウドインテグレーション事業部SRE第三セクションブログリレー企画』の2回目(全6回)の記事になります!

前回は比嘉 東一郎さんの「The Linux Foundation の日本語オンライン講座を試してみた」が公開されました。

本記事では、業務の中でTerraformで管理されていないAWSリソースをインポートすることがありましたので、
備忘録として紹介します。

前提

今回は前提としてEC2インスタンスをインポートすることを想定とします。

まずはterrraformの基本定義を行う

terraformを書くときにはまず基本となる定義が必要となります。
設定パラメータは下記公式ドキュメントを参考にします。
https://registry.terraform.io/providers/hashicorp/aws/latest/docs

私の場合はこのように記述しました。

# provider.tf
terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
    }
  }

  backend "local" {
    path = "./terraform.tfstate"
  }
}

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

required_providers

ここで、使用する プロバイダーを定義します。
AWSリソースになりますので、「”hashicorp/aws”」と記述します。

backend “local”

ここで、Terraformの構成情報を保存する場所を定義します。
今回はローカルに保存するため、localとします

provider “aws”

ここでは、定義したプロバイダーに対して設定を記述します。
使用するリージョンを「ap-northeast-1」で設定します。

実際にインポートする

インポートする前に

インポートに移る前に、まず空の定義を記述してあげる必要があります。
以下の通りに本当に空の状態で書きます。

# ec2.tf
resource "aws_instance" "test" {
}

このとき、Terraform上で扱う定義名はわかりやすく自由に指定して大丈夫です。
(この場合 “test” の部分です。)

インポートを実行する

空の定義ができたらいよいよインポートです。
下記のコマンドを実行します

terraform import aws_instance.test インスタンスID

インポートが成功すると、このようになります

❯ terraform import aws_instance.test インスタンスID                                    
aws_instance.test: Importing from ID "XXXX"...
aws_instance.test: Import prepared!
  Prepared aws_instance for import
aws_instance.test: Refreshing state... [id=XXXX]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

インポート後

コマンドを実行したら、tfstateファイルに下記のようなEC2の構成情報が書き出されます。

# terraform.tfstate
{
  "version": 4,
  "terraform_version": "1.9.7",
  "serial": 1,
  "lineage": "XXXX",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "aws_instance",
      "name": "test",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "schema_version": 1,
          "attributes": {
            "ami": "ami-0ef29ab52ff72213b",
            "arn": "XXXX",
            "associate_public_ip_address": false,
            "availability_zone": "ap-northeast-1d",
            "capacity_reservation_specification": [
              {
                "capacity_reservation_preference": "open",
                "capacity_reservation_target": []
              }
            ],
            "cpu_core_count": 1,
            "cpu_options": [
              {
                "amd_sev_snp": "",
                "core_count": 1,
                "threads_per_core": 1
              }
            ],
            "cpu_threads_per_core": 1,
            "credit_specification": [
              {
                "cpu_credits": "standard"
              }
            ],
            "disable_api_stop": false,
            "disable_api_termination": false,
            "ebs_block_device": [],
            "ebs_optimized": false,
            "enclave_options": [
              {
                "enabled": false
              }
            ],
            "ephemeral_block_device": [],
            "get_password_data": false,
            "hibernation": false,
            "host_id": "",
            "host_resource_group_arn": null,
            "iam_instance_profile": "",
            "id": "XXXX",
            "instance_initiated_shutdown_behavior": "stop",
            "instance_lifecycle": "",
            "instance_market_options": [],
            "instance_state": "running",
            "instance_type": "t2.micro",
            "ipv6_address_count": 0,
            "ipv6_addresses": [],
            "key_name": "",
            "launch_template": [],
            "maintenance_options": [
              {
                "auto_recovery": "default"
              }
            ],
            "metadata_options": [
              {
                "http_endpoint": "enabled",
                "http_protocol_ipv6": "disabled",
                "http_put_response_hop_limit": 2,
                "http_tokens": "required",
                "instance_metadata_tags": "disabled"
              }
            ],
            "monitoring": false,
            "network_interface": [],
            "outpost_arn": "",
            "password_data": "",
            "placement_group": "",
            "placement_partition_number": 0,
            "primary_network_interface_id": "XXXX",
            "private_dns": "ip-172-31-17-175.ap-northeast-1.compute.internal",
            "private_dns_name_options": [
              {
                "enable_resource_name_dns_a_record": false,
                "enable_resource_name_dns_aaaa_record": false,
                "hostname_type": "ip-name"
              }
            ],
            "private_ip": "172.31.17.175",
            "public_dns": "",
            "public_ip": "",
            "root_block_device": [
              {
                "delete_on_termination": true,
                "device_name": "/dev/xvda",
                "encrypted": false,
                "iops": 3000,
                "kms_key_id": "",
                "tags": {},
                "tags_all": {},
                "throughput": 125,
                "volume_id": "XXXX",
                "volume_size": 8,
                "volume_type": "gp3"
              }
            ],
            "secondary_private_ips": [],
            "security_groups": [
              "default"
            ],
            "source_dest_check": true,
            "spot_instance_request_id": "",
            "subnet_id": "XXXX",
            "tags": {
              "Name": "test-otake",
              "Owner": "otake"
            },
            "tags_all": {
              "Name": "test-otake",
              "Owner": "otake"
            },
            "tenancy": "default",
            "timeouts": null,
            "user_data": null,
            "user_data_base64": null,
            "user_data_replace_on_change": null,
            "volume_tags": null,
            "vpc_security_group_ids": [
              "XXXX"
            ]
          },
          "sensitive_attributes": [],
          "private": "XXXX"
        }
      ]
    }
  ],
  "check_results": null
}

上記を元に、空の状態で記述していた、EC2の定義を書き込んでいきます。

# ec2.tf
resource "aws_instance" "test" {
    ami                    = "ami-0ef29ab52ff72213b"
    instance_type          = "t2.micro"
    availability_zone      = "ap-northeast-1d"
    vpc_security_group_ids =  [セキュリティグループID] #リスト型
    subnet_id              =  サブネットID

    root_block_device {
      volume_type = "gp3"
      volume_size = "8"
      iops        = 3000
      throughput  = 125
      encrypted   = false
    }

    tags = {
      Name   = "test-otake"
      Owner  = "otake"
    }
}

その他に、キーペアやIAMロールを割り当てている場合、tfstateを見ながら同じように追記します。

TerraformのState情報と差分がないかを確認

記述を追記した後はplanを実行して差分がないことを確認しましょう。
以下の実行結果になれば成功です!!

# コマンド
terraform plan

# 実行結果
❯ terraform plan
aws_instance.test: Refreshing state... [id=XXXX]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

まとめ

今回インポートを実行してみて、意外と簡単に後からリソースをTerraformに紐づけて管理することができました!
また、Terraform管理にすることで、マネジメントコンソールよりも効率的に運用作業を実施することができます。
今後も活用していきたいと思います!

この記事がどなたかの参考になりますと幸いです!

次回は田中 俊さんの「CDKでEKSクラスター構築してみた」が公開される予定です!お楽しみに!