DevSecOpsのDRY原則-Terraform編(1): Input Variables

#目次
[1.はじめに](#1-はじめに)
[2.サンプルテンプレート](#2-サンプルテンプレート)
[3.Variablesの定義](#3-Variablesの定義)
[4.tfvarsを使って環境を立ち上がる](#4-tfvarsを使って環境を立ち上がる)
[5.おわりに](#5-おわりに)

#1. はじめに
株式会社オプティマインドのGoです。
今回は「DevSecOpsのDRY原則-Terraform編(1)」をお届けします。
これからインフラ構築を考えている、既存インフラをリファクタリングしたい、持続可能な開発環を作りたいエンジニアの方々におすすめの記事です。

さて、今回の架空シナリオでは、環境ごとにAWSのEC2サーバーを立て、dev, staging, test, prod 4つの環境を立ち上げる場面を想定します。

1番簡単な方法として思いつくのは、テンプレとなるscriptを用意し4つコピペし、それぞれ変数部分のみ手動で書き換える方法です。
しかし、どこかの環境で何らかの変更が必要な場合に、他の環境でも同時に手動修正が必要となり、開発ストレスが高くなり、fat fingerが増えてしまうという問題点が挙げられます。


#2-サンプルテンプレート
それを解決するのはDRY原則です。
今回はDRY原則その1 [TerraformのInput Variables](https://www.terraform.io/language/values/variables)を用いて社内stagingとprodの環境を構築してきます。


```terraform:main.tf
provider "aws" {
  region = var.aws_region
}

data "aws_ami" "latest_amazon_linux" {
  owners      = ["11111111"]
  most_recent = true
  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

resource "aws_eip" "web" {
  instance = aws_instance.web.id
  //Merge Name to default map
  tags     = merge(var.tags, { Name = "${var.tags["Environment"]}-EIP by Terraform" })
}

resource "aws_instance" "web" {
  ami                    = data.aws_ami.latest_amazon_linux.id
  instance_type          = var.instance_size
  vpc_security_group_ids = [aws_security_group.web.id]
  key_name               = var.key_pair
  user_data              = file("user_data.sh")
  tags                   = merge(var.tags, { Name = "${var.tags["Environment"]}-by Terraform" })

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_security_group" "web" {
  name        = "${var.tags["Environment"]}-Server"
  description = "Security Group of ${var.tags["Environment"]} Server"

  dynamic "ingress" {
    for_each = var.port_list
    content {
      from_port   = ingress.value
      to_port     = ingress.value
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    }
  }

  egress {
    description = "Allow ALL ports"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  tags = merge(var.tags, { Name = "${var.tags["Environment"]}-SG by Terraform" })
}
```


```bash:user_data.sh
#!/bin/bash
yum -y update
yum -y install httpd
PrivateIP=`curl http://169.254.169.254/latest/meta-data/local-ipv4`


cat <<EOF > /var/www/html/index.html
<html>
Default EC2 MetaData from AWSP: $PrivateIP
</html>
EOF

service httpd start
chkconfig httpd on
```

#3-Variablesの定義
```terraform:variables.tf
variable "aws_region" {
  description = "Region of this EC2"
  type        = string
  default     = "ap-northeast-1"
}

variable "port_list" {
  description = "White listed ports"
  type        = list(any)
}

variable "instance_size" {
  description = "EC2 Instance Type"
  type        = string
}

variable "tags" {
  description = "Tags to Apply to Resources"
  type        = map(any)
  default = {
    Environment = "Dev"
    Project     = "MyProject"
  }
}

variable "key_pair" {
  description = "SSH Key pair name for EC2"
  type        = string
  default     = "MyKey"
  sensitive   = true
}

```
#4-tfvarsを使って環境を立ち上がる
4つの環境に対応している.tfvarsファイルを作成します。

```hcl:dev.tfvars
aws_region    =  "ap-northeast-1"
port_list     =  ["80", "443", "8443"]
instance_size =  "t2.micro"
key_pair      =  "MyDevKey"

tags = {
  Environment = "Dev"
  Project     = "MyDevProject"
}
```
```hcl:test.tfvars
aws_region    =  "ap-northeast-1"
port_list     =  ["80", "443", "8443"]
instance_size =  "t2.micro"
key_pair      =  "MyTestKey"

tags = {
  Environment = "Test"
  Project     = "MyTestProject"
}
```
```hcl:staging.tfvars
aws_region    =  "ap-northeast-1"
port_list     =  ["80", "443", "8443"]
instance_size =  "t2.micro"
key_pair      =  "MyStagingKey"

tags = {
  Environment = "Staging"
  Project     = "MyStagingProject"
}
```
```hcl:prod.tfvars
aws_region    =  "ap-northeast-1"
port_list     =  ["80", "443", "8443"]
instance_size =  "t3.large"
key_pair      =  "MyProdKey"

tags = {
  Environment = "Prod"
  Project     = "MyProdProject"
}
```

たとえばstaging環境を立ち上げたい時に```-var-file="staging.tfvars"```　をパラメーターとして引き渡します。

```bash:apply.sh
terraform apply -var-file="staging.tfvars"
```

#5-おわりに


ここまで読んでいただきありがとうございました。

株式会社オプティマインドでは、一緒に働く仲間を大募集中です。
カジュアル面談も大歓迎ですので、気軽にお声がけください。

【エンジニア領域の募集職種】
[●ソフトウェアエンジニア](https://hrmos.co/pages/optimind/jobs/0002012)
[●QAエンジニア](https://hrmos.co/pages/optimind/jobs/0002011)
[●Androidアプリエンジニア](https://hrmos.co/pages/optimind/jobs/0002008)
[●組合せ最適化アルゴリズムエンジニア](https://hrmos.co/pages/optimind/jobs/0002013)
[●経路探索アルゴリズムエンジニア](https://hrmos.co/pages/optimind/jobs/0002014)
[●バックエンドエンジニア](https://hrmos.co/pages/optimind/jobs/0002015)
[●インフラエンジニア](https://hrmos.co/pages/optimind/jobs/0002016)
[●UXUIデザイナー](https://hrmos.co/pages/optimind/jobs/0002010)

【ビジネス領域の募集職種】
[●セールスコンサルタント](https://hrmos.co/pages/optimind/jobs/0002001)
[●採用・人事](https://hrmos.co/pages/optimind/jobs/0002022)


『Loogiaってどんなサービス？』については、[こちら](https://www.youtube.com/watch?v=o-z19Hd7DYY)


『オプティマインドってどんな会社？』については、[こちら](https://www.youtube.com/watch?v=o-z19Hd7DYY)


Wantedlyでも[こちら](https://www.wantedly.com/companies/optimind/projects)で募集中


- [参考文献](https://www.terraform.io/language/values/variables)