ちょっと話題の記事

【朗報】Terraform v0.7.0 から既存リソースをインポートする機能が追加されます!

2016.07.07

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは、中山です。

次回リリースバージョンであるTerraformのv0.7.0にはいろいろと大きな変更点があります。本エントリでは変更点の内インパクトの大きい import サブコマンドについてご紹介します。

import サブコマンドとは何か

ドキュメントから引用します。

Terraform is able to import existing infrastructure. This allows you take resources you've created by some other means and bring it under Terraform management.

つまりTerraform以外で作成したAWSなどのresourceをTerraform管理下にすることができる機能です。すばらしい。ただし、v.0.7.0時点では以下の文章に書かれているように tfstate へのインポートのみ実装される予定です。

The current implementation of Terraform import can only import resources into the state. It does not generate configuration. A future version of Terraform will also generate configuration.

そのため import サブコマンドで既存リソースをインポート後、自分で tf ファイルを作成する必要があります。この辺りの改善は今後に期待しましょう。

従来のAWSリソースインポート事情

AWS provider限定ではありますが、以前からAWS resourceのインポートはterraformingを利用することにより可能でした。このツールはRuby製のツールで既存AWSリソースから tftfstate 両方を生成することが可能です。つまり、v0.7.0の import サブコマンドに対応していない機能も実装しています。そのため、しばらくは両方のツールが混在する状況になると思われます。

import サブコマンドの使い方

それでは早速使ってみましょう。

コマンドの書式

以下の通りです。

terraform import [options] ADDR ID

ADDR にresourceのAddressを指定します。 <resource-type>.<resource-name> の形式で指定します。詳細はドキュメントを参照してください。 ID にはresourceのIDを指定します。例えば、EC2インスタンスの場合はインスタンスIDのことです。

EC2インスタンスをインポートしてみる

東京リージョンに設置されているインスタンスID i-b41c682b のEC2インスタンスをインポートしてみます。

$ terraform import aws_instance.bar i-b41c682b
provider.aws.region
  The region where AWS operations will take place. Examples
  are us-east-1, us-west-2, etc.

  Default: us-east-1
  Enter a value: ap-northeast-1

aws_instance.bar: Importing from ID "i-b41c682b"...
aws_instance.bar: Import complete!
  Imported aws_instance (ID: i-b41c682b)
aws_instance.bar: Refreshing state... (ID: i-b41c682b)

Import success! The resources imported are shown above. These are
now in your Terraform state. Import does not currently generate
configuration, so you must do this next. If you do not create configuration
for the above resources, then the next `terraform plan` will mark
them for destruction.

コマンド実行後、カレントディレクトリに terraform.tfstate というファイルが作成されます。中身を確認してみましょう。

$ cat terraform.tfstate
{
    "version": 3,
    "terraform_version": "0.7.0",
    "serial": 0,
    "lineage": "f9822c82-fd5c-4eb2-86ce-08741d36e896",
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {},
            "resources": {
                "aws_instance.bar": {
                    "type": "aws_instance",
                    "primary": {
                        "id": "i-b41c682b",
                        "attributes": {
                            "ami": "ami-383c1956",
<snip>

tfstate ファイルが作成されているようですね。この状態で plan サブコマンドを実行すると tf ファイルが存在しないため、このresourceを削除する旨表示されてしまいます。このresource用の tf ファイルを以下のように作成してみましょう。

<snip>
variable "region" {
  default = "ap-northeast-1"
}

variable "web_instance_ami_id" {
  default = "ami-383c1956"
}

variable "web_instance_type" {
  default = "t2.micro"
}

provider "aws" {
  region = "${var.region}"
}

resource "aws_instance" "bar" {
  ami                         = "${var.web_instance_ami_id}"
  instance_type               = "${var.web_instance_type}"
  vpc_security_group_ids      = ["sg-f5522491"]
  subnet_id                   = "subnet-980ce0ee"
  key_name                    = "test"
  associate_public_ip_address = true

  root_block_device {
    volume_type = "gp2"
    volume_size = 8
  }

  user_data = <<EOT
#cloud-config
repo_update: true
repo_upgrade: all

runcmd:
  - cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
EOT

  tags {
    Name = "simple"
  }
}

plan を実行してみます。

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but
will not be persisted to local or remote state storage.

aws_instance.bar: Refreshing state... (ID: i-b41c682b)

The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed. Cyan entries are data sources to be read.

Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.

-/+ aws_instance.bar
    ami:                                       "ami-383c1956" => "ami-383c1956"
    associate_public_ip_address:               "" => "true" (forces new resource)
    availability_zone:                         "ap-northeast-1a" => "<computed>"
    ebs_block_device.#:                        "0" => "<computed>"
    ephemeral_block_device.#:                  "0" => "<computed>"
    instance_state:                            "running" => "<computed>"
    instance_type:                             "t2.micro" => "t2.micro"
    key_name:                                  "test" => "test"
    placement_group:                           "" => "<computed>"
    private_dns:                               "ip-172-16-0-4.ap-northeast-1.compute.internal" => "<computed>"
    private_ip:                                "172.16.0.4" => "<computed>"
    public_dns:                                "ec2-54-238-193-249.ap-northeast-1.compute.amazonaws.com" => "<computed>"
    public_ip:                                 "54.238.193.249" => "<computed>"
    root_block_device.#:                       "1" => "1"
    root_block_device.0.delete_on_termination: "true" => "true"
    root_block_device.0.iops:                  "100" => "<computed>"
    root_block_device.0.volume_size:           "8" => "8"
    root_block_device.0.volume_type:           "gp2" => "gp2"
    security_groups.#:                         "0" => "<computed>"
    source_dest_check:                         "true" => "true"
    subnet_id:                                 "subnet-980ce0ee" => "subnet-980ce0ee"
    tags.%:                                    "1" => "1"
    tags.Name:                                 "simple" => "simple"
    tenancy:                                   "default" => "<computed>"
    vpc_security_group_ids.#:                  "1" => "1"
    vpc_security_group_ids.156324148:          "sg-f5522491" => "sg-f5522491"


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

ハイライトの箇所に注目していただきたいのですが、 associate_public_ip_address の項目が "" => "true" (forces new resources) と表示されています。つまり、 apply 実行時、既存resourceの再作成が発生してしまいます。また、 user_data の項目も表示されていません。現時点では単純にインポートするだけでOKとはならないようです。残念。

state サブコマンドを使う

tfstate ファイルは単なるJSONなので、エディタなどを使い直接修正することも可能ですが、それは非推奨になっています。そこで、v0.7.0からstateサブコマンドが導入されます。このサブコマンドを利用することで tfstate ファイルの修正が可能です。また、単にファイルを修正するだけではなく以下のような特徴があります。

  1. Unix系ツール(grep/awkなど)と連携しやすいように作られている
  2. tfstate のバックアップを自動で作成する

コマンドの書式

以下の通りです。

Usage: terraform state <subcommand> [options] [args]

このコマンドは更にサブコマンドを指定する形式になっています。現時点で対応しているコマンドは以下の通りです。

  • list
  • show
  • mv

それぞれのコマンドについてご紹介します。

list

list サブコマンドは tfstate ファイルからresource名を標準出力に表示します。コマンドの書式は以下の通りです。

terraform state list [options] [address...]

使用例は以下の通りです。

# 全てのresourceを表示
$ terraform state list
<snip>
aws_instance.bar
# resource名でフィルタリング
$ terraform state list aws_instance.bar
aws_instance.bar

show

show サブコマンドは1つのresource情報(attribute)を標準出力に表示します。コマンドの書式は以下の通りです。

terraform state show [options] ADDRESS

使用例は以下の通りです。

$ terraform state show aws_instance.bar
id                                        = i-b41c682b
ami                                       = ami-383c1956
availability_zone                         = ap-northeast-1a
disable_api_termination                   = false
ebs_block_device.#                        = 0
ebs_optimized                             = false
ephemeral_block_device.#                  = 0
iam_instance_profile                      =
instance_state                            = running
instance_type                             = t2.micro
key_name                                  = test
monitoring                                = false
private_dns                               = ip-172-16-0-4.ap-northeast-1.compute.internal
private_ip                                = 172.16.0.4
public_dns                                = ec2-54-238-193-249.ap-northeast-1.compute.amazonaws.com
public_ip                                 = 54.238.193.249
root_block_device.#                       = 1
root_block_device.0.delete_on_termination = true
root_block_device.0.iops                  = 100
root_block_device.0.volume_size           = 8
root_block_device.0.volume_type           = gp2
security_groups.#                         = 0
source_dest_check                         = true
subnet_id                                 = subnet-980ce0ee
tags.%                                    = 1
tags.Name                                 = simple
tenancy                                   = default
vpc_security_group_ids.#                  = 1
vpc_security_group_ids.156324148          = sg-f5522491

mv

mv サブコマンドは tfstate の情報を変更できます。resource名の変更/module化/別 tfstate ファイルへの移動、といった操作が可能です。コマンドの書式は以下の通りです。

terraform state mv [options] SOURCE DESTINATION

使用例は以下の通りです。 aws_vpc resourceのresource名を変更してみます。

$ terraform state mv aws_vpc.vpc aws_vpc.test
Moved aws_vpc.vpc to aws_vpc.test

mv サブコマンドは自動で tfstate ファイルをバックアップしてくれます。差分を確認してみましょう。

--- terraform.tfstate.1467891070..backup        2016-07-07 20:31:10.000000000 +0900
+++ terraform.tfstate   2016-07-07 20:31:10.000000000 +0900
@@ -1,7 +1,7 @@
 {
     "version": 3,
     "terraform_version": "0.7.0",
-    "serial": 34,
+    "serial": 35,
     "modules": [
         {
             "path": [
@@ -226,7 +226,7 @@
                         }
                     }
                 },
-                "aws_vpc.vpc": {
+                "aws_vpc.test": {
                     "type": "aws_vpc",
                     "primary": {
                         "id": "vpc-73575616",

aws_vpc resource名を変更してくれているようです。また、シリアル番号も変更されるので tf ファイルを更新すれば、実際の状態と tfstate の状態が合致するようになっています。

tf ファイルを手動で変更後 plan を実行してみましょう。

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but
will not be persisted to local or remote state storage.

aws_key_pair.site_key: Refreshing state... (ID: test)
aws_vpc.test: Refreshing state... (ID: vpc-73575616)
aws_internet_gateway.public: Refreshing state... (ID: igw-ea6c098f)
aws_security_group.web: Refreshing state... (ID: sg-db6d21bf)
aws_subnet.public: Refreshing state... (ID: subnet-e56b9893)
aws_route_table.public: Refreshing state... (ID: rtb-f730c693)
aws_instance.web: Refreshing state... (ID: i-f3feb86c)
aws_network_acl.acl: Refreshing state... (ID: acl-a77b97c3)
aws_route_table_association.public: Refreshing state... (ID: rtbassoc-e3d35487)

No changes. Infrastructure is up-to-date. This means that Terraform
could not detect any differences between your configuration and
the real physical resources that exist. As a result, Terraform
doesn't need to do anything.

aws_vpc のresource名が変更されていること、差分が解消されていることが確認できます。やりましたね。

まとめ

いかがだったでしょうか。

import サブコマンドの導入によりTerraformの可能性がまた1つ広がりました。まだまだ基本的な機能しか実装されていませんが、今後の更新が楽しみです。

本エントリがみなさんの参考になれば幸いです。