[モバイルアーキテクチャ ] Cognitoにどこまで任せるべきか?研究してみた

CognitoとはAWSサービスの一つであり、主にモバイルアプリの認証システムとして利用されている。
Cognitoには主に3つの機能があり、それぞれ以下のように自分は解釈している
(ツッコミをいただくために後悔してるので、間違ってたら是非教えてください)

Cognitoのサービス達

Cognito UserPool

概要

emailの確認、SMSによる確認、パスワードの再発行などもやってくれるメールによる認証機能。

メリット

  • ステップが複雑なメール認証機能を実装する手間がない
  • ユーザーの認証ステップの保持、Email, Passwordの暗号化など、面倒なデータを保持せずに済む(AWS管理コンソールにおいて、ユーザーの権限などいじる必要はあるけど)

デメリット

  • 既存のメール認証システムから移行する場合、name, email, phone numberなどはcsvでimportできるが、passwordだけはimportできないので、ユーザーにpasswordの再設定を依頼する必要がある。そのため、移行コストは高い
  • データを自分たちで保持していないため、トラブルシューティングの工数が増す危険がある

Cognito IdentityPool

概要

メール、Google, Facebookなどで認証されたユーザーに対して、特定のAWSサービスへのアクセスを許可する(=認可)の機能。
未認証ユーザーも取り扱い可能で、未認証ユーザーは認証ユーザーと別のrole(権限)を与えることができる

メリット

  • アクセストークンに関する実装をする必要がない
  • サーバーレスアーキテクチャーに移行しやすい(もとからサーバーレスで組むつもりならほぼ必須要件)

デメリット

  • API Gateway経由で、APIを公開する際に、Lambdaならuser情報を取得できるが、HTTPプロキシでELBやEC2を繋いだときは、ユーザー情報が取得できない(?)
  • tokenの受け渡しだけで、未認証ユーザーの照会をすることができない?(idをパラメータに含めないといけない?)

Cognito Sync

概要

Cognito IdentityPoolに存在するユーザーのユーザー設定を保持・同期できるサービス。認証認可とは関係ない。

メリット

機種変時、マルチデバイス対応時に、設定項目を同期するのに便利。

デメリット

今の所見つかってはいないが、iOSでいうUserDefaultに入れるような情報の保持、復元など用途が限定的なので、使わなくていいアプリケーションもたくさんありそう

考察

UserPool

開発コスト

[使わない場合]
– 単純なemail認証であったとしても、rubyだとdeviseなど便利なgemがあるものの、センシティブなデータを扱うために確認工数なども膨れる可能性が高い
– SMS認証も必須だとすると、それをサポートしてるライブラリも少ないため開発工数は爆発的に膨らみそう

[使った場合]
– ライブラリの利用方法を覚える必要があるものの、Mobile Hubなどを利用することでそのコストを格段に下げることができる
– SMS周りは特にハマるらしい。http://qiita.com/aki/items/e35e1bea8c27cfab5e9e
– アクセストークンの検証も忘れがち。http://qiita.com/devalon/items/721ef4bdec80e1e6847c, http://qiita.com/ya-mada/items/154ea6e10f9f788bfdd5

運用コスト

[使わない場合]
– いつでもデータにアクセス可能なので、ユーザー固有の問題に対するトラブルシューティングが比較的容易
– 認証の度にDBへのアクセスが走るためシステムリソースが喰われる
– センシティブなデータにいつでもアクセス可能なため、ルール作りや社内でのアクセス制御が面倒
– UserPoolを使いたくなった時に、ユーザーにpasswordの再設定を依頼する必要がある

[使った場合]
– データを自分たちで保持していないため、トラブルシューティングに時間がかかることもありそう
– AWSのユーザー権限で、アクセス制御が可能なので、そこは簡単
– AWS Cognitoが落ちたら、機能の大半が使えなくなるというリスクを孕んでいる
– export機能はないので、AWS Cognitoから独自の認証機能に移行する場合は、APIを叩いてデータをexportした上で、ユーザーにpasswordの再設定を依頼する必要がある

所感

まだ認証システムを独自で持ってないなら、使うと大幅に開発コストが削減できそう

Cognito IdentityPool

開発コスト

[使わない場合]
– Facebook, Googleなどのuidに相当する情報を保持する必要がある
– クライアントに一時的なアクセストークンを発行する必要がある
– アクセストークンの検証機構を作る必要がある

[使った場合]
– uidの保持をしてくれる
– アクセストークンの発行、検証をawsがよしなにしてくれるが、ec2, elbでそれをやろうとすると、前段にAPI Gatewayをhttp proxyとして置く必要がある
– API Gateway + Lambda ならユーザーの照会までしてくれるが、ec2, elbだとそれができない?
– elb, ec2との相性はわるそうで、未認証ユーザーの照会ができない?
– 上記の理由で構成が複雑化する

運用コスト

[使わない場合]
– uidなどの情報へのアクセス制御をする必要がある
– アクセストークン発行に際して、システムリソースを食う
– 保守しないといけないコードが増える
– サーバーレスアーキテクチャにする際にIdentityPoolに乗り換えるか、STSへのリクエストを独自で頑張るかする必要がある。1ユーザーに認証情報を複数持てるようにしておけば、IdentityPoolのidentityIdをuidとして紐付けできるので、移行コストはそこまで高くないと思う(?)

[使った場合]
– 一部システムを移譲できるものの、構成が複雑になり、全員がクラウドネイティブな考えがない限り、運用が大変になりそう
– クラウドネイティブ的な構成になると、デバッグが大変そう(慣れてないだけ?)

所感

使うことによって、構成が限定されるため初期から使う必要はなさそう。
サービスが大きくなってきて、クラウドを存分に使いたくなったら検討したい

疑問

誰か、もし知ってたらおしえてください

  • API Gateway経由で、APIを公開する際に、Lambdaならuser情報を取得できるが、HTTPプロキシでELBやEC2を繋いだときは、ユーザー情報が取得できないっぽいんですが、何か方法ないですかね?(API Gatewayにおいて、 Integration Type = AWS Service というのがあるけど、これでどうにかできないものか…)

続きを読む

[JAWS-UG CLI] IAM #xx インスタンスプロファイルへのロール追加 (opsworksインスタンス用)

前提条件

IAMへの権限

IAMに対してフル権限があること。

AWS CLIのバージョン

以下のバージョンで動作確認済

  • AWS CLI 1.11.14
コマンド
aws --version

結果(例):

  aws-cli/1.11.34 Python/2.7.10 Darwin/15.6.0 botocore/1.4.91

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

0. 準備

0.1. 変数の確認

プロファイルが想定のものになっていることを確認します。

変数の確認
aws configure list

結果(例):

        Name                    Value             Type    Location
        ----                    -----             ----    --------
     profile       lambdaFull-prjz-mbp13        env    AWS_DEFAULT_PROFILE
  access_key     ****************XXXX shared-credentials-file
  secret_key     ****************XXXX shared-credentials-file
      region        ap-northeast-1        env    AWS_DEFAULT_REGION

0.2. インスタンスプロファイルの指定

インスタンスプロファイルを指定します。

変数の設定
IAM_INSTANCE_PROFILE_NAME='aws-opsworks-ec2-role'

0.3. IAMロールの指定

インスタンスプロファイルにアタッチするIAMロールを指定します。

変数の設定
IAM_ROLE_NAME='aws-opsworks-ec2-role'

1. 事前作業

インスタンスプロファイルを確認します。

コマンド
aws iam list-instance-profiles-for-role 
        --role-name ${IAM_ROLE_NAME} 
        --query 'InstanceProfiles[].InstanceProfileName'

結果(例):

  []

2. ロールのアタッチ

インスタンスプロファイルにロールをアタッチします。

変数の確認
cat << ETX

        IAM_INSTANCE_PROFILE_NAME: ${IAM_INSTANCE_PROFILE_NAME}
        IAM_ROLE_NAME:             ${IAM_ROLE_NAME}

ETX
コマンド
aws iam add-role-to-instance-profile 
        --instance-profile-name ${IAM_INSTANCE_PROFILE_NAME} 
        --role-name ${IAM_ROLE_NAME}

結果:

  (戻り値なし)

3. 事後作業

インスタンスプロファイルの確認

コマンド
aws iam list-instance-profiles-for-role 
        --role-name ${IAM_ROLE_NAME} 
        --query 'InstanceProfiles[].InstanceProfileName'

結果(例):

  [
      "aws-opsworks-ec2-role"
  ]

完了

続きを読む

EC2 Systems ManagerでAnsibleを使ってみた

背景

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がインストールされていること

大まかな手順

  1. Managed Instanceの作成
  2. Playbookの作成
  3. コマンドの実行(AWS-RunAnsiblePlaybook)
  4. 動作確認

前提条件

  • 以降で利用するコマンドを実行する権限が付与されたインスタンス上で作業を実施しています。

    • インスタンスプロファイルを設定しています。
  • 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にアクセスして、テストページが表示されたら成功です。

続きを読む

[JAWS-UG CLI] OpsWorks #1 スタックの作成

前提条件

OpsWorksへの権限

OpsWorksに対してフル権限があること。

AWS CLIのバージョン

以下のバージョンで動作確認済

  • AWS CLI 1.11.14
コマンド
aws --version

結果(例):

  aws-cli/1.11.102 Python/2.7.12 Linux/4.4.11-23.53.amzn1.x86_64 botocore/1.5.65

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

0. 準備

まず変数の確認をします。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE:              (0.1) ${AWS_DEFAULT_PROFILE}
        AWS_DEFAULT_REGION:               (0.2) ${AWS_DEFAULT_REGION}
        IAM_ROLE_ARN:                     (0.3) ${IAM_ROLE_ARN}
        IAM_INSTANCE_PROFILE_ARN:         (0.4) ${IAM_INSTANCE_PROFILE_ARN}
        VPC_ID:                           (0.5) ${VPC_ID}
        VPC_SUBNET_ID:                    (0.6) ${VPC_SUBNET_ID}
        OPSW_STACK_NAME:                  (0.8) ${OPSW_STACK_NAME}
        OPSW_ATTRIBUTE:                   (0.9) ${OPSW_ATTRIBUTE}
        OPSW_CONF_MANAGER_NAME            (0.10) ${OPSW_CONF_MANAGER_NAME}
        OPSW_CONF_MANAGER_VERSION         (0.10) ${OPSW_CONF_MANAGER_VERSION}
        OPSW_CUSTOM_COOKBOOK_SOURCE_TYPE: (0.11) ${OPSW_CUSTOM_COOKBOOK_SOURCE_TYPE}
        OPSW_CUSTOM_COOKBOOK_SOURCE_URL:  (0.11) ${OPSW_CUSTOM_COOKBOOK_SOURCE_URL}
        OPSW_DEFAULT_ROOT_DEVICE_TYPE:    (0.12) ${OPSW_DEFAULT_ROOT_DEVICE_TYPE}
        OPSW_DEFAULT_OS:                  (0.13) ${OPSW_DEFAULT_OS}

ETX

結果(例):

  AWS_DEFAULT_PROFILE:              (0.1) opsworksFull-prjZ-mbp13
  AWS_DEFAULT_REGION:               (0.2) ap-northeast-1
  IAM_ROLE_ARN:                     (0.3) arn:aws:iam::XXXXXXXXXXXX:role/aws-opsworks-service-role
  IAM_INSTANCE_PROFILE_ARN:         (0.4) arn:aws:iam::XXXXXXXXXXXX:instance-profile/aws-opsworks-ec2-role
  VPC_ID:                           (0.5) <スタックを起動するVPC>
  VPC_SUBNET_ID:                    (0.6) <スタックを起動するサブネット>
  OPSW_STACK_NAME:                  (0.8) My Sample Stack (Linux)
  OPSW_ATTRIBUTE:                   (0.9) { "Color": "rgb(45, 114, 184)" }
  OPSW_CONF_MANAGER_NAME            (0.10) Chef
  OPSW_CONF_MANAGER_VERSION         (0.10) 12
  OPSW_CUSTOM_COOKBOOK_SOURCE_TYPE: (0.11) archive
  OPSW_CUSTOM_COOKBOOK_SOURCE_URL:  (0.11) https://s3.amazonaws.com/opsworks-demo-assets/opsworks-linux-demo-cookbooks-nodejs.tar.gz
  OPSW_DEFAULT_ROOT_DEVICE_TYPE:    (0.12) ebs
  OPSW_DEFAULT_OS:                  (0.13) Amazon Linux 2017.03

変数が入っていない、適切でない場合は、それぞれの手順番号について作業を
行います。

0.1. プロファイルの指定

プロファイルの一覧を確認します。

コマンド
cat ~/.aws/credentials 
       | grep '[' 
       | sed 's/[//g' | sed 's/]//g'

結果(例):

  iamFull-prjz-mbpr13
  opsworksFull-prjZ-mbp13
変数の設定
export AWS_DEFAULT_PROFILE='opsworksFull-prjZ-mbp13'

0.2. リージョンの決定

変数の設定
export AWS_DEFAULT_REGION='ap-northeast-1'

0.3. IAMロールの指定

変数の設定
IAM_ROLE_NAME='aws-opsworks-service-role'
コマンド
IAM_ROLE_ARN=$( 
        aws iam get-role 
          --role-name ${IAM_ROLE_NAME} 
          --query 'Role.Arn' 
          --output text 
) 
        && echo ${IAM_ROLE_ARN}

結果(例):

  arn:aws:iam::XXXXXXXXXXXX:role/aws-opsworks-service-role

0.4. インスタンスプロファイルの指定

変数の設定
IAM_INSTANCE_PROFILE_NAME='aws-opsworks-ec2-role'
コマンド
IAM_INSTANCE_PROFILE_ARN=$( 
        aws iam get-instance-profile 
          --instance-profile-name ${IAM_INSTANCE_PROFILE_NAME} 
          --query 'InstanceProfile.Arn' 
          --output text 
) 
        && echo ${IAM_INSTANCE_PROFILE_ARN}

結果(例):

  arn:aws:iam::XXXXXXXXXXXX:instance-profile/aws-opsworks-ec2-role

0.5. VPC IDの指定

変数の設定
VPC_ID=
コマンド
VPC_ID=$( 
        aws ec2 describe-vpcs 
          --filters Name=cidr,Values=${VPC_CIDR} 
          --query 'Vpcs[].VpcId' 
          --output text 
) 
        && echo ${VPC_ID}

結果(例):

  vpc-xxxxxxxx

0.6. デフォルトサブネットIDの指定

変数の設定
VPC_SUBNET_ID=
コマンド
VPC_SUBNET_ID=$( 
        aws ec2 describe-subnets 
          --filters Name=cidrBlock,Values=${VPC_SUBNET_CIDR} 
          --query 'Subnets[].SubnetId' 
          --output text 
) 
        && echo ${VPC_SUBNET_ID}

結果(例):

  subnet-xxxxxxxx

0.8. スタック名の指定

変数の設定
OPSW_STACK_NAME='My Sample Stack (Linux)'

0.9. スタックのアトリビュートの指定

変数の設定
OPSW_ATTRIBUTE='{ "Color": "rgb(45, 114, 184)" }'

0.10. 設定マネージャの指定

変数の設定
OPSW_CONF_MANAGER_NAME='Chef'
変数の設定
OPSW_CONF_MANAGER_VERSION='12'

0.11. カスタムクックブックの指定

変数の設定
OPSW_CUSTOM_COOKBOOK_SOURCE_TYPE='archive'
変数の設定
OPSW_CUSTOM_COOKBOOK_SOURCE_URL='https://s3.amazonaws.com/opsworks-demo-assets/opsworks-linux-demo-cookbooks-nodejs.tar.gz'

0.12. インスタンスのルートデバイスの指定

変数の設定
OPSW_DEFAULT_ROOT_DEVICE_TYPE='ebs'

0.13. デフォルトOSの指定

変数の設定
OPSW_DEFAULT_OS='Amazon Linux 2017.03'

再確認

設定されている変数の内容を再確認します。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE:              (0.1) ${AWS_DEFAULT_PROFILE}
        AWS_DEFAULT_REGION:               (0.2) ${AWS_DEFAULT_REGION}
        IAM_ROLE_ARN:                     (0.3) ${IAM_ROLE_ARN}
        IAM_INSTANCE_PROFILE_ARN:         (0.4) ${IAM_INSTANCE_PROFILE_ARN}
        VPC_ID:                           (0.5) ${VPC_ID}
        VPC_SUBNET_ID:                    (0.6) ${VPC_SUBNET_ID}
        OPSW_STACK_NAME:                  (0.8) ${OPSW_STACK_NAME}
        OPSW_ATTRIBUTE:                   (0.9) ${OPSW_ATTRIBUTE}
        OPSW_CONF_MANAGER_NAME            (0.10) ${OPSW_CONF_MANAGER_NAME}
        OPSW_CONF_MANAGER_VERSION         (0.10) ${OPSW_CONF_MANAGER_VERSION}
        OPSW_CUSTOM_COOKBOOK_SOURCE_TYPE: (0.11) ${OPSW_CUSTOM_COOKBOOK_SOURCE_TYPE}
        OPSW_CUSTOM_COOKBOOK_SOURCE_URL:  (0.11) ${OPSW_CUSTOM_COOKBOOK_SOURCE_URL}
        OPSW_DEFAULT_ROOT_DEVICE_TYPE:    (0.12) ${OPSW_DEFAULT_ROOT_DEVICE_TYPE}
        OPSW_DEFAULT_OS:                  (0.13) ${OPSW_DEFAULT_OS}

ETX

結果(例):

  AWS_DEFAULT_PROFILE:              (0.1) opsworksFull-prjZ-mbp13
  AWS_DEFAULT_REGION:               (0.2) ap-northeast-1
  IAM_ROLE_ARN:                     (0.3) arn:aws:iam::XXXXXXXXXXXX:role/aws-opsworks-service-role
  IAM_INSTANCE_PROFILE_ARN:         (0.4) arn:aws:iam::XXXXXXXXXXXX:instance-profile/aws-opsworks-ec2-role
  VPC_ID:                           (0.5) <スタックを起動するVPC>
  VPC_SUBNET_ID:                    (0.6) <スタックを起動するサブネット>
  OPSW_STACK_NAME:                  (0.8) My Sample Stack (Linux)
  OPSW_ATTRIBUTE:                   (0.9) { "Color": "rgb(45, 114, 184)" }
  OPSW_CONF_MANAGER_NAME            (0.10) Chef
  OPSW_CONF_MANAGER_VERSION         (0.10) 12
  OPSW_CUSTOM_COOKBOOK_SOURCE_TYPE: (0.11) archive
  OPSW_CUSTOM_COOKBOOK_SOURCE_URL:  (0.11) https://s3.amazonaws.com/opsworks-demo-assets/opsworks-linux-demo-cookbooks-nodejs.tar.gz
  OPSW_DEFAULT_ROOT_DEVICE_TYPE:    (0.12) ebs
  OPSW_DEFAULT_OS:                  (0.13) Amazon Linux 2017.03

1. 事前作業

2. 本作業

作成

変数の設定
OPSW_CONF_MANAGER_STRING="Name=${OPSW_CONF_MANAGER_NAME},Version=${OPSW_CONF_MANAGER_VERSION}" 
        && echo ${OPSW_CONF_MANAGER_STRING}
変数の設定
OPSW_CUSTOM_COOKBOOK_SOURECE_STRING="Type=${OPSW_CUSTOM_COOKBOOK_SOURCE_TYPE},Url=${OPSW_CUSTOM_COOKBOOK_SOURCE_URL}" 
        && echo ${OPSW_CUSTOM_COOKBOOK_SOURECE_STRING}
変数の確認
cat << ETX

        AWS_DEFAULT_REGION:                  ${AWS_DEFAULT_REGION}
        IAM_ROLE_ARN:                        ${IAM_ROLE_ARN}
        IAM_INSTANCE_PROFILE_ARN:            ${IAM_INSTANCE_PROFILE_ARN}
        VPC_ID:                              ${VPC_ID}
        VPC_SUBNET_ID:                       ${VPC_SUBNET_ID}
        OPSW_STACK_NAME:                     ${OPSW_STACK_NAME}
        OPSW_ATTRIBUTE:                      ${OPSW_ATTRIBUTE}
        OPSW_CONF_MANAGER_STRING             ${OPSW_CONF_MANAGER_STRING}
        OPSW_CUSTOM_COOKBOOK_SOURECE_STRING: ${OPSW_CUSTOM_COOKBOOK_SOURECE_STRING}
        OPSW_DEFAULT_ROOT_DEVICE_TYPE:       ${OPSW_DEFAULT_ROOT_DEVICE_TYPE}
        OPSW_DEFAULT_OS:                     ${OPSW_DEFAULT_OS}

ETX
コマンド
aws opsworks create-stack 
  --name "${OPSW_STACK_NAME}" 
  --attributes "${OPSW_ATTRIBUTE}" 
  --service-role-arn ${IAM_ROLE_ARN} 
  --stack-region ${AWS_DEFAULT_REGION} 
  --default-instance-profile-arn ${IAM_INSTANCE_PROFILE_ARN} 
  --default-os "${OPSW_DEFAULT_OS}" 
  --vpc-id ${VPC_ID} 
  --default-subnet-id ${VPC_SUBNET_ID} 
  --use-custom-cookbooks 
  --use-opsworks-security-groups 
  --configuration-manager ${OPSW_CONF_MANAGER_STRING} 
  --custom-cookbooks-source ${OPSW_CUSTOM_COOKBOOK_SOURECE_STRING} 
  --default-root-device-type ${OPSW_DEFAULT_ROOT_DEVICE_TYPE}

結果(例):

  {
    "StackId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
  }

2.2. スタックIDの取得

コマンド
OPSW_STACK_ID=$( 
        aws opsworks describe-stacks 
          --query "Stacks[?Name ==`${OPSW_STACK_NAME}`].StackId" 
          --output text 
) 
        && echo ${OPSW_STACK_ID}

結果(例):

  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

3. 事後作業

変数の設定
ARRAY_OPSW_STACK_IDS="${OPSW_STACK_ID}" 
        && echo ${ARRAY_OPSW_STACK_IDS}
コマンド
aws opsworks describe-stacks 
        --stack-ids ${ARRAY_OPSW_STACK_IDS}

結果(例):

  {
    "Stacks": [
      {
          "StackId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
          "ServiceRoleArn": "arn:aws:iam::XXXXXXXXXXXX:role/aws-opsworks-service-role",
          "VpcId": "vpc-xxxxxxxx",
          "DefaultRootDeviceType": "ebs",
          "Name": "My Sample Stack (Linux)",
          "HostnameTheme": "Layer_Dependent",
          "UseCustomCookbooks": true,
          "UseOpsworksSecurityGroups": true,
          "Region": "ap-northeast-1",
          "DefaultAvailabilityZone": "ap-northeast-1a",
          "CreatedAt": "2017-06-26T01:23:45+00:00",
          "CustomCookbooksSource": {
              "Url": "https://s3.amazonaws.com/opsworks-demo-assets/opsworks-linux-demo-cookbooks-nodejs.tar.gz",
              "Type": "archive"
          },
          "ConfigurationManager": {
              "Version": "12",
              "Name": "Chef"
          },
          "ChefConfiguration": {},
          "DefaultSubnetId": "subnet-xxxxxxxx",
          "DefaultInstanceProfileArn": "arn:aws:iam::XXXXXXXXXXXX:instance-profile/aws-opsworks-ec2-role",
          "Attributes": {
              "Color": "rgb(45, 114, 184)"
          },
          "DefaultOs": "Amazon Linux 2017.03",
          "Arn": "arn:aws:opsworks:ap-northeast-1:XXXXXXXXXXXX:stack/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/",
          "AgentVersion": "4023-20170402215230"
      }
    ]
  }

完了

続きを読む

[JAWS-UG CLI] IAM #?? インスタンスプロファイルの作成

前提条件

IAMへの権限

IAMに対してフル権限があること。

AWS CLIのバージョン

以下のバージョンで動作確認済

  • AWS CLI 1.11.14
コマンド
aws --version

結果(例):

  aws-cli/1.11.34 Python/2.7.10 Darwin/15.6.0 botocore/1.4.91

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

0. 準備

1.1. 変数の確認

プロファイルが想定のものになっていることを確認します。

コマンド
aws configure list

結果(例)

        Name                    Value             Type    Location
        ----                    -----             ----    --------
     profile         iamFull-prjz-mbp13        env    AWS_DEFAULT_PROFILE
  access_key     ****************XXXX shared-credentials-file
  secret_key     ****************XXXX shared-credentials-file
      region                         ap-northeast-1  env    AWS_DEFAULT_REGION

AssumeRoleを利用している場合はprofileが ”と表示されます。 そ
れ以外のときにprofileが ” と表示される場合は、以下を実行して
ください。

変数の設定:

     export AWS_DEFAULT_PROFILE=<IAMユーザ名>

1.2. IAMロール名の決定

変数の設定
IAM_ROLE_NAME='aws-opsworks-ec2-role'

1. 事前作業

インスタンスプロファイル名の決定

変数の設定
IAM_INSTANCE_PROFILE_NAME='aws-opsworks-ec2-role'

同じ名前のインスタンスプロファイルが存在しないことを確認します。

コマンド
aws iam get-instance-profile 
        --instance-profile-name ${IAM_INSTANCE_PROFILE_NAME}

結果(例):

  A client error (NoSuchEntity) occurred when calling the GetInstanceProfile operation: Instance Profile aws-opsworks-ec2-role cannot be found.

2. 本作業

インスタンスプロファイルの作成

変数の確認
cat << ETX

        IAM_INSTANCE_PROFILE_NAME: ${IAM_INSTANCE_PROFILE_NAME}

ETX
コマンド
aws iam create-instance-profile 
        --instance-profile-name ${IAM_INSTANCE_PROFILE_NAME}

結果(例):

  {
    "InstanceProfile": {
      "InstanceProfileId": "AIPAxxxxxxxxxxxxxxxxx",
      "Roles": [],
      "CreateDate": "2017-02-13T01:23:45.678",
      "InstanceProfileName": "aws-opsworks-ec2-role",
      "Path": "/",
      "Arn": "arn:aws:iam::XXXXXXXXXXXX:instance-profile/aws-opsworks-ec2-role"
    }
  }

3. 事後作業

インスタンスプロファイルの確認

コマンド
aws iam get-instance-profile 
        --instance-profile-name ${IAM_INSTANCE_PROFILE_NAME}

結果(例):

  {
    "InstanceProfiles": [
      {
          "InstanceProfileId": "AIPAJRUJIZXNNQU4R6VZM",
          "Roles": [],
          "CreateDate": "2017-02-13T01:23:45Z",
          "InstanceProfileName": "aws-opsworks-ec2-role",
          "Path": "/",
          "Arn": "arn:aws:iam::XXXXXXXXXXXX:instance-profile/aws-opsworks-ec2-role"
      }
    ]
  }

完了

続きを読む

[JAWS-UG CLI] IAM #?? IAMポリシーの追加 (aws-opsworks-service-role)

AWS CLIを利用して、opsworks利用に必要な権限をIAMロールに追加してみます。

前提条件

IAMへの権限

IAMに対してフル権限があること。

AWS CLIのバージョン

以下のバージョンで動作確認済

  • AWS CLI 1.11.14
コマンド
aws --version

結果(例):

  aws-cli/1.11.70 Python/2.7.12 Linux/4.4.11-23.53.amzn1.x86_64 botocore/1.5.33

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

0. 準備

まず変数の確認をします。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}
        IAM_ROLE_NAME        (0.2) ${IAM_ROLE_NAME}
        IAM_POLICY_ARN       (0.3) ${IAM_POLICY_ARN}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) <IAMのフル権限を許可されたプロファイル>
  IAM_ROLE_NAME        (0.2) aws-opsworks-service-role
  IAM_POLICY_ARN       (0.3) arn:aws:iam::XXXXXXXXXXXX:policy/aws-opsworks-service-role

変数が入っていない、適切でない場合は、それぞれの手順番号について作業を
行います。

0.1. プロファイルの指定

プロファイルの一覧を確認します。

コマンド
cat ~/.aws/credentials 
       | grep '[' 
       | sed 's/[//g' | sed 's/]//g'

結果(例):

  iamFull-prjz-mbpr13
  <IAMのフル権限を許可されたプロファイル>
変数の設定
export AWS_DEFAULT_PROFILE='<IAMのフル権限を許可されたプロファイル>'

0.2. IAMロール名の指定

変数の設定
IAM_ROLE_NAME='aws-opsworks-service-role'

0.3. IAMロールポリシーの決定

今回必要となる権限については、すでにカスタマ管理ポリシーで
‘aws-opsworks-service-policy’という名前で作成済みです。

‘aws-opsworks-service-policy’というキーワードでAWS管理ポリシーから検索
してみましょう。

変数の設定
SEARCH_KEYWORD='aws-opsworks-service-policy'
コマンド
aws iam list-policies 
        --scope Local 
        --max-items 1000 
        --query "Policies[?contains(PolicyName, `${SEARCH_KEYWORD}`)].PolicyName"

結果(例):

  [
    "aws-opsworks-service-policy"
  ]

利用するIAMポリシーを指定します。

変数の設定
IAM_POLICY_NAME='aws-opsworks-service-policy'

ポリシーを特定するためのAWSリソース識別名(ARN)を取得します。

変数の設定
IAM_POLICY_ARN=$( 
        aws iam list-policies 
          --max-items 1000 
          --query "Policies[?PolicyName==`${IAM_POLICY_NAME}`].Arn" 
          --output text 
) 
        && echo "${IAM_POLICY_ARN}"

結果(例):

  arn:aws:iam::XXXXXXXXXXXX:policy/aws-opsworks-service-policy

最終確認

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}
        IAM_ROLE_NAME        (0.2) ${IAM_ROLE_NAME}
        IAM_POLICY_ARN       (0.3) ${IAM_POLICY_ARN}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) <IAMのフル権限を許可されたプロファイル>
  IAM_ROLE_NAME        (0.2) aws-opsworks-service-role
  IAM_POLICY_ARN       (0.3) arn:aws:iam::XXXXXXXXXXXX:policy/aws-opsworks-service-role

1. 事前作業

1.1. IAMポリシーの確認

IAMポリシのバージョンを取得します。

コマンド
IAM_POLICY_VERSION=$( 
        aws iam list-policies 
          --max-items 1000 
          --query "Policies[?PolicyName==`${IAM_POLICY_NAME}`].DefaultVersionId" 
          --output text 
) 
        && echo ${IAM_POLICY_VERSION}

結果(例)

  v1

ポリシの内容を見てみましょう。

コマンド
aws iam get-policy-version 
        --policy-arn ${IAM_POLICY_ARN} 
        --version-id ${IAM_POLICY_VERSION}

結果(例):

1.2. IAMロールの確認

コマンド
aws iam get-role 
         --role-name ${IAM_ROLE_NAME}

結果(例):

  {
      "Role": {
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": "sts:AssumeRole",
                    "Principal": {
                        "Service": "opsworks.amazonaws.com"
                    },
                    "Effect": "Allow",
                    "Sid": ""
                }
            ]
        },
        "RoleId": "AROAXXXXXXXXXXXXXXXXX",
        "CreateDate": "2017-04-17T01:23:45Z",
        "RoleName": "aws-opsworks-service-role",
        "Path": "/",
        "Arn": "arn:aws:iam::XXXXXXXXXXXX:role/aws-opsworks-service-role"
      }
  }

1.3. ロールポリシーの確認

IAMロールのロールポリシーを確認します。

コマンド
aws iam list-attached-role-policies 
        --role-name ${IAM_ROLE_NAME}

結果:

  {
      "AttachedPolicies": [], 
  }

2. ロールポリシーの適用

ロールポリシーの適用

ロールポリシーをIAMロールに適用します。

変数の確認
cat << ETX

        IAM_ROLE_NAME:  ${IAM_ROLE_NAME}
        IAM_POLICY_ARN: ${IAM_POLICY_ARN}

ETX
コマンド
aws iam attach-role-policy 
        --role-name ${IAM_ROLE_NAME} 
        --policy-arn ${IAM_POLICY_ARN}

結果:

  (戻り値なし)

3. 事後作業

ロールポリシの確認

コマンド
aws iam list-attached-role-policies 
        --role-name ${IAM_ROLE_NAME}

結果(例):

  {
    "AttachedPolicies": [
      {
        "PolicyName": "aws-opsworks-service-policy",
        "PolicyArn": "arn:aws:iam::XXXXXXXXXXXX:policy/aws-opsworks-service-policy"
      }
    ]
  }

完了

続きを読む

[JAWS-UG CLI] IAM #65 IAMロールの作成 (aws-opsworks-service-role)

前提条件

IAMへの権限

IAMに対してフル権限があること。

AWS CLIのバージョン

以下のバージョンで動作確認済

  • AWS CLI 1.11.14
コマンド
aws --version

結果(例):

  aws-cli/1.11.70 Python/2.7.12 Linux/4.4.11-23.53.amzn1.x86_64 botocore/1.5.33

バージョンが古い場合は最新版に更新しましょう。

コマンド
sudo -H pip install -U awscli

0. 準備

まず変数の確認をします。

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) <IAMのフル権限を許可されたプロファイル>

変数が入っていない、適切でない場合は、それぞれの手順番号について作業を
行います。

0.1. プロファイルの指定

プロファイルの一覧を確認します。

コマンド
cat ~/.aws/credentials 
       | grep '[' 
       | sed 's/[//g' | sed 's/]//g'

結果(例):

  iamFull-prjz-mbpr13
  <IAMのフル権限を許可されたプロファイル>
変数の設定
export AWS_DEFAULT_PROFILE='<IAMのフル権限を許可されたプロファイル>'

最終確認

変数の確認
cat << ETX

        AWS_DEFAULT_PROFILE: (0.1) ${AWS_DEFAULT_PROFILE}

ETX

結果(例):

  AWS_DEFAULT_PROFILE: (0.1) <IAMのフル権限を許可されたプロファイル>

1. 事前作業

1.1. IAMロール名の指定

変数の設定
IAM_ROLE_NAME='aws-opsworks-service-role'

同じ名前のIAMロールが存在しないことを確認します。

コマンド
aws iam get-role 
         --role-name ${IAM_ROLE_NAME}

結果(例):

  An error occurred (NoSuchEntity) when calling the GetRole operation: Role not found for aws-opsworks-service-role

1.2. プリンシパルの指定

変数の設定
IAM_PRINCIPAL='opsworks.amazonaws.com'

1.3. assumeロールポリシドキュメントの作成

変数の設定
FILE_INPUT="${IAM_ROLE_NAME}.json" 
        && echo ${FILE_INPUT}
変数の確認
cat << ETX

        FILE_INPUT:    ${FILE_INPUT}
        IAM_PRINCIPAL: ${IAM_PRINCIPAL}

ETX
コマンド
cat << EOF > ${FILE_INPUT}
{
        "Version": "2012-10-17",
        "Statement": [
          {
            "Action": "sts:AssumeRole",
            "Principal": {
              "Service": "${IAM_PRINCIPAL}"
            },
            "Effect": "Allow",
            "Sid": ""
          }
        ]
}
EOF

cat ${FILE_INPUT}

JSONファイルを作成したら、フォーマットが壊れてないか必ず確認します。

コマンド
jsonlint -q ${FILE_INPUT}

エラーが出力されなければOKです。

2. 本作業

IAMロールの作成

変数の確認
cat << ETX

        IAM_ROLE_NAME: ${IAM_ROLE_NAME}
        FILE_INPUT:    ${FILE_INPUT}

ETX
コマンド
aws iam create-role 
        --role-name ${IAM_ROLE_NAME} 
        --assume-role-policy-document file://${FILE_INPUT}

結果(例):

  {
     "Role": {
       "AssumeRolePolicyDocument": {
           "Version": "2012-10-17",
           "Statement": [
               {
                   "Action": "sts:AssumeRole",
                   "Sid": "",
                   "Effect": "Allow",
                   "Principal": {
                       "Service": "opsworks.amazonaws.com"
                   }
               }
           ]
       },
       "RoleId": "AROAXXXXXXXXXXXXXXXXX",
       "CreateDate": "2017-04-17T01:23:45.678Z",
       "RoleName": "aws-opsworks-service-role",
       "Path": "/",
       "Arn": "arn:aws:iam::XXXXXXXXXXXX:role/aws-opsworks-service-role"
     }
   }

3. 事後作業

IAMロールの確認

コマンド
aws iam get-role 
         --role-name ${IAM_ROLE_NAME}

結果(例):

  {
      "Role": {
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": "sts:AssumeRole",
                    "Principal": {
                        "Service": "opsworks.amazonaws.com"
                    },
                    "Effect": "Allow",
                    "Sid": ""
                }
            ]
        },
        "RoleId": "AROAXXXXXXXXXXXXXXXXX",
        "CreateDate": "2017-04-17T01:23:45Z",
        "RoleName": "aws-opsworks-service-role",
        "Path": "/",
        "Arn": "arn:aws:iam::XXXXXXXXXXXX:role/aws-opsworks-service-role"
      }
  }

完了

続きを読む

[JAWS-UG CLI] IAM:#?? IAMロールの作成 (aws-opsworks-ec2-role)

前提条件

IAMへの権限

IAMに対してフル権限があること。

AWS CLIのバージョン

コマンド
aws --version
結果(例)
      aws-cli/1.9.0 Python/2.7.5 Darwin/13.4.0 botocore/1.3.0

0. 準備

変数の確認

プロファイルが想定のものになっていることを確認します。

変数の確認
aws configure list
結果(例)

            Name                    Value             Type    Location
            ----                    -----             ----    --------
         profile       iamFull-prjZ-mbp13iamFull-prjZ-mbp13              env    AWS_DEFAULT_PROFILE
      access_key     ****************XXXX shared-credentials-file
      secret_key     ****************XXXX shared-credentials-file
          region                eu-west-1              env    AWS_DEFAULT_REGION

1. 事前作業

1.1. IAMロール名の決定

変数の設定
IAM_ROLE_NAME='aws-opsworks-ec2-role'

同じ名前のIAMロールが存在しないことを確認します。

コマンド
aws iam get-role 
         --role-name ${IAM_ROLE_NAME}
結果(例)
      A client error (NoSuchEntity) occurred when calling the GetRole operation: The role with name aws-opsworks-ec2-role cannot be found.

2. ロールの作成

2.1. assumeロールの決定

変数の設定
FILE_ROLE_DOC="${IAM_ROLE_NAME}.json" 
        && echo ${FILE_ROLE_DOC}
コマンド
cat << EOF > ${FILE_ROLE_DOC}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF

cat ${FILE_ROLE_DOC}
コマンド
jsonlint -q ${FILE_ROLE_DOC}

2.2. ロールの作成

変数の確認
cat << ETX

        IAM_ROLE_NAME: ${IAM_ROLE_NAME}
        FILE_ROLE_DOC: ${FILE_ROLE_DOC}

ETX
コマンド
aws iam create-role 
        --role-name ${IAM_ROLE_NAME} 
        --assume-role-policy-document file://${FILE_ROLE_DOC}
結果(例)
      {
        "Role": {
          "AssumeRolePolicyDocument": {
              "Version": "2012-10-17",
              "Statement": [
                  {
                      "Action": "sts:AssumeRole",
                      "Sid": "",
                      "Effect": "Allow",
                      "Principal": {
                          "Service": "ec2.amazonaws.com"
                      }
                  }
              ]
          },
          "RoleId": "AROAJOMDKX4MPARLRM5CO",
          "CreateDate": "2015-10-23T02:05:55.750Z",
          "RoleName": "aws-opsworks-ec2-role",
          "Path": "/",
          "Arn": "arn:aws:iam::XXXXXXXXXXXX:role/aws-opsworks-ec2-role"
        }
      }
コマンド
aws iam get-role 
         --role-name ${IAM_ROLE_NAME}
結果(例)
      {
        "Role": {
          "AssumeRolePolicyDocument": {
              "Version": "2012-10-17",
              "Statement": [
                  {
                      "Action": "sts:AssumeRole",
                      "Principal": {
                          "Service": "ec2.amazonaws.com"
                      },
                      "Effect": "Allow",
                      "Sid": ""
                  }
              ]
          },
          "RoleId": "AROAJOMDKX4MPARLRM5CO",
          "CreateDate": "2015-10-23T02:05:55Z",
          "RoleName": "aws-opsworks-ec2-role",
          "Path": "/",
          "Arn": "arn:aws:iam::XXXXXXXXXXXX:role/aws-opsworks-ec2-role"
        }
      }

完了

続きを読む

LambdaでAWSの料金を毎日Slackに通知する(Python3)

はじめに

個人アカウントは基本的に無料枠で運用しているので、少しでも請求がある場合はいち早く気づきたいです。
先日、とあるハンズオンイベントで使ったリソースを消し忘れて、最終的に$30ぐらい請求が来てしまいました。。。

CloudWatchで請求アラートは設定していますが、閾値超えが想定の場合、当然見逃すことになり、最終的な請求額に驚くハメになります。

これを防ぐためにLambdaで毎日SlackにAWS料金を通知することにします。

先日LambdaがPython3に対応したので、せっかくだし勉強がてらPython3で実装したい。
ネット上にはNode.jsでの実装例が多いようで、今回はこちらを参考にPython3で実装してみます。

必要なもの

  • Slack

    • incoming-webhooks URL

    • 適当なchannel
  • lambda-uploader
    • requestsモジュールをLambda上でimportするために利用

      • カレントディレクトリにモジュールをインストールして、モジュールごとZipに固めてアップロードでもいけるはずですが、私の環境だとうまくいかなかったので
  • aws cli
    • lambda-uploaderで必要
  • AWS
    • Lambda関数用IAM Role

      • CloudWatchReadOnlyAccessポリシーをアタッチ

事前準備

lambda-uploaderをインストール

$ pip install lambda-uploader 

こちらを参考にさせていただきました。

aws cliをインストール

$ pip install awscli

credential、リージョン設定

$ aws configure

確認
$ aws configure list

コード

ディレクトリ構成

ディレクトリ名は任意です。関数名とは無関係です。

ディレクトリ構成
awscost_to_slack/
|--lambda_function.py
|--requirements.txt
|--lambda.json

lambda_function.py

超過金額に応じて色をつけるようにしています。
\$0.0なら緑、超過したら黄色、\$10超えで赤になります。

lambda_function.py
#!/usr/bin/env python
# encoding: utf-8

import json
import datetime
import requests
import boto3
import os
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

# Slack の設定
SLACK_POST_URL = os.environ['slackPostURL']
SLACK_CHANNEL = os.environ['slackChannel']

response = boto3.client('cloudwatch', region_name='us-east-1')

get_metric_statistics = response.get_metric_statistics(
    Namespace='AWS/Billing',
    MetricName='EstimatedCharges',
    Dimensions=[
        {
            'Name': 'Currency',
            'Value': 'USD'
        }
    ],
    StartTime=datetime.datetime.today() - datetime.timedelta(days=1),
    EndTime=datetime.datetime.today(),
    Period=86400,
    Statistics=['Maximum'])

cost = get_metric_statistics['Datapoints'][0]['Maximum']
date = get_metric_statistics['Datapoints'][0]['Timestamp'].strftime('%Y年%m月%d日')

def build_message(cost):
    if float(cost) >= 10.0:
        color = "#ff0000" #red
    elif float(cost) > 0.0:
        color = "warning" #yellow
    else:
        color = "good"    #green

    text = "%sまでのAWSの料金は、$%sです。" % (date, cost)

    atachements = {"text":text,"color":color}
    return atachements

def lambda_handler(event, context):
    content = build_message(cost)

    # SlackにPOSTする内容をセット
    slack_message = {
        'channel': SLACK_CHANNEL,
        "attachments": [content],
    }

    # SlackにPOST
    try:
        req = requests.post(SLACK_POST_URL, data=json.dumps(slack_message))
        logger.info("Message posted to %s", slack_message['channel'])
    except requests.exceptions.RequestException as e:
        logger.error("Request failed: %s", e)

requirements.txt

pip installしたいモジュール名を書きます。

requirements.txt
requests

lambda.json

Lambda関数名、IAM RoleのARNなどは環境に合わせてください。
スクリプト本体のファイル名とハンドラの前半を一致させないと動きません。地味にハマるので注意!

lambda.json
{
  "name": "LAMBDA_FUNCTION_NAME",
  "description": "DESCRIPTION",
  "region": "ap-northeast-1",
  "handler": "lambda_function.lambda_handler",
  "role": "arn:aws:iam::XXXXXXX:role/ROLE_NAME_FOR_LUMBDA",
  "timeout": 300,
  "memory": 128,
  "variables":
    {
      "slackPostURL":"https://hooks.slack.com/services/XXX",
      "slackChannel":"#XXX"
    }
}

デプロイ

上記ファイルを配置したディレクトリに移動して、lambda-uploaderを実行します。

$ cd awscost_to_slack
$ lambda-uploader
λ Building Package
λ Uploading Package
λ Fin

Lambda環境変数設定

今回のLambda関数では、通知先SlackチャネルとWebhooks URLを環境変数で渡すようにしたので、設定します。

スクリーンショット 2017-06-23 14.51.30.png

lambda-uploaderのlambda.jsonに書けそうなのですが、書式が分からず、今回はマネコンで設定しました。
lambda-uploaderでLambda関数を更新すると消えてしまうので注意。

上記のようにlambda.jsonのvariablesで定義すれば環境変数も設定可能です。uploadのたびに消えることもありません。

Lambda定期実行設定

CloudWatchのスケジュールイベントを定義して、lambda関数をターゲットに指定します。
時刻はUTCなので注意しましょう。
毎日UTC0:00に実行されるよう設定しました。

スクリーンショット 2017-06-23 15.01.27.png

実行イメージ

スクリーンショット 2017-06-23 14.15.54.png
こんな感じで毎朝9:00に通知がきます。
今日も無料!

まとめ

Lambdaはほぼ無料でプログラムが動かせるので楽しいですね!
Python初心者なのでコードが見苦しい点はご容赦ください。

続きを読む

Fresh Install bitnami redmine stack on AWS, then migrate old data and database from old instance.

Fresh Install bitnami redmine stack on AWS, then migrate old data and database from old instance.

目的: Redmine のバージョンアップ と HTTPS化

ですが、新環境の構築手順のみやっていただければ新規構築手順としても使えます。

  • 前回書いた記事から1年ちょっと経過しました。
  • bitnami redmine stack AMI のバージョンも以下のように変化しました。(2017/06/21 現在)
    • Old version 3.2.1
    • New version 3.3.3
  • 前回は GUI でぽちぽち作成しましたが、今回は AWS CLI を中心に進めます。
  • ついでに前回書いてなかった HTTPS化 についても簡単に追記します。
  • 以下の AWS の機能を利用します。
    • Route53: ドメイン名取得、名前解決(DNS)
    • ACM(Amazon Certificate Manager): 証明書発行
    • ELB(Elastic Load Balancer): 本来は複数インスタンスをバランシングする用途に使うものですが今回は EC2 インスタンス 1台 をぶら下げ、ACM で取得した証明書を配布し外部との HTTPS通信 のために利用します。

注意と免責

  • 無料枠でない部分は料金が発生します。
  • データの正常な移行を保証するものではありません。

前提

  • 現環境が正常に動作していること。
  • 新環境を同一リージョン、同一VPC、同一サブネット内に新たにたてます。
    • インスタンス間のデータ転送を SCP で簡易に行いたいと思います。
    • 適宜セキュリティグループを解放してください。
  • aws cli version
% aws --version
aws-cli/1.11.47 Python/2.7.12 Darwin/16.6.0 botocore/1.5.10

段取り

  • 大まかに以下の順序で進めます
  1. Version 3.3.3 の AMI を使って EC2 インスタンスを起動
  2. Version 3.2.1 のデータバックアップ
    • Bitnami Redmine Stack の停止
    • MySQL Dump 取得
  3. Version 3.3.3 へのデータ復元
    • Bitnami Redmine Stack の停止
    • MySQL Dump 復元
    • Bitnami Redmine Stack の開始
  4. 動作確認

参考資料

作業手順

1. Newer Bitnami redmine stack インスタンス作成

以下の条件で作成します。

  • Common conditions

    • AMI: ami-15f98503
    • Type: t2.micro
    • Public IP: あり
  • User defined conditions
    • Region: N.Virginia
    • Subnet: subnet-bd809696
    • Security Group: sg-5b5b8f2a
    • Keypair: aws-n.virginia-default001
    • IAM Role: ec2-001
    • EBS: 20GB

WEB GUI から作るも良し、AWS CLI から作るも良し
以下コマンド実行例

set-env
KEY_NAME=aws-nvirginia-default001.pem
echo $KEY_NAME
check-ami
aws ec2 describe-images \
    --filters "Name=image-id,Values=ami-15f98503"
check-ami-name
aws ec2 describe-images \
    --filters "Name=image-id,Values=ami-15f98503" \
    | jq ".Images[].Name" \
    | grep --color redmine-3.3.3
create-instance
aws ec2 run-instances \
    --image-id ami-15f98503 \
    --count 1 \
    --instance-type t2.micro \
    --key-name aws-n.virginia-default001 \
    --security-group-ids sg-5b5b8f2a \
    --subnet-id subnet-bd809696 \
    --block-device-mappings "[{\"DeviceName\":\"/dev/sda1\",\"Ebs\":{\"VolumeSize\":20,\"DeleteOnTermination\":false}}]" \
    --iam-instance-profile Name=ec2-001 \
    --associate-public-ip-address
set-env
INSTANCE_ID=i-0f8d079eef9e5aeba
echo $INSTANCE_ID
add-name-tag-to-instance
aws ec2 create-tags --resources $INSTANCE_ID \
    --tags Key=Name,Value=redmine-3.3.3

注意書きにもありますが以下に表示される MySQL Database の root パスワードは初期起動時にしか表示されません。
このときに保管しておくか MySQL のお作法にしたがって変更しておいても良いでしょう

check-instance-created
aws ec2 describe-instances --filter "Name=instance-id,Values=$INSTANCE_ID"
wait-running-state
aws ec2 describe-instances \
    --filter "Name=instance-id,Values=$INSTANCE_ID" \
    | jq '.Reservations[].Instances[].State["Name"]'
get-redmine-password
aws ec2 get-console-output \
    --instance-id $INSTANCE_ID \
    | grep "Setting Bitnami application password to"
get-publicip
aws ec2 describe-instances \
    --filter "Name=instance-id,Values=$INSTANCE_ID" \
    | jq '.Reservations[].Instances[].NetworkInterfaces[].Association'
set-env
PUBLIC_IP=54.243.10.66
echo $PUBLIC_IP
site-check
curl -I http://$PUBLIC_IP/
HTTP/1.1 200 OK
(snip)
ssh-connect
ssh -i .ssh/$KEY_NAME bitnami@$PUBLIC_IP
first-login
bitnami@new-version-host:~$ sudo apt-get update -y
bitnami@new-version-host:~$ sudo apt-get upgrade -y
bitnami@new-version-host:~$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.5 LTS"
bitnami@new-version-host:~$ sudo ./stack/ctlscript.sh status
subversion already running
php-fpm already running
apache already running
mysql already running
bitnami@new-version-host:~$ sudo ./stack/ctlscript.sh help
usage: ./stack/ctlscript.sh help
       ./stack/ctlscript.sh (start|stop|restart|status)
       ./stack/ctlscript.sh (start|stop|restart|status) mysql
       ./stack/ctlscript.sh (start|stop|restart|status) php-fpm
       ./stack/ctlscript.sh (start|stop|restart|status) apache
       ./stack/ctlscript.sh (start|stop|restart|status) subversion

help       - this screen
start      - start the service(s)
stop       - stop  the service(s)
restart    - restart or start the service(s)
status     - show the status of the service(s)

2. 旧バージョンのバックアップ取得

login_to_oldversion
Welcome to Ubuntu 14.04.3 LTS (GNU/Linux 3.13.0-74-generic x86_64)
       ___ _ _                   _
      | _ |_) |_ _ _  __ _ _ __ (_)
      | _ \ |  _| ' \/ _` | '  \| |
      |___/_|\__|_|_|\__,_|_|_|_|_|

  *** Welcome to the Bitnami Redmine 3.2.0-1         ***
  *** Bitnami Wiki:   https://wiki.bitnami.com/      ***
  *** Bitnami Forums: https://community.bitnami.com/ ***
Last login: Sun May 29 07:33:45 2016 from xxx.xxx.xxx.xxx
bitnami@old-version-host:~$
check-status
bitnami@old-version-host:~$ sudo stack/ctlscript.sh status
subversion already running
php-fpm already running
apache already running
mysql already running
stop
bitnami@old-version-host:~$ sudo stack/ctlscript.sh stop
/opt/bitnami/subversion/scripts/ctl.sh : subversion stopped
Syntax OK
/opt/bitnami/apache2/scripts/ctl.sh : httpd stopped
/opt/bitnami/php/scripts/ctl.sh : php-fpm stopped
/opt/bitnami/mysql/scripts/ctl.sh : mysql stopped
start-mysql
bitnami@old-version-host:~$ sudo stack/ctlscript.sh start mysql
170621 10:04:34 mysqld_safe Logging to '/opt/bitnami/mysql/data/mysqld.log'.
170621 10:04:34 mysqld_safe Starting mysqld.bin daemon with databases from /opt/bitnami/mysql/data
/opt/bitnami/mysql/scripts/ctl.sh : mysql  started at port 3306
check-available-filesystem-space
bitnami@old-version-host:~$ df -h /
Filesystem                                              Size  Used Avail Use% Mounted on
/dev/disk/by-uuid/6cdd25df-8610-4f60-9fed-ec03ed643ceb  9.8G  2.7G  6.6G  29% /
load-env-setting
bitnami@old-version-host:~$ . stack/use_redmine
bitnami@old-version-host:~$ echo $BITNAMI_ROOT
/opt/bitnami
dump-mysql
bitnami@old-version-host:~$ mysqldump -u root -p bitnami_redmine > redmine_backup.sql
Enter password:
bitnami@old-version-host:~$ ls -ltrh
  • scp 準備

手元の作業PCから新Redmine環境へssh接続するときに使用した証明書(pem)ファイルを旧Redmine環境にも作成します。

  • 今回は作業PC上で cat で表示させておいて旧環境のコンソール上にコピペしました。
example
bitnami@old-version-host:~$ vi .ssh/aws-nvirginia-default001.pem
bitnami@old-version-host:~$ chmod 600 .ssh/aws-nvirginia-default001.pem
  • ファイル転送
file_transfer
bitnami@old-version-host:~$ scp -i .ssh/aws-nvirginia-default001.pem redmine_backup.sql <new-version-host-ipaddr>:~
  • 新バージョン側にファイルが届いているか確認
check-transfered-files
bitnami@new-version-host:~$ ls -alh redmine*

3. 新バージョンへの復元

stop-stack
bitnami@new-version-host:~$ sudo stack/ctlscript.sh status
subversion already running
php-fpm already running
apache already running
mysql already running

bitnami@new-version-host:~$ sudo stack/ctlscript.sh stop

/opt/bitnami/subversion/scripts/ctl.sh : subversion stopped
Syntax OK
/opt/bitnami/apache2/scripts/ctl.sh : httpd stopped
/opt/bitnami/php/scripts/ctl.sh : php-fpm stopped
/opt/bitnami/mysql/scripts/ctl.sh : mysql stopped
start-mysql
bitnami@new-version-host:~$ sudo stack/ctlscript.sh start mysql
initial-database
bitnami@new-version-host:~$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.35 MySQL Community Server (GPL)

(snip)

mysql> drop database bitnami_redmine;

mysql> create database bitnami_redmine;

mysql> grant all privileges on bitnami_redmine.* to 'bn_redmine'@'localhost' identified by 'DATAB
ASE_PASSWORD';

mysql> quit
restore-dumpfile
bitnami@new-version-host:~$ mysql -u root -p bitnami_redmine < redmine_backup.sql
Enter password:
bitnami@new-version-host:~$
edit-line-18
bitnami@new-version-host:~$ vi /opt/bitnami/apps/redmine/htdocs/config/database.yml

    18    password: "DATABASE_PASSWORD"
db-migrate
bitnami@new-version-host:~$ cd /opt/bitnami/apps/redmine/htdocs/
bitnami@new-version-host:/opt/bitnami/apps/redmine/htdocs$ ruby bin/rake db:migrate RAILS_ENV=production
bitnami@new-version-host:/opt/bitnami/apps/redmine/htdocs$ ruby bin/rake tmp:cache:clear
bitnami@new-version-host:/opt/bitnami/apps/redmine/htdocs$ ruby bin/rake tmp:sessions:clear
stack-restart
bitnami@new-version-host:/opt/bitnami/apps/redmine/htdocs$ cd
bitnami@new-version-host:~$ sudo stack/ctlscript.sh restart

bitnami@new-version-host:~$ exit
site-check
curl -I http://$PUBLIC_IP/
HTTP/1.1 200 OK
(snip)

ブラウザでも http://$PUBLIC_IP/ でアクセスして旧環境のユーザー名とパスワードでログイン出来ることを確認してください。

この時点で旧環境のインスタンスを停止させておいて良いでしょう。
いらないと判断した時に削除するなりしてください。

4. おまけ HTTPS化

  • 4-1. Route53 でドメイン名を取得してください

    • .net で 年額 11 USドル程度ですので実験用にひとつくらい維持しておくと便利
  • 4-2. Certificate Manager で証明書を取得します
    • コマンドでリクエストしてます
aws acm request-certificate --domain-name redmine.hogefuga.net
check-status-pending
aws acm describe-certificate \
    --certificate-arn "arn:aws:acm:us-east-1:942162428772:certificate/fdf099f9-ced7-4b97-a5dd-f85374d7d112" \
    | jq ".Certificate.Status"
"PENDING_VALIDATION"
  • 4-3. 承認する

    • ドメインに設定しているアドレスにメールが飛んできます
  • 4-4. ステータス確認
check-status-ISSUED
aws acm describe-certificate \
    --certificate-arn "arn:aws:acm:us-east-1:942162428772:certificate/fdf099f9-ced7-4b97-a5dd-f85374d7d112" \
    | jq ".Certificate.Status"
"ISSUED"
  • 4-5. Classic タイプの HTTPS ELB をつくる
example
aws elb create-load-balancer \
    --load-balancer-name redmine-elb1 \
    --listeners "Protocol=HTTPS,LoadBalancerPort=443,InstanceProtocol=HTTP,InstancePort=80,SSLCertificateId=arn:aws:acm:us-east-1:942162428772:certificate/fdf099f9-ced7-4b97-a5dd-f85374d7d112" \
    --availability-zones us-east-1a \
    --security-groups sg-3c90f343
  • 4-6. インスタンスをくっつける
example
aws elb register-instances-with-load-balancer \
    --load-balancer-name redmine-elb1 \
    --instances i-0f8d079eef9e5aeba
  • 4-7. State が InService になるまで待ちます
example
aws elb describe-instance-health \
    --load-balancer-name redmine-elb1
  • 4-8. DNS Name を確認する(4-10. で使います)
example
aws elb describe-load-balancers \
    --load-balancer-name redmine-elb1 \
  | jq ".LoadBalancerDescriptions[].DNSName"
  • 4-9. 今の設定を確認
example
aws route53 list-resource-record-sets \
    --hosted-zone-id Z3UG9LUEGNT0PE | jq .
  • 4-10. 投入用の JSON をつくる
example
vi change-resource-record-sets.json
example
{
  "Comment": "add CNAME for redmine.hogefuga.net",
  "Changes": [
    {
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "redmine.hogefuga.net",
        "Type":"CNAME",
        "TTL": 300,
        "ResourceRecords": [
          {
            "Value": <DNSName>
          }
        ]
      }
    }
  ]
}

4-11. 設定投入

example
aws route53 change-resource-record-sets \
    --hosted-zone-id Z3UG9LUEGNT0PE \
    --change-batch file://change-resource-record-sets.json

4-12. 設定確認

example
aws route53 list-resource-record-sets \
    --hosted-zone-id Z3UG9LUEGNT0PE

4-13. ブラウザ確認

https://redmine.hogefuga.net

4-14. EC2 インスタンスのセキュリティグループ再設定

  • グローバルの TCP:80 削除
  • サブネット内の TCP:80 許可
check
aws ec2 describe-security-groups --group-ids sg-b7d983c8
change
aws ec2 modify-instance-attribute \
    --instance-id i-0f8d079eef9e5aeba \
    --groups sg-b7d983c8

4-15. 再度ブラウザからアクセス可能か確認

続きを読む