TerraformでCloudFormationを実行

## 課題

HashiCorpの[Terraform](https://www.terraform.io)ではInfrastructure as a Codeを容易に記述できるが、各種providerではまだ未実装のモノが多い。

例えば、Redshift (2015/11/27現在)
https://github.com/hashicorp/terraform/pull/3862

## 解決法

しかし、Terraform v0.6.7では最近CloudFormationのproviderに[対応した](https://github.com/hashicorp/terraform/pull/2636)為、CloudFormation templateを埋め込んで機能を補完できるようになった！（ついこの間まではHEADにしか入ってなかったので、goでコンパイルする必要があったけど）

# TF files

こんな感じで。

```rb:vpc.tf
# VPC
resource "aws_vpc" "test" {
    cidr_block = "172.7.0.0/16"
    enable_dns_hostnames = true

    tags {
        Name = "test"
    }
}

# subnets
resource "aws_subnet" "test-1a" {
    vpc_id = "${aws_vpc.test.id}"
    cidr_block = "172.7.0.0/20"
    availability_zone = "ap-northeast-1a"
    map_public_ip_on_launch = true

    tags {
        Name = "1a"
    }
}

resource "aws_subnet" "test-1c" {
    vpc_id = "${aws_vpc.test.id}"
    cidr_block = "172.7.16.0/20"
    availability_zone = "ap-northeast-1c"
    map_public_ip_on_launch = true

    tags {
        Name = "1c"
    }
}
```

```rb:security-group.tf
resource "aws_security_group" "test-redshift" {
    vpc_id = "${aws_vpc.test.id}"
    name = "test-redshift"
    description = "redshift SG (test)"
    tags {
      Name = "redshift"
    }

    ingress {
        from_port = 5439
        to_port = 5439
        protocol = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }

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

```rb:redshift.tf
resource "aws_cloudformation_stack" "redshift" {
    name = "redshift"
    template_body = <<STACK
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Parameters" : {
    "DatabaseName" : {
      "Description" : "The name of the first database to be created when the cluster is created",
      "Type" : "String",
      "Default" : "mydwh",
      "AllowedPattern" : "([a-z]|[0-9])+"
    },
    "ClusterType" : {
      "Description" : "The type of cluster",
      "Type" : "String",
      "Default" : "multi-node",
      "AllowedValues" : [ "single-node", "multi-node" ]
    },
    "NumberOfNodes" : {
      "Description" : "The number of compute nodes in the cluster. For multi-node clusters, the NumberOfNodes parameter must be greater than 1",
      "Type" : "Number",
      "Default" : "2"
    },
    "NodeType" : {
      "Description" : "The type of node to be provisioned",
      "Type" : "String",
      "Default" : "dc1.large",
      "AllowedValues" : [ "dc1.large", "dc1.8xlarge", "ds2.large", "ds2.8xlarge" ]
    },
    "MasterUsername" : {
      "Description" : "The user name that is associated with the master user account for the cluster that is being created",
      "Type" : "String",
      "Default" : "dbuser",
      "AllowedPattern" : "([a-z])([a-z]|[0-9])*"
    },
    "MasterUserPassword" :  {
      "Description" : "The password that is associated with the master user account for the cluster that is being created.",
      "Type" : "String",
      "Default" : "dbpassword",
      "NoEcho" : "true"
    },
    "PortNumber" : {
      "Description" : "The port number on which the cluster accepts incoming connections.",
      "Type" : "Number",
      "Default" : "5439"
    },
    "VpcId" : {
      "Description" : "The VPC ID to launch the cluster",
      "Type" : "AWS::EC2::VPC::Id",
      "Default" : "${aws_vpc.test.id}"
    },
    "SecurityGroupId" : {
      "Description" : "The Security Group ID(s) to launch the cluster",
      "Type" : "List<AWS::EC2::SecurityGroup::Id>",
      "Default" : "${aws_security_group.test-redshift.id}"
    },
    "Subnets" : {
      "Description" : "The Subnet ID(s) to include in the subnet group",
      "Type" : "List<AWS::EC2::Subnet::Id>",
      "Default" : "${aws_subnet.test-1a.id},${aws_subnet.test-1c.id}"
    }
  },
  "Conditions" : {
    "IsMultiNodeCluster" : {
      "Fn::Equals" : [{ "Ref" : "ClusterType" }, "multi-node" ]
    }
  },
  "Resources" : {
    "RedshiftCluster" : {
      "Type" : "AWS::Redshift::Cluster",
      "Properties" : {
        "ClusterType" : { "Ref" : "ClusterType" },
        "NumberOfNodes" : { "Fn::If" : [ "IsMultiNodeCluster",  { "Ref" : "NumberOfNodes" }, { "Ref" : "AWS::NoValue" }]},
        "NodeType" : { "Ref" : "NodeType" },
        "DBName" : { "Ref" : "DatabaseName" },
        "MasterUsername" : { "Ref" : "MasterUsername" },
        "MasterUserPassword" : { "Ref" : "MasterUserPassword" },
        "ClusterParameterGroupName" : { "Ref" : "RedshiftClusterParameterGroup" },
        "VpcSecurityGroupIds" : { "Ref" : "SecurityGroupId" },
        "ClusterSubnetGroupName" : { "Ref" : "RedshiftClusterSubnetGroup" },
        "PubliclyAccessible" : "true",
        "Port" : { "Ref" : "PortNumber" }
      }
    },
    "RedshiftClusterParameterGroup" : {
      "Type" : "AWS::Redshift::ClusterParameterGroup",
      "Properties" : {
        "Description" : "Cluster parameter group",
        "ParameterGroupFamily" : "redshift-1.0",
        "Parameters" : [{
          "ParameterName" : "enable_user_activity_logging",
          "ParameterValue" : "true"
        }]
      }
    },
    "RedshiftClusterSubnetGroup" : {
      "Type" : "AWS::Redshift::ClusterSubnetGroup",
      "Properties" : {
        "Description" : "Cluster subnet group",
        "SubnetIds" : { "Ref" : "Subnets" }
      }
    }
  },
  "Outputs" : {
    "ClusterEndpoint" : {
      "Description" : "Cluster endpoint",
      "Value" : { "Fn::Join" : [ ":", [ { "Fn::GetAtt" : [ "RedshiftCluster", "Endpoint.Address" ] }, { "Fn::GetAtt" : [ "RedshiftCluster", "Endpoint.Port" ] } ] ] }
    },
    "ClusterName" : {
      "Description" : "Name of cluster",
      "Value" : { "Ref" : "RedshiftCluster" }
    },
    "ParameterGroupName" : {
      "Description" : "Name of parameter group",
      "Value" : { "Ref" : "RedshiftClusterParameterGroup" }
    }
  }
}
STACK
```

Terraformで作成されたvariableがそのまま埋め込めるのが素晴らしい。

## 機能実装の優先順位付け

ちなみに欲しい機能は以下のspreadsheetに自分のgithub idを付け足して投票する事によって、優先順位を[決めている](https://github.com/hashicorp/terraform/issues/28)とのこと。

https://docs.google.com/spreadsheets/d/1yJKjLaTmkWcUS3T8TLwvXC6EBwNSpuQbIq0Y7OnMXhw/edit#gid=1149664272&vpid=A1