github actionsを使って aws + terraform + ansible でCI/CD環境を構築してみた

## 概要

最近github actionsが楽しいのでaws環境でteraform + ansible実行してみた。
EC2を構築し、httpdをインストールする所まで実施。

## 目次

・事前準備
・ディレクトリ構成
・バケット作成
・IAMユーザ作成
・terraformコード作成
・ansible コード作成
・github actions 作成
・githu actions 実行結果

##事前準備

- EC2のIPの自動取得にdyanamic inventoryが使用できること

##ディレクトリ構成

```
├── README.md
├── ansible
│   ├── ansible.cfg
│   ├── ec2.ini
│   ├── ec2.py
│   ├── roles
│   │   ├── common
│   │   │   ├── files
│   │   │   │   └── main.yml
│   │   │   └── tasks
│   │   └── nginx
│   │       ├── files
│   │       └── tasks
│   │           └── main.yml
│   ├── sandbox.yml
└── terraform
    ├── backend.tf
    ├── data.tf
    ├── main.tf
    ├── output
    ├── output.tf
    ├── provider.tf
    └── variables.tf

```

## S3バケット作成

tfstate保存用のバケットを作成する。

``` shell:バケット作成
% aws s3 mb s3://tf-sandbox-masa3521
make_bucket: tf-sandbox-masa3521
```


## IAMユーザ作成

terraform,ansibleの実行用のIAMユーザを作成する。
EC2とS3、key系はパラメータストアから取得するので必要な権限を付与する。


```shell:IAMユーザ作成
% aws iam create-user --user-name cicd_user

{
    "User": {
        "Path": "/",
        "UserName": "cicd_user",
        "UserId": "***************",
        "Arn": "arn:aws:iam::***************:user/cicd_user",
        "CreateDate": "2020-05-21T15:05:18Z"
    }
}
```

```shell:アクセスキー付与
% aws iam create-access-key --user-name cicd_user
{
    "AccessKey": {
        "UserName": "cicd_user",
        "AccessKeyId": "***************",
        "Status": "Active",
        "SecretAccessKey": "***************",
        "CreateDate": "2020-05-22T00:42:37Z"
    }
}
```

```shell:権限付与〜policy確認
% aws iam attach-user-policy --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess  --user-name cicd_user
% aws iam attach-user-policy --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess --user-name cicd_user
% aws iam attach-user-policy --policy-arn arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess --user-name cicd_user
% aws iam list-attached-user-policies --user-name cicd_user
{
    "AttachedPolicies": [
        {
            "PolicyName": "AmazonEC2FullAccess",
            "PolicyArn": "arn:aws:iam::aws:policy/AmazonEC2FullAccess"
        },
        {
            "PolicyName": "AmazonS3FullAccess",
            "PolicyArn": "arn:aws:iam::aws:policy/AmazonS3FullAccess"
        },
        {
            "PolicyName": "AmazonSSMReadOnlyAccess",
            "PolicyArn": "arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess"
        }
    ]
}

```



## key作成

EC2に設定する公開鍵/秘密鍵を作成し、作成したものをパラメータストアに登録する。

```shell:key作成
% ssh-keygen -t rsa -b 4096  
```  


```shell:parameter_storeに登録
% aws ssm put-parameter --name "publickey" \
                      --type SecureString \
                      --value  "$(cat id_rsa.pub)"

{
    "Version": 1,
    "Tier": "Standard"
}

% aws ssm put-parameter --name "pravatekey" \
                      --type SecureString \
                      --value  "$(cat id_rsa)"

{
    "Version": 1,
    "Tier": "Standard"
}
```

## terraformコード作成

- data.tfで公開鍵をパラメータストアから取得
- backend.tfでtfstateをリモートで管理
- output.tfでセキュリティーグループのgroupidをファイルに出力 ※後のgithub-actionsで使用


```provider.tf
provider "aws" {
  profile = "default"
  version = "= 2.61"
  region  = "ap-northeast-1"
}
```

```backend.tf
terraform {
  backend "s3" {
    bucket  = "tf-sandbox-masa3521"
    region  = "ap-northeast-1"
    key     = "terraform.tfstate"
    encrypt = true
  }
}
```

```data.tf
data "aws_ssm_parameter" "publickey" {
  name            = "publickey"
  with_decryption = true
}
```


```variables.tf
variable "region" {
  default = "ap-northeast-1"
}

variable "system" {
  default = "sandbox"
}
```

```output.tf
resource "local_file" "sgroupid" {
  filename = "./group_id"
  content  = aws_security_group.sandboxSG.id
}
```

```main.tf

resource "aws_vpc" "sandboxVPC" {
  cidr_block           = "10.1.0.0/16"
  instance_tenancy     = "default"
  enable_dns_support   = "true"
  enable_dns_hostnames = "false"
  tags = {
    Name = var.system
    Env  = terraform.workspace
  }
}

resource "aws_route_table" "sanbboxRT" {
  vpc_id = aws_vpc.sandboxVPC.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.sandboxGW.id
  }

  tags = {
    Name = var.system
    Env  = terraform.workspace
  }
}

resource "aws_subnet" "sandboxSUBNET" {
  vpc_id     = aws_vpc.sandboxVPC.id
  cidr_block = "10.1.0.0/24"
  tags = {
    Name = var.system
    Env  = terraform.workspace
  }
}

resource "aws_internet_gateway" "sandboxGW" {
  vpc_id = aws_vpc.sandboxVPC.id
  tags = {
    Name = var.system
    Env  = terraform.workspace
  }
}

resource "aws_route_table_association" "sandboxRTA" {
  subnet_id      = aws_subnet.sandboxSUBNET.id
  route_table_id = aws_route_table.sanbboxRT.id
}

resource "aws_security_group" "sandboxSG" {
  name        = "sandboxSG"
  description = "Allow SSH inbound traffic"
  vpc_id      = aws_vpc.sandboxVPC.id
  ingress {
    from_port   = 80
    to_port     = 80
    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"]
  }
  tags = {
    Name = var.system
    Env  = terraform.workspace
  }
}

resource "aws_key_pair" "publickey" {
  key_name   = "key"
  public_key = data.aws_ssm_parameter.publickey.value
  tags = {
    Name = var.system
    Env  = terraform.workspace
  }
}

resource "aws_instance" "sandboxinstance" {
  key_name      = aws_key_pair.publickey.id
  ami           = "ami-0f310fced6141e627"
  instance_type = "t2.nano"
  vpc_security_group_ids = [
    aws_security_group.sandboxSG.id
  ]

  subnet_id                   = aws_subnet.sandboxSUBNET.id
  associate_public_ip_address = "true"
  tags = {
    Name = var.system
    Env  = terraform.workspace
  }
}
```

## ansible 作成

```ansile.cfg
[defaults]
deprecation_warnings = False
remote_user = ec2-user
private_key_file = ./privatekey
host_key_checking = False
```

```sandbox.yml
- hosts: tag_Name_sandbox
  roles:
    - htted
```

```yml:roles/tasks/main.yml
---
- name: install apache
  yum:
   name: httpd
   state: present
  become: true
- name: Start httpd
  service:
    name: httpd
    state: started
    enabled: yes
  become: true
```


## github-actions作成

- 実行の為にsecretsの登録が必要
- IPを自動で取得するためDynamic Inventoryで取得する。
- ansble実行の為秘密鍵のダウンロード
- ansible実行前にセキュリティーグループに自分のIPを登録、実行後に自分のIPを削除

### secrets登録
<img width="1039" alt="スクリーンショット 2020-05-29 1.59.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267983/e02eaa0d-36a2-1256-315e-cbd607059894.png">






```yml:terraform-ansible.yml

name: Terraform deploy to Azure

on:
  push:
    branches:    
      - master

jobs:
  terraform-ansible:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
      
    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ap-northeast-1

    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v1
      with:
        terraform_version: 0.12.9

    - name: Terraform Init
      run: terraform init
      working-directory: ${{ github.workspace }}/terraform

    - name: Terraform select
      run: terraform workspace select dev
      working-directory: ${{ github.workspace }}/terraform

    - name: Terraform Format
      run: terraform fmt -check
      working-directory: ${{ github.workspace }}/terraform

    - name: Terraform Plan
      run: terraform plan
      working-directory: ${{ github.workspace }}/terraform

    - name: Terraform Apply
      if: github.ref == 'refs/heads/master' && github.event_name == 'push'
      run: terraform apply -auto-approve
      working-directory: ${{ github.workspace }}/terraform

    - name: Set up Python 3.7
      uses: actions/setup-python@v2
      with:
        python-version: 3.7

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install boto ansible==2.9.2

    - name: Download privatekey
      run: aws ssm get-parameter --name "privatekey" --with-decryption | jq -r .Parameter.Value > ./privatekey
      working-directory: ${{ github.workspace }}/ansible

    - name: modify permission privatekey
      run: chmod 400 ./privatekey
      working-directory: ${{ github.workspace }}/ansible

    - name: open sg
      run:  aws ec2 authorize-security-group-ingress --group-id "$(cat ../terraform/group_id)" --protocol tcp --port 22 --cidr `curl inet-ip.info`/32
      working-directory: ${{ github.workspace }}/ansible

    - name: exec ansible
      run: ansible-playbook -i ec2.py sandbox.yml
      working-directory: ${{ github.workspace }}/ansible

    - name: close sg
      run:  aws ec2 revoke-security-group-ingress --group-id "$(cat ../terraform/group_id)" --protocol tcp --port 22 --cidr `curl inet-ip.info`/32
      working-directory: ${{ github.workspace }}/ansible
```

## github-actions 結果

<img width="2034" alt="スクリーンショット 2020-05-29 2.07.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267983/e2b37be5-4592-845e-9c98-22d05a47e297.png">



［参考］
https://www.terraform.io/docs/github-actions/setup-terraform.html
https://github.com/aws-actions/configure-aws-credentials
https://docs.ansible.com/ansible/latest/user_guide/intro_dynamic_inventory.html