背景
1ヶ月ほど前に、EC2 Systems Managerのドキュメントに"AWS-RunAnsiblePlaybook"が追加されました。
Running Ansible Playbooks using EC2 Systems Manager Run Command and State Manager
この機能を使うための事前設定を含めて、一通りの設定を行ってみます。
例によって、AWS CLIで実施します。
動作要件
- Managed Instanceとなるためのインスタンスプロファイルを設定していること
- SSM Agentがインストールされていること
- Ansibleがインストールされていること
大まかな手順
- Managed Instanceの作成
- Playbookの作成
- コマンドの実行(AWS-RunAnsiblePlaybook)
- 動作確認
前提条件
- 以降で利用するコマンドを実行する権限が付与されたインスタンス上で作業を実施しています。
- インスタンスプロファイルを設定しています。
- Amazon Linux上で実行しています。
0. 事前準備
リージョンの指定
コマンド
export AWS_DEFAULT_REGION="ap-northeast-1"
AWS CLIのインストール
コマンド
sudo pip install awscli
必要に応じてアップデートしてください。
コマンド
sudo pip install awscli -U
1. Managed Instanceの作成
テンプレートの作成
CloudFormationでまとめて作成します。
コマンド
CF_TEMPLATE_FILE_NAME="ec2-systems-manager.yml"
コマンド
cat << EOF > ${CF_TEMPLATE_FILE_NAME}
AWSTemplateFormatVersion: "2010-09-09"
Description: JAWS-UG CLI EC2 Systems Manager LT Ansible
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: "10.0.0.0/16"
IGW:
Type: AWS::EC2::InternetGateway
AttachIGW:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId:
Ref: VPC
InternetGatewayId:
Ref: IGW
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 0
- Fn::GetAZs: ""
CidrBlock: "10.0.0.0/24"
MapPublicIpOnLaunch: true
VpcId:
Ref: VPC
PublicRT:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: VPC
PublicDefaultRoute:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: "0.0.0.0/0"
GatewayId:
Ref: IGW
RouteTableId:
Ref: PublicRT
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId:
Ref: PublicSubnet
RouteTableId:
Ref: PublicRT
SecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupDescription: String
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
VpcId:
Ref: VPC
SecurityGroupIngress:
Type: "AWS::EC2::SecurityGroupIngress"
Properties:
GroupId:
Ref: SecurityGroup
IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Role:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Principal:
Service:
- "ec2.amazonaws.com"
Action:
- "sts:AssumeRole"
Path: "/"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"
InstanceProfile:
Type: "AWS::IAM::InstanceProfile"
Properties:
Path: "/"
Roles:
- Ref: Role
Instance:
Type: "AWS::EC2::Instance"
Properties:
ImageId: ami-bbf2f9dc
InstanceType: t2.micro
SecurityGroupIds:
- Ref: SecurityGroup
SubnetId:
Ref: PublicSubnet
IamInstanceProfile:
Ref: InstanceProfile
UserData:
Fn::Base64: |
#!/bin/bash
cd /tmp
sudo yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm
sudo pip install ansible
BucketPlaybook:
Type: AWS::S3::Bucket
Outputs:
InstanceID:
Value:
Ref: Instance
IPAddress:
Value:
!GetAtt Instance.PublicIp
BucketPlaybook:
Value:
Ref: BucketPlaybook
EOF
コマンド
aws cloudformation validate-template \
--template-body file://${CF_TEMPLATE_FILE_NAME}
結果
{
"CapabilitiesReason": "The following resource(s) require capabilities: [AWS::IAM::Role]",
"Description": "JAWS-UG CLI EC2 Systems Manager LT Ansible",
"Parameters": [],
"Capabilities": [
"CAPABILITY_IAM"
]
}
スタックの作成
コマンド
CF_STACK_NAME="TestAnsiblePlaybook"
コマンド
aws cloudformation create-stack \
--stack-name ${CF_STACK_NAME} \
--template-body file://${CF_TEMPLATE_FILE_NAME} \
--capabilities "CAPABILITY_IAM"
コマンド
aws cloudformation wait stack-create-complete \
--stack-name ${CF_STACK_NAME}
コマンド
aws cloudformation describe-stacks \
--stack-name ${CF_STACK_NAME}
インスタンスIDの確認
コマンド
OUTPUTKEY_INSTANCEID="InstanceID"
コマンド
INSTANCE_ID=$(aws cloudformation describe-stacks \
--stack-name ${CF_STACK_NAME} \
--query "Stacks[].Outputs[?OutputKey==\`${OUTPUTKEY_INSTANCEID}\`].OutputValue[]" \
--output text) \
&& echo ${INSTANCE_ID}
結果
i-*****************
Playbook用S3バケット名の確認
コマンド
OUTPUTKEY_BUCKETNAME="BucketPlaybook"
コマンド
BUCKET_NAME=$(aws cloudformation describe-stacks \
--stack-name ${CF_STACK_NAME} \
--query "Stacks[].Outputs[?OutputKey==\`${OUTPUTKEY_BUCKETNAME}\`].OutputValue[]" \
--output text) \
&& echo ${BUCKET_NAME}
結果(例)
temp-bucketplaybook-19b6mabyyf43m
IPアドレスの確認
コマンド
OUTPUTKEY_PUBLICIP="IPAddress"
コマンド
PUBLIC_IP=$(aws cloudformation describe-stacks \
--stack-name ${CF_STACK_NAME} \
--query "Stacks[].Outputs[?OutputKey==\`${OUTPUTKEY_PUBLICIP}\`].OutputValue[]" \
--output text) \
&& echo ${PUBLIC_IP}
結果(例)
54.**.**.**
2. Playbookの作成
以下のPlaybookを実行してみます。(本投稿の冒頭で紹介したブログから拝借)
Playbook
---
- hosts: all
become: true
tasks:
- name: gather ec2 facts
action: ec2_facts
- name: install apache on redhat or centos instances
yum: name=httpd state=present
when: ansible_os_family == "RedHat"
- name: install apache on debian or ubuntu instances
apt: name=apache2 state=present
when: ansible_os_family == "Debian"
- name: enable apache on startup and start service for redhat or centos
service: name=httpd enabled=yes state=started
when: ansible_os_family == "RedHat"
- name: enable apache on startup and start service for debian or ubuntu
service: name=apache2 enabled=yes state=started
when: ansible_os_family == "Debian"
Playbookの生成
コマンド
PLAYBOOK_FILE_NAME="test-playbook.yml"
コマンド
cat << EOF > ${PLAYBOOK_FILE_NAME}
---
- hosts: all
become: true
tasks:
- name: gather ec2 facts
action: ec2_facts
- name: install apache on redhat or centos instances
yum: name=httpd state=present
when: ansible_os_family == "RedHat"
- name: install apache on debian or ubuntu instances
apt: name=apache2 state=present
when: ansible_os_family == "Debian"
- name: enable apache on startup and start service for redhat or centos
service: name=httpd enabled=yes state=started
when: ansible_os_family == "RedHat"
- name: enable apache on startup and start service for debian or ubuntu
service: name=apache2 enabled=yes state=started
when: ansible_os_family == "Debian"
EOF
cat ${PLAYBOOK_FILE_NAME}
PlaybookをS3へアップロード
コマンド
aws s3 cp ${PLAYBOOK_FILE_NAME} s3://${BUCKET_NAME}
3. コマンドの実行(AWS-RunAnsiblePlaybook)
コマンド
PARAMETER='{"extravars":["SSM=True"],"check":["False"],"playbookurl":["s3://'${BUCKET_NAME}/${PLAYBOOK_FILE_NAME}'"]}' \
&& echo ${PARAMETER}
コマンド
COMMAND_ID=$(aws ssm send-command \
--document-name "AWS-RunAnsiblePlaybook" \
--instance-ids "${INSTANCE_ID}" \
--parameters ${PARAMETER} \
--query "Command.CommandId" \
--output text) \
&& echo ${COMMAND_ID}
コマンド
aws ssm get-command-invocation \
--command-id ${COMMAND_ID} \
--instance-id ${INSTANCE_ID} \
--query StandardOutputContent \
--output text
結果
ansible 2.3.1.0
config file =
configured module search path = Default w/o overrides
python version = 2.7.12 (default, Sep 1 2016, 22:14:00) [GCC 4.8.3 20140911 (Red Hat 4.8.3-9)]
download: s3://testansibleplaybook-bucketplaybook-hn4g6uw9j3jb/test-playbook.yml to ./playbook.yml
PLAY [all] *********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [gather ec2 facts] ********************************************************
ok: [localhost]
TASK [install apache on redhat or centos instances] ****************************
ok: [localhost]
TASK [install apache on debian or ubuntu instances] ****************************
skipping: [localhost]
TASK [enable apache on startup and start service for redhat or centos] *********
ok: [localhost]
TASK [enable apache on startup and start service for debian or ubuntu] *********
skipping: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0
4. 動作確認
インスタンスのPublic IPにアクセスして、テストページが表示されたら成功です。