Skip to main content

Posts about ansible

ansible vault

create a vault file

$ ansible-vault create test
New Vault password: 
Confirm New Vault password: 
:
:
$ ansible-vault view test
Vault password: 
test
$ cat test 
$ANSIBLE_VAULT;1.1;AES256
34373931643530353734633238383738363430616366366463653631316362353730613165316464
3834656662626430306662333038656263613139373036640a303131396539353663383932323133
63316564623264323434326138623737326463633830623465373131646134616662363565656132
3165616239303564330a646436643339303665336333363530346636666339643561336565393434
3766

We can use vault-id option for password instead of stdin.

$ ansible-vault view --vault-id test@prompt test
Vault password (test): 
test
$ cat vaultpass.test 
password

$ ansible-vault view --vault-id test@vaultpass.test test
test

We can also encrypt/decrpt any file

$ ansible-vault decrypt --vault-id test@vaultpass.test test
Decryption successful

$ cat test 
test

$ ansible-vault encrypt --vault-id test@vaultpass.test test
Encryption successful

example for encrypt var file.

$ cat ./roles/test/vars/main.yml 
target: ansible_os_family

$ ansible-vault encrypt --vault-id test@vaultpass.test ./roles/test/vars/main.yml 
Encryption successful

$ cat ./roles/test/vars/main.yml 
$ANSIBLE_VAULT;1.2;AES256;test
33653839336162646466373262663965636561366464643634643431633639343437633166636137
3236636535383832383932353865396630313266366264360a363134333532393038396561323137
30643734663961363561666461326362373035623131663637383636346633393737636234316163
3463663838626334660a616564343730343561656335343534353161666430313731376631623634
65643135323666303430616331666165656131373639393936333731626131653764

ansible-playbook example using encrypted file.

$ cat ./roles/test/tasks/main.yml 
- name: print ansible facts
  debug:
    var: "{{ target }}"

$ cat hosts 
[test]
localhost ansible_connection=local

$ ansible-playbook -i hosts --vault-id test@vaultpass.test --list-tasks test.yml 

playbook: test.yml

  play #1 (test): ansible vault test    TAGS: []
    tasks:
      test : print ansible facts    TAGS: []


$ ansible-playbook -i hosts --vault-id test@vaultpass.test test.yml 

PLAY [ansible vault test] *****************************************************************************************************

TASK [Gathering Facts] ********************************************************************************************************
ok: [localhost]

TASK [test : print ansible facts] *********************************************************************************************
ok: [localhost] => {
    "ansible_os_family": "Debian"
}

PLAY RECAP ********************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

terraform docker container

Dockerfile

From centos:centos8

ARG VERSION=0.12.25
RUN yum install -y unzip python2-pip openssh-clients && \
    yum clean all && \
    curl -s https://releases.hashicorp.com/terraform/${VERSION}/terraform_${VERSION}_linux_amd64.zip -o terraform.zip && \
    unzip terraform.zip && \
    rm terraform.zip && \
    mv terraform /usr/local/bin && \
    pip2 install ansible boto boto3 awscli && \
    ln -s /usr/bin/python2.7 /usr/bin/python && \
    mkdir /tmp/terraform && \
    useradd -m docker

VOLUME ["/tmp/terraform"]
USER "docker"
WORKDIR "/tmp/terraform"
CMD ["/bin/bash"]

build an image

$ sudo docker build --build-arg VERSION=0.12.25 -t terraform:0.12.25 .
$ sudo docker tag terraform:0.12.25 terraform:latest

test the image

$ sudo docker run -v /mylocal/terraform:/tmp/terraform --rm -it terraform:latest terraform --version
$ sudo docker run -v /mylocal/terraform:/tmp/terraform --rm -it terraform:latest ansible --version
$ sudo docker run -v /mylocal/terraform:/tmp/terraform --rm -it terraform:latest bash

create variable file /mydir/credentials

AWS_ACCESS_KEY_ID=xxxx
AWS_SECRET_ACCESS_KEY=xxxx
AWS_DEFAULT_REGION=xxxx

create wrapper script infradeploy.sh

docker run -v /mylocal/terraform:/tmp/terraform --env-file /mydir/credentials --rm -it terraform:latest $*

test terraform and ansible work

$ sudo sh infradeploy.sh terraform show
$ sudo sh infradeploy.sh ./ec2.py --list
$ sudo sh infradeploy.sh ansible -i ec2.py -u admin ec2 -m ping --private-key id_rsa.mykey
$ sudo sh infradeploy.sh ansible-playbook --check -e @extravars.json playbook.yml

As described at this site we can use ec2.py and ec2.ini(optional) for dynamic inventory When I ran ec2.py I got ImportError: No module named ansible.module_utils in the case I installed ansible from ubuntu repository. It seems ansible should be installed with pip if you want to use ec2.py

playbook sample

- name: test playbook
  hosts: tag_Name_tagname
  remote_user: admin
  become: yes
  vars:
    ansible_ssh_private_key_file: "./id_rsa.mykey"
  tasks:
    - name: install some packages
      apt:
        name: ['make','screen']
        state: present
        install_recommends: no
      when: ansible_pkg_mgr == 'apt'
      tags: packages

    - name: create a directory
      file:
        path: /my/direcoty
        state: directory
        owner: admin
        mode: '0755'

$ sudo sh infradeploy.sh ansible-playbook --check --diff -i ec2.py playbook.yml
$ sudo sh infradeploy.sh ansible-playbook --diff -i ec2.py playbook.yml

first step of terraform

install

$ wget url https://releases.hashicorp.com/terraform/x.xx.xx/terraform_x.xx.xx_linux_amd64.zip
$ unzip terraform_x.xx.xx_linux_amd64.zip
$ cp terraform /usr/local/bin

sample varible file

terraform.tfvars

# region   = "ap-northeast-2"
# az = {
#   zone0    = "ap-northeast-2a"
#   zone1    = "ap-northeast-2b"
# }
# ami     = "ami-0d79f772de48b11f7"
cidr_block   = "172.16.0.0/16"
cidr_subnet0 = "172.16.0.0/24"
cidr_subnet1 = "172.16.10.0/24"

sample tf file

test.tf

variable "region" {
  default = "ap-northeast-1"
}
variable "az" {
  default = {
    zone0 = "ap-northeast-1a"
    zone1 = "ap-northeast-1c"
  }
}
variable "ami" {
  default = "ami-03a1ce3bba6d67270"
}
variable "instance_type" {
  default = "t3.nano"
}
variable "cidr_block" {}
variable "cidr_subnet0" {}
variable "cidr_subnet1" {}
variable "operation_addr" {}
variable "key_name" {}
variable "private_key" {}

provider "aws" {
  region = var.region
}

resource "aws_vpc" "terraform_test_vpc" {
  cidr_block           = var.cidr_block
  instance_tenancy     = "default"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "terraform_test"
  }
}

resource "aws_subnet" "terraform_test_subnet0" {
  vpc_id            = aws_vpc.terraform_test_vpc.id
  cidr_block        = var.cidr_subnet0
  availability_zone = var.az.zone0

  tags = {
    Name = "terraform_test"
  }
}

resource "aws_subnet" "terraform_test_subnet1" {
  vpc_id            = aws_vpc.terraform_test_vpc.id
  cidr_block        = var.cidr_subnet1
  availability_zone = var.az.zone1

  tags = {
    Name = "terraform_test"
  }
}

resource "aws_internet_gateway" "terraform_test_igw" {
  vpc_id = aws_vpc.terraform_test_vpc.id

  tags = {
    Name = "terraform_test"
  }
}

resource "aws_route_table" "terraform_test_route" {
  vpc_id = aws_vpc.terraform_test_vpc.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.terraform_test_igw.id
  }

  tags = {
    Name = "terraform_test"
  }
}

resource "aws_route_table_association" "terraform_test_subnet0" {
  subnet_id      = aws_subnet.terraform_test_subnet0.id
  route_table_id = aws_route_table.terraform_test_route.id
}

resource "aws_route_table_association" "terraform_test_subnet1" {
  subnet_id      = aws_subnet.terraform_test_subnet1.id
  route_table_id = aws_route_table.terraform_test_route.id
}

resource "aws_security_group" "sg_ssh_web" {
  name = "terraform_test_security_group"
  description = "allow inbound ssh and web traffic"
  vpc_id = aws_vpc.terraform_test_vpc.id
  ingress {
    from_port = 22
    to_port = 22
    protocol = "tcp"
    cidr_blocks = [var.operation_addr]
  }
  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"]
  }
}

resource "aws_instance" "ec2_public" {
    ami           = var.ami
    instance_type = var.instance_type
    key_name      = var.key_name
    vpc_security_group_ids = [
      aws_security_group.sg_ssh_web.id,
    ]
    subnet_id = aws_subnet.terraform_test_subnet0.id
    associate_public_ip_address = "true"
    user_data = file("userdata.sh")
    connection {
      user = "admin"
      host = self.public_ip
      private_key = file(var.private_key)
    }
    provisioner "file" {
      source      = "index.html"
      destination = "~/index.html"
    }
    provisioner "remote-exec" {
      inline = [
        "sudo apt update",
        "sudo apt install -y nginx",
        "sudo mv ~/index.html /var/www/html"
      ]
    }

    tags = {
        Name = "terraform_test"
    }
}

output "public_dns" {
  value = aws_instance.ec2_public.public_dns
}

ininialize

install plugin file under the current directory

$ terraform init

validate tf file

$ terraform validate

apply

$ terraform plan -out=apply.plan -var 'operation_addr=xxx.xxx.xxx.xxx/xx' -var 'key_name=mykey' -var 'private_key=./id_rsa.mykey'
$ terraform apply -auto-approve "apply.plan"

or when destroy

$ terraform plan -destroy -out=apply.plan -var 'operation_addr=xxx.xxx.xxx.xxx/xx' -var 'key_name=mykey' -var 'private_key=./id_rsa.mykey' -target aws_instance.web1
$ terraform apply -auto-approve "destroy.plan"

confirm result

$ terraform show

destroy

$ terraform destroy -auto-approve -var 'operation_addr=xxx.xxx.xxx.xxx/xx' -var 'key_name=mykey' -var 'private_key=./id_rsa.mykey'

sample ansible playbook

apply.playbook

- hosts: localhost
  connection: local
  gather_facts: no

  tasks:
    - name: terraform plan
      terraform:
        project_path: './'
        state: planned
        plan_file: ansible.plan
        variables:
          operation_addr: "{{ operation_addr }}"
          key_name: "{{ key_name }}"
          private_key: "{{ private_key }}"
      register: result

    - name: debug result
      debug: 
        var: result

    - name: terraform apply
      terraform:
        project_path: './'
        state: present
        plan_file: ansible.plan
        variables:
          operation_addr: "{{ operation_addr }}"
          key_name: "{{ key_name }}"
          private_key: "{{ private_key }}"
      when: not ansible_check_mode
      register: result

    - name: debug result
      debug: 
        var: result
      when: not ansible_check_mode

destroy.playbook

- hosts: localhost
  connection: local
  gather_facts: no

  tasks:
    - name: terraform destroy
      terraform:
        project_path: './'
        state: absent
        plan_file: ansible.plan
        variables:
          operation_addr: "{{ operation_addr }}"
          key_name: "{{ key_name }}"
          private_key: "{{ private_key }}"
      when: not ansible_check_mode
      register: result

    - name: debug result
      debug: 
        var: result
      when: not ansible_check_mode

apply and destroy with ansible

$ jq . extravars.json
{
  "operation_addr": "xxx.xxx.xxx.xxx/xx",
  "key_name": "mykey",
  "private_key": "id_rsa.mykey"
}
$ ansible-playbook --check -e @extravars.json apply.playbook
$ ansible-playbook -e @extravars.json apply.playbook
$ ansible-playbook --check -e @extravars.json destroy.playbook
$ ansible-playbook -e @extravars.json destroy.playbook

CloudFormation

sample template of CloudFormation in yaml format

$ cat mystack.yml
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  FirstVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      Tags:
      - Key: Name
        Value: myfirststack

validate template file

$ aws cloudformation validate-template --template-body file://mystack.yml

create stack

$ aws cloudformation create-stack --stack-name mystack --template-body file://mystack.yml

confrim information of created stack

$ aws cloudformation list-stacks
$ aws cloudformation describe-stacks --stack-name mystack
$ aws cloudformation describe-stack-events --stack-name mystack --query 'StackEvents[?ResourceType==`AWS::CloudFormation::Stack`]'

$ aws cloudformation describe-stack-resources --stack-name mystack
$ aws cloudformation list-stack-resources --stack-name mystack

$ aws cloudformation get-template --stack-name mystack
$ aws cloudformation get-template --stack-name mystack | jq -r '.TemplateBody'
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  FirstVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      Tags:
      - Key: Name
        Value: first-VPC

update template file and validate it

$ diff -u mystack.yml{,.bak}
--- mystack.yml 2019-09-16 11:23:04.368417527 +0900
+++ mystack.yml.bak     2019-09-16 10:51:35.053928361 +0900
@@ -1,12 +1,9 @@
 AWSTemplateFormatVersion: '2010-09-09'
-Parameters:
-  CidrBlock:
-    Type: String
 Resources:
   FirstVPC:
     Type: AWS::EC2::VPC
     Properties:
-      CidrBlock: !Ref CidrBlock
+      CidrBlock: 10.0.0.0/16
       Tags:
       - Key: Name
-        Value: mystack-VPC
+        Value: first-VPC

$ aws cloudformation validate-template --template-body file://mystack.yml

update the stack

$ aws cloudformation update-stack --stack-name mystack --template-body file://mystack.yml --parameters ParameterKey=CidrBlock,ParameterValue=10.0.0.0/17

confirm

delete the stack

$ aws cloudformation delete-stack --stack-name mystack

create stack with ansible

$ cat mystack.yml 
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  FirstVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.10.0.0/16
      Tags:
      - Key: Name
        Value: mystack

$ cat playbook.yml 
- hosts: 127.0.0.1
  connection: local
  gather_facts: False
  tasks:
    - name: create a cloudformation stack
      cloudformation:
        stack_name: "mystack"
        state: "present"
        region: "ap-northeast-1"
        disable_rollback: true
        template: "mystack.yml"

$ ansible-playbook --syntax-check playbook.yml 
$ ansible-playbook --list-tasks playbook.yml 
$ ansible-playbook --check playbook.yml
$ ansible-playbook playbook.yml

update stack with ansible

$ cat mystack.yml
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  CidrBlock:
    Type: String
Resources:
  FirstVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref CidrBlock
      Tags:
      - Key: Name
        Value: mystack

$ cat playbook.yml 
- hosts: 127.0.0.1
  connection: local
  gather_facts: False
  tasks:
    - name: create a cloudformation stack
      cloudformation:
        stack_name: "mystack"
        state: "present"
        region: "ap-northeast-1"
        disable_rollback: true
        template: "mystack.yml"
        template_parameters:
          CidrBlock: "10.20.0.0/16"

$ ansible-playbook playbook.yml

variable from command line

$ cat mystack.yml
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  CidrBlock:
    Type: String
Resources:
  FirstVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref CidrBlock
      Tags:
      - Key: Name
        Value: mystack

$ cat playbook.yml
- hosts: 127.0.0.1
  connection: local
  gather_facts: False
  tasks:
    - name: create a cloudformation stack
      cloudformation:
        stack_name: "mystack"
        state: "present"
        region: "ap-northeast-1"
        disable_rollback: true
        template: "mystack.yml"
        template_parameters:
          CidrBlock: "{{ CidrBlock }}"

$ ansible-playbook -e "CidrBlock=10.30.0.0/16" playbook.yml

delete stack with ansible

$ cat playbook.yml
- hosts: 127.0.0.1
  connection: local
  gather_facts: False
  tasks:
    - name: delete a cloudformation stack
      cloudformation:
        stack_name: "mystack"
        state: "absent"
        region: "ap-northeast-1"

$ ansible-playbook playbook.yml

ansible

エージェントレスの構成管理ツール。

対象ホストでは、エージェント等をインストールする必要がなく、基本的に下記を満たせばよい。

  • Pythonが動くこと
  • 相手がリモートホストならsshでログインできること

(ただし、使うモジュールによっては追加パッケージが必要。例:aptモジュールを使うためにはpython-aptが必要)

「あるべき姿」をYAMLで記載する。(記載したファイル(群)をplaybookと呼ぶ)

ansible
  -i hosts インベントリファイルを指定
  -m ping 疎通確認
  -m setup ファクト収集
  -a 'command -h' 指定したコマンドを実行
  --ask-pass ログインで必要となるパスワードを要求
  targethost 対象ホストを指定

リポジトリファイル(hosts)の内容の例

[myserver]
192.168.1.1 ansible_ssh_user=myusername
[desktop]
myhostname  ansible_connection=local

疎通確認の例

$ ansible -i hosts -m ping 192.168.1.1 --ask-pass
$ ansible -i hosts -m ping myhostname
    $ ansible -i hosts -m ping 192.168.1.1 --ask-pass --ssh-common-args '-o PubkeyAuthentication=no'

ansible-playbook
  -i hosts
  --syntax-check  プレイブックの文法チェックのみ
  --list-tasks   プレイブックのタスクをリスト表示
  --check ドライラン
  --ask-sudo-pass sudoで必要となるパスワードを要求
  --ask-su-pass suで必要となるパスワードを要求
  --ask-pass ログインで必要となるパスワードを要求
  --diff show differences
  --tags "tag1,tag2,..."
  --skip-tags "tag1,tag2,..."
  playbook.yml プレイブックを指定

プレイリストの内容確認、実行前確認、設定反映の例

$ ansible-playbook -i hosts --syntax-check myserver.yml
$ ansible-playbook -i hosts --list-tasks myserver.yml 
$ ansible-playbook -i hosts --check myserver.yml --ask-pass
$ ansible-playbook -i hosts myserver.yml --ask-pass

Sample ansible playbook in a single yaml file