カテゴリー: AWS Page 1 of 3

Re:Invent2019で発表されていたAWS CLI V2 について触ってみました

クラウド事業部の原口です。
今年は初めて AWS Re:Invent に参加する事ができました。
いろいろ書きたい事はあるのですがクリエイターズブログなのか?という事もあり、技術的なネタで、本日はAWS CLI v2 を触ってみたという内容となります。

ちなみにコネクションハブの OVERFLOW 枠で参加してました

AWS CLI v2の新機能をレビュー

インストール

インストールにpythonが必要がなくなり非常に簡単になりました!
MacOSやWindowsではインストーラーポチでよいようです。
ひとまずはAmazon Linuxにインストールしてみました

cd /usr/local/src
curl "https://d1vvhvl2y92vvt.cloudfront.net/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

現在はプレビューという事もあり aws2 というコマンドが追加されていてv1のawsコマンドと共存が可能なようです

設定が対話式で行えるようになりました

aws2 configure wizard というコマンドが追加され対話式で設定が可能となっていました

こういった対話式で設定を行うものは多数増やしていく、との事。対話式で行えるのはawsコマンドの設定だけではなく、各種コマンドについても増やしていく、との事。
例えばDynamoDBでテーブルを作る時は一生懸命ドキュメントを見ながら設定していましたが…

このように `aws2 dynamodb wizard new-table` とする事で対話式で新規テーブル作成が可能でした。便利

コマンド補完がすごくなっている

complete -C aws2_completer aws2

こちらでタブ補完を有効化したのちに以下のようにタブで補完される事が確認できました。こちらがアップデートされて、各種コマンドの引数はもちろんの事、なんとそのリソースの補完も行えるようになりました。
先程つくったdynamoDBのtestというテーブルを削除したい場合の動きです。

[root@ip-10-0-0-134 ~]# aws2 dynamodb delete-
delete-backup  delete-item    delete-table   

[root@ip-10-0-0-134 ~]# aws2 dynamodb delete-table --ta<TAB>
↓
[root@ip-10-0-0-134 ~]# aws2 dynamodb delete-table --table-name <TAB>
↓
[root@ip-10-0-0-134 ~]# aws2 dynamodb delete-table --table-name test 

わざわざ実際のリソースをlistで一覧してから確認したりする必要がないです!とてもすごいと思いました

AWS SSOとの連携

AWS CLI v2だけの機能としてAWS SSOとの連携が可能なようで試してみました。
AWS SSOについては「AWS SSOを使って複数AWSアカウントのログインを簡単にする!」で試してみましたのでこの環境にログインしてみたいと思います。

まずはaws2 configure sso コマンドでssoの設定を行います。
といっても SSOのstartページとSSOを設定しているリージョンを設定するだけです。

すると
https://device.sso.us-east-1.amazonaws.com/
へのアクセスをアクセスして、出力されているcodeを入力せよ、となります。

ここでブラウザで上記URLに接続するとcodeを記述する画面が出てきます。

CLI側で出力されているコードを入力し、ログインすると、「CLIでログインするか?」というポップアップが出てくるのでSignします。

するとCLI側はsingした瞬間にずらずらと動きだし、ログインできるAWSアカウントが一覧されます。こちらもインタラクティブモードで選択可能です。

ここでアカウントを選択すると、そのaws2コマンドはもうそのAWSアカウントの世界になっています。
一旦ブラウザを挟んでログインするとCLI側が動く、というのは新鮮でしたが、SSOログインと連携する事で余計なAWSシークレットキー/アクセスキーを出力する機会を減らせるかもしれないですね。

その他の追加項目

以下はさらっと試したレベルですが以下の内容が新規に追加されているようです

・出力にyamlが追加された
・Paging機能の追加。

正式発表が楽しみですね!

AWS SSOを使って複数AWSアカウントのログインを簡単にする!

クラウド事業部の原口です。

皆様は複数あるAWSアカウントの管理、どのように管理されているでしょうか?
一般的にある1つのサービスに対して以下のように環境に応じたアカウントを用意する事が多いのではないでしょうか。

「本番環境用アカウント」
「ステージング環境用アカウント」
「テスト環境用アカウント」

このように複数の環境を作ってくると問題となってくるのがログインにかかるコストになります。
MFAなどの設定をしていったり…といった事をすべての環境ぶん行う必要があったり、新しい人がジョインした、プロジェクトを抜けた、というたびにすべての環境のIAM設定が必要。またそれぞれの権限の範囲など…となってくると本当にバカにできないコストとなってきます。

これまで複数AWSアカウントの管理といえばIAMのスイッチロールを使うという方法がとられてきましたが、
今回、AWS SSOがMicrosoft AD以外も選択できるようになった、、具体的にはAWS SSO の ID ストアでユーザー ID を作成および管理ができるようになった、という事を知り、スイッチロールの代替になるのでは!?という事で検証してみました。
もう一度繰り返しますと ADが不要でAWS SSOの機能だけで利用可能となってました。
しかも無料なんですよね。すごい。

AWS SSOを利用する

0.AWS SSOに必要な環境

・AWS SSOを設定するアカウントがAWS Organizationを利用しており、その親アカウントである事
・バージニアリージョンでのみ利用可能(ですが特に問題はないかと)

①AWS SSOを有効化

まずはリージョンをバージニアに変更して AWS Single Sign-Onのページへ移動し、「AWS SSOを有効にする」をクリックします。
これだけで AWS SSO の ID ストアでユーザー ID を作成および管理ができる状態でセットアップされます

② ID ストアでユーザー ID を作成

IDソースを選択、もしくは左メニューのユーザをクリックして新規にユーザを登録します。
ユーザはパスワード認証の他にもMFA認証も対応していますので安心ですね。
ユーザ作成すると作成時に設定したメールアドレスに招待メールが送られますのでAcceptしてください。

③AWS アカウントでOrganizationに属するアカウントに対してユーザとログイン権限と認証の設定を行う

AWS アカウントを選択すると AWS Organizationで属している組織のアカウントがすべて表示されます。これらの中でログインさせたいアカウントをチェックし「ユーザの割り当て」を行います。ここで先程のIDプールで作成したユーザが一覧として出てきます。

ユーザーの設定を行ったあとは、該当組織の子アカウントに対するアクセス権限セットを設定します。IAMポリシーとほぼ同じようなイメージとなります。
これらはアカウント毎(グループ毎)に設定できる為、きめ細かな権限設定が可能です。
ちなみにここでポリシーを設定すると子アカウント側にはそのIAMロールが作られる形でした。
つまり内部的な技術仕様としてはスイッチロールを使用しているのとほぼ変わらない動きとなっているようです。
ちなみにこのIAMロールは子アカウントのrootアカウントでも削除はできませんでした。 また、cloudtrailではIDプールで設定したユーザ名で記録されていました。(完璧!)

④作成したユーザでログインする

SSOを作成した際に同時に作成されるユーザポータルよりログインします。
https://xxxxxxx.awsapps.com/start のものですね。
xxxxxxxの部分はカスタマイズ可能ですが変更は1度しかできないようですのできちんと意味のある名前にしたほうがよいです。
上記のURLにログインするとログイン画面が出てきます

ログインするとこんな形で設定したアカウントが一覧で表示されます。アタッチしている権限で Management console をクリックするとその権限でアカウントにログインできました!

感想

AWS SSO自身がIDプールを持つ事ができるようになったおかげで、Microsoft ADを使用せずとも気軽に複数アカウントへのログインが可能となりました。
内容としてはスイッチロールと大きく変わらないようなので、「スイッチロールの設定が簡単にできるようになった」と捉えて、スイッチロールの代替として使ってみてもいいのではないかと思います。
以下はAWS公式ブログより転載している画像ですがこれをきっかけに複数の他アプリへのログインやカスタムSAMLのアプリケーションへのSSOログインが可能です

まずは簡単にAWSアカウントの管理から初めてみて、今後組織のユーザのslackなどのアプリケーションもすべてこちらでログイン管理してしまうと大変便利になっていくのではないかという可能性を感じました。
AWS CLI v2 ではこのAWS SSOに対応しているという事でそのあたりもとても便利になりそうに思います。

現時点での不満点としては以下となります
・AWS Organizationが必須なので請求を一緒にできないアカウントでは厳しい
・スイッチしたロール名を変えれないので地味にどの環境にログインしているかわからなくなる

前半は仕方がないように思いますが、ログイン後のアカウントがどのAWSアカウントであるかはすぐにわかるようになると嬉しいな、、、と感じました。

IAMユーザを使ってEC2のLinuxユーザを管理する

クラウドソリューション事業部 インフラエンジニアの上野です。

皆さんはEC2(Linux)のユーザ管理をどのようにされていますか?
個別の管理シートのようなものを用意して管理をされていたりするのでしょうか?あるいはディレクトリサービスと連携させて管理しているのでしょうか?

人の出入りが激しいプロジェクトや、そもそもの管理するインスタンス数が多い場合に 個別にユーザを作成・削除したり、現在のユーザの状況を管理するのは非常に運用コストがかかります。

このようなユーザ作成・削除の運用コストやユーザの管理の問題を解決するのにおすすめのaws-ec2-sshというツールを紹介します。

aws-ec2-ssh
https://github.com/widdix/aws-ec2-ssh

aws-ec2-sshはIAMユーザとEC2のLinuxユーザを紐付けし、IAMユーザの作成・削除に連動して、Linuxユーザも作成・削除されるという便利ツールです。

それでは早速導入していきましょう。

aws-ec2-sshの導入方法(AWS側)

まずはAWS側の設定を行います。
EC2インスタンスがaws-ec2-sshを使えるようにするためのIAMロールを用意します。 下記のポリシーを持つIAMロールを作成し、連携を行うEC2インスタンスにロールを割り当てます。

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": [
      "iam:ListUsers",
      "iam:GetGroup"
    ],
    "Resource": "*"
  }, {
    "Effect": "Allow",
    "Action": [
      "iam:GetSSHPublicKey",
      "iam:ListSSHPublicKeys"
    ],
    "Resource": [
      "arn:aws:iam::<YOUR_USERS_ACCOUNT_ID_HERE>:user/*"
    ]
  }, {
      "Effect": "Allow",
      "Action": "ec2:DescribeTags",
      "Resource": "*"
  }]
}

※<YOUR_USERS_ACCOUNT_ID_HERE>にはAWSのアカウントIDを記入してください。

次にIAMグループを作成します。
ここで作成したグループを元に連携させるIAMユーザであるか、連携させるIAMユーザは管理者権限を付与する対象であるかを判別します。
今回は linux-member を連携対象とするグループ、linux-adminを特権ユーザとするグループとします。

  • linux-member( 連携対象とするグループ )
  • linux-admin(特権ユーザ用のグループ )

それでは連携させるIAMユーザを作成します。
今回はテストのために、次の3つのユーザを登録します。

  • user-member
    Linuxの一般ユーザとして登録するユーザ
    linux-member のIAMグループに 所属
  • user-admin
    Linuxの管理者ユーザとして登録するユーザ
    linux-memberとlinux-admin のIAMグループに 所属
  • no-linux-user
    Linuxには同期させないユーザ
    いずれのIAMグループにも所属しない

IAMユーザ作成後に
IAM > ユーザ > 対象のユーザ > 認証情報 > SSHパブリックキーのアップロード
から公開鍵認証用の公開鍵をアップロードします。

作成したIAMユーザとグループの状況はこのような形です。

aws-ec2-sshの導入方法(Linuxサーバ側)

ここからはLinuxサーバ側の作業になります。
まずはLinuxサーバにaws-ec2-sshをインストールします。

cd /usr/local/src
git clone https://github.com/widdix/aws-ec2-ssh.git
cd aws-ec2-ssh
./install.sh

aws-ec2-sshの設定ファイルを作成します。

cd /usr/local/src/aws-ec2-ssh
cp aws-ec2-ssh.conf aws-ec2-ssh.conf
vi aws-ec2-ssh.conf

aws-ec2-ssh.confの内容は以下のように設定します。

IAM_AUTHORIZED_GROUPS="linux-member" #管理対象するIAMグループ
LOCAL_MARKER_GROUP="iam-synced-users" #IAMユーザと連携していることを示すグループ
LOCAL_GROUPS=""
SUDOERS_GROUPS="linux-admin" #特権を付与するIAMグループ
ASSUMEROLE=""
# Remove or set to 0 if you are done with configuration
# To change the interval of the sync change the file
# /etc/cron.d/import_users
DONOTSYNC=0 #0の場合に同期機能が有効化

一通り設定ができたので、手動でIAMユーザと連携を行います。
なお、インストール時点で同期処理がcronに登録されており、デフォルトでは10分毎に同期されるように設定されています。

sh /opt/import_users.sh

実行後にユーザが作成されていることが確認できます。
同期対象外であったno-linux-userはLinux上にはユーザを作られていません。

# egrep "user-member|user-admin|no-linux-user" /etc/passwd
user-admin:x:1004:1005::/home/user-admin:/bin/bash
user-member:x:1005:1006::/home/user-member:/bin/bash

user-adminにはsudoの実行権限があります。

# ls /etc/sudoers.d/
90-cloud-init-users  user-admin
# cat /etc/sudoers.d/user-admin
user-admin ALL=(ALL) NOPASSWD:ALL

結果はこのようになりました。

  • Linux上にuser-adminとuser-memberというユーザが追加された
  • user-adminには管理者権限が付与されていた
  • 同期対象外のno-linux-userはLinux上にユーザは作成されなかった

意図したとおりに動作していますね。
今回は作成を試しましたが、IAMユーザから削除を行うと、Linuxサーバ上のアカウントも削除されます。

aws-ec2-ssh の仕組み

ここからはaws-ec2-sshの仕組みについて少し説明します。

Q.ユーザをどのように判別しているか?
/etc/aws-ec2-ssh.conf 内の設定をもとに判断して処理しています。

  • IAM_AUTHORIZED_GROUPS
    IAM上でこのグループに所属しているユーザがLinuxサーバへの同期の対象となります。
  • SUDOERS_GROUPS
    IAM上でこのグループに所属しているユーザが管理者権限(sudoの実行権限)を付与されます。
    同期の対象となるにはIAM_AUTHORIZED_GROUPSに指定されたグループに所属している必要があり、 このグループだけに所属している場合はLinuxサーバへの同期対象にはなりません。
  • LOCAL_MARKER_GROUP
    Linuxサーバ上でIAMと同期しているユーザを判別するためのグループです。 このグループに所属しているLinuxサーバ上のユーザがIAMユーザとの同期対象になります。


Q. 同期処理はいつ・どのように実施されるか?
インストール時に/etc/cron.d/import_usersが追加されます。
このcronの設定により10分おきに同期処理が実行されます。
インストール直後からcronが動き出してしまうので注意が必要です。

SHELL=/bin/bash
PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/opt/aws/bin
MAILTO=root
HOME=/
*/10 * * * * root /opt/import_users.sh

Q. 公開鍵認証はどのように行われているのか?
aws-ec2-sshをインストールすると /etc/ssh/sshd_config に下記の設定が追加されます。

AuthorizedKeysCommand /opt/authorized_keys_command.sh

この設定によりsshで接続時に /opt/authorized_keys_command.sh の実行結果を公開鍵として渡されています。
authorized_keys_command.shではaws cliのコマンドを発行し、対象ユーザと一致するIAMユーザに登録されている公開鍵の情報を取得しています。
この仕組みによりEC2インスタンス上に各ユーザの公開鍵を設置せずに公開鍵認証ができます。

さいごに

いかがでしたでしょうか。

AIMユーザを作成するだけで対象のLinuxサーバに自動的にユーザが追加され、ユーザの公開鍵をサーバに設置するなどの手間も省けます。

また、ユーザ作成時は厳密な手続きのもと作成を行われますが、 作成されたユーザに関しては厳密に管理が行われていないということもあります。
よくある見られるのは利用されていないユーザが削除されずに残っているというパターンです。 削除手続きが正しく行われていない場合もあれば、対象サーバが多く漏れていたということも考えられます。

このような問題を解決してくれる非常に便利なツールでした。
導入も容易なので、ぜひご活用ください。

S3静的ホスティングの次の一歩、https対応をCloudFrontとACMで行う

こんにちは、西山です。
最近、AWS認定ソリューションアーキテクト プロフェッショナルを取得しました!

既に合格したメンバー達が「やってやろうぜ」って雰囲気で挑戦していたのを見て、影響を受けました。人生やったもん勝ちです。

ただ、やはり触って使って身に付けるが基本です。
試験受かっただけのテスト野郎にならないように、今後も手を動かすのを意識して進んでいきます。

今回はS3で静的なサイトを公開した後に、サイトをhttpsに対応する方法を説明してまいります。

この記事の対象者

  • AWSのサービスを使い始めたばかり
  • S3で静的サイトを公開して、次に何か挑戦しようと考えている
  • httpのサイトをhttpsにしたい

■1. 【ACMを使い証明書を取得する】

ACM は AWS Certificate Manager の略です。
SSL/TLS 証明書を管理し、AWSのサイトに簡単に設定する事ができます。

ちなみに、サービス名の英語3文字の略語は、正式名称で覚えた方が混乱せず各AWSのサービス内容を把握できるのでおすすめです。

それでは、ACMの設定に進んでまいりましょう。
まずはAWS マネジメントコンソールでACMの画面を開きます。
「ACM」と入力すれば「Certificate Manager」と表示されますので選択します。

AWS Certificate Manager の画面が開きましたら
まず最初に行う事があります。

注意点

画面右上のリージョン選択で
米国東部 (バージニア北部) us-east-1」を選択してください。
今回はCloudFrontにACMで作成した証明書を関連付ける為です。

その後で、「証明書のプロビジョニング」より「今すぐ始める」をクリックして設定を開始します。

補足

Amazon CloudFront で ACM 証明書を使用するには、米国東部(バージニア北部) リージョンで証明書をリクエストまたはインポートする必要があります。CloudFront ディストリビューションに関連づけられたこのリージョンの ACM 証明書は、このディストリビューションに設定されたすべての地域に分配されます。

https://docs.aws.amazon.com/ja_jp/acm/latest/userguide/acm-regions.html

順番に設定を進めてまいります。
「パブリック証明書のリクエスト」を選択し「証明書のリクエスト」をクリック

ドメイン名の追加の入力箇所に来ました。
今回は静的なサイト http://odenge.jp/ をhttpsに対応する例で説明します。

ドメイン名は下記の2つを設定します。
odenge.jp
*.odenge.jp

※「*.odenge.jp」を設定したのは、今後、test.odenge.jp や sample.odenge.jp などでもhttps対応を考慮する為です。

注意点

「*.odenge.jp」だけでは、「odenge.jp」の設定は反映されないので
「odenge.jp」も記載してください。

以前は「Eメールの検証」のみだったとの事ですが
「Route 53」を使いS3で静的サイトをホスティングしている場合には
簡単に行う事ができる「DNSの検証を」選択します。

補足

DNS検証はRoute 53 でなくても行うことは可能です。

ここは特に何も入力しなくても大丈夫ですので
そのまま「確認」をクリック

確認し「確定とリクエスト」をクリック

「検証保留中」と表示されますが、そのまま「続行」ボタンを押します。

「状況」の「ドメイン」の箇所を開くと
「Route 53でのレコードの作成」ボタンが表示されますのでクリック

下記の情報でRoute 53 にACM設定の為のレコードが追加されます。
「作成」をクリックします。

「検証保留中」とまだ表示されていますが
DNSレコードの追加は成功しました。

「Route 53」で追加されたレコードを確認します。
「Route 53」では右上のリージョンは「グローバル」になります。

再度、「ACM」で右上のリージョンが「米国東部 (バージニア北部) us-east-1」を選択します。
少しだけ待つと「状況」が発行済みとなり証明書が発行されました。
私の場合は数分でしたが、最大で30分以上かかる場合もあるとの事でお待ちください。

これでACMで証明書の取得を行う事ができました。

■2. 【CloudFrontに証明書を設定する】

CloudFrontの画面に行き、右上のリージョンは「グローバル」しかないのでそのまま設定を進んで行き、「Web」の方の「Get Started」をクリック

補足

Q: ACM 証明書は、どの AWS のサービスで使えますか?

パブリックとプライベートの ACM 証明書は、以下の AWS のサービスで使えます。
• Elastic Load Balancing
• Amazon CloudFront
• Amazon API Gateway
• AWS Elastic Beanstalk
• AWS CloudFormation – 現時点では、E メール検証を使うパブリック証明書のみをサポートしています。

「Origin Domain Name」の箇所、手動で入力が必要かと思いましたが
空白にするとプルダウンメニューから選択できるようになり、静的サイトを公開しているS3を選択します。

「Origin Domain Name」と「Origin ID」が自動入力されます。
その他の項目は初期設定のままで下の項目に進みます。

「Alternate Domain Names」に設定するドメインを記載しました。
(www付きでもアクセスできるようにwww付きのドメインも記載しました)

「Custom SSL Certificate」を選択し
先ほどACMで作成した内容がプルダウンメニューで選択できるので選択します。

注意点

「Custom SSL Client Support」は
「Clients that Support Server Name Indication(SNI)」を選択します。
※下の項目を選択すると「$600 / month」と金額がかかってしまいます。

上記に記載した箇所以外は初期設定のまま作成すると
下記の画面のように追加された状態となります。

注意点

作成完了まで30分近くかかりました。結構時間がかかった印象ですので気長に待ちます。「Status」が「Deployed」になれば完成です。

「CloudFront」が「Deployed」になった後
「Route 53」に行き
レコードのタイプ「A」に設定されている「エイリアス先」を「CloudFrontディストリビューション」で作成した内容を選択し、「レコードセットの保存」をクリック

これで https://odenge.jp/ にアクセスすれば完成と考えていましたがエラーが起こりページが表示されませんでした。

「CloudFront」の設定を修正します。
一覧の「ID」に記載されているリンクテキストをクリックし
「Edit」ボタンを押して編集します。

S3の静的ホスティングではトップページをindex.htmlで作成していました。
「Default Root Object」に「index.html」と記載し「Yes, Edit」をクリック

問題なく表示され完成です!
いかがでしたでしょうか?

■3. 【追加のもう一歩 OAI設定】

CloudFrontを使ってアクセスする事ができるようになりましたが
S3のエンドポイントのURLを使っても、現時点ではアクセスできる状態です。

origin access identity(OAI)を使えば、S3に直接アクセスしても閲覧できなくする事が可能です。明確にCloudFrontを通してサイトアクセスを行わせる事を考慮したシステム構成にする事で、CloudFrontによりレイテンシーを低くする事や様々な構成・仕様を考慮した上でシステムを構築する事が可能です。

「CloudFront」の修正方法を見て行きましょう。
作成した内容をチェックし「Distribution Settings」をクリック

タブ「Origins and Origin Groups」を選択し該当の箇所をチェックし
「Edit」をクリック

「Restrict Bucket Access」を「Yes」
「Origin Access Identity」を「Create a New Identity」
「Comment」は自動で記入されるのでそのまま
「Grant Read Permissions on Bucket」を「Yes, Update Bucket Policy」

そして「Yes, Edit」をクリック

修正した「CloudFront」の「Status」が「Deployed」になった後で
S3のバケットポリシーを修正します。

※上記で「Grant Read Permissions on Bucket」を「Yes, Update Bucket Policy」を選択しましたが更新されなかったので直接、S3のバケットポリシーを修正します。

「CloudFront」の「Origin access identity」で下記の図の
「Amazon S3 Canonical User ID」をコピーします。

S3バケットポリシーの「Principal」を変更します。

"Principal":{"CanonicalUser":"コピーした乱数"},

参考

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-bucket-policies.html#example-bucket-policies-use-case-6

上記の内容で「Principal」の箇所を修正しバケットポリシーを更新しました。すると「Principal」の記載内容が違う記述に書き換わりましたが、それまでアクセスできていたS3エンドポイントのURLからのアクセスが拒否され、CloudFrontからのアクセスのみ許可されている事が確認できました。

あとがき

証明書の設定と聞くと、少し難しい印象を持たれるかもしれません。
しかし、マネジメントコンソールの操作で簡単に設定する事ができます。
ぜひ、試していただければ幸いです。

証明のない現代にドロップキック

以上、西山でした。

AWS Chatbot(beta版)を用いたSlack通知

クラウド事業部 インフラエンジニアの吉岡です。

AWS Chatbot(beta版)を用いたSlack通知を試してみましたので、ご紹介したいと思います。

AWS Chatbotとはどういった機能なのか

Slack チャンネルや Amazon Chime チャットルームに対して通知を行う機能です。
サポートしているサービスの通知をSNSトピックに転送し、
SNSトピックからChatbotを経由して各チャットサービスに表示することができます。
現在対応しているサービスは下記の通りです。

AWS Billing and Cost Managemen
AWS CloudFormation
AWS CodeBuild等の開発者用ツール
Amazon CloudWatch Alarms
Amazon CloudWatch Events
AWS Config
AWS Health
AWS Security Hub
Amazon GuardDuty
AWS Systems Manager

今回はAWS System ManagerのRunCommandを実行し、Slackに通知します。設定する通知フローは下記の図の通りです。

Slackへの通知フロー

手順

・SNSトピックの作成
Amazon SNS→トピック→トピックの作成と進み、適当な名前を付けたトピックを作成します。(サブスクリプションはまだ設定しません。)

f:id:seeds-std:20191114181702p:plain

・CloudWatchEventの設定
CloudWatch→ルールの作成、と進みルールを作成します。
今回はchatbot-testという名前で作成しました。

サービス名:EC2 Simple Systems Manager(SSM)
f:id:seeds-std:20191114182349p:plain

イベントタイプ:すべてのイベント
f:id:seeds-std:20191114182353p:plain

ターゲット:SNSトピック(先ほど作成したトピック)
f:id:seeds-std:20191114182357p:plain

・Chatbotのインストール
AWS Chatbotを選択し、
Chatbotの設定を始めていきます。
通知を送る先となるChatツールを選択します。

f:id:seeds-std:20191114182950p:plain

SlackにChatbotをインストールします。

f:id:seeds-std:20191114183017p:plain

今回はpublicのslacktestというチャンネルに通知するように設定します。
IAMロールは自動作成、ロール名はchatbot-slackとしました。
SNS regionはSNSトピックの設定されているリージョン(今回は東京リージョン)
SNS topicsは先ほど作成したchatbot-testを指定します。

f:id:seeds-std:20191114183152p:plain
f:id:seeds-std:20191114183155p:plain
f:id:seeds-std:20191114183157p:plain

設定が完了すると、SNSトピックにサブスクリプションが追加されていることが確認できます。

動作確認

RunCommandを実行し、Slackに通知されることを確認します。
今回はAWS-RunShellScriptでechoコマンドを実行します。
インスタンス指定
SNS通知なし
の条件で下記の内容で実行します。

#!/bin/bash
echo "Hello chatbot"

実行後に下記の様な通知がSlackに届いていることが確認できました。

f:id:seeds-std:20191114183614p:plain

あとがき

今回はChatbotを用いてSlack通知を行うために必要な作業がどういったものか、勉強を兼ねて試してみましたが、非常に簡単に設定ができました。
現在はbeta版ですので、一部の環境のみですが、シーズではこの機能を利用してAWS Health Dashboardの情報をslack通知する設定を行っています。

CloudTrailが有効化されていることが前提ですが、セッションマネージャーを使ったログイン時に通知が届くような設定もできるようです。
正式なリリースが楽しみですね!

Kotlin/JSのAWS Lambda関数でJsonを返してみる

f:id:seeds-std:20191010160615p:plain
こんにちは, Web事業部の西村です
私の前回の記事 Promiseを利用して非同期に処理するようにしてみました
今回はこのLambda関数で kotlinx.serialization を用いてJsonを返すようにしてみたいと思います

目次

過去の記事

なぜ kotlinx.serialization?

まず kotlinx.serialization を用いる理由です
ここまでの記事を読んでくださっている方々であれば “わざわざないでしょ” と思っている方もいると思います
その通りで, json()JSON.stringifiy() 関数を用いればJsonを返すことができます
しかし, kotlinx.serialization を用いることにより, Client側でも同じクラスを利用することができ, 開発する際に便利であるといえます
またマルチプラットフォームであるため様々な環境で利用できるというのもメリットです

注意事項

この記事では kotlinx.serialization に関する詳しい解説は行いません
詳しく知りたい方は下記のリポジトリをご覧ください
github.com

開発環境

この記事では下記の環境で開発を行っています

  • AdoptOpenJDK 1.8.0_222-b10
  • IntelliJ IDEA Community 2019.2.2
  • Kotlin 1.3.50

プロジェクト

前回の記事 まで作成したプロジェクトを利用します
プロジェクトの構成は下記のようになっています

KotlinLambda
├─.gradle/
├─.idea/
├─build/
├─gradle/
├─src/
│ └─main/
│   └─kotlin/
│     └─jp.co.seeds_std.lambda.kotlin/
│       └─handler.kt
├─build.gradle.kts
├─compress.zip
├─gradlew
├─gradlew.bat
└─settings.gradle.kts

また, handler.ktは下記のようになっています

package jp.co.seeds_std.lambda.kotlin
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.delay
import kotlinx.coroutines.promise
import kotlin.js.Json
import kotlin.js.json
@JsExport
@JsName("handler")
fun handler(event: Json, context: Json) = GlobalScope.promise {
val tasks = (1..10).map {
async {
delay(1000)
it
}
}
val results = tasks.awaitAll()
return@promise Response(body = "Result: ${results.sum()} - Coroutines")
}
data class Response(
val statusCode: Int = 200,
val body: String = "{}",
val isBase64Encoded: Boolean = false,
val headers: Json = json(),
val multiValueHeaders: Json = json()
)

Jsonを返すようにする

目標

今回は下記のようなJsonを返すようにしてみます

{
"id": 0, // ID
"text": "", // 文字列
"random_numbers": [5, 38, 24] // 適当な数値
}

依存関係を追加する

kotlinx.serialization は外部ライブラリですので依存関係を追加する必要があります
また, 専用のプラグインも必要であるためプラグインの追加も行います

build.gradle.kts / pluginsブロック

plugins {
kotlin("js") version "1.3.50"
kotlin("plugin.serialization") version "1.3.50"
}

依存関係の追加

build.gradle.kts / dependenciesブロック

sourceSets["main"].dependencies {
implementation(kotlin("stdlib-js"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:1.3.2")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:0.13.0") // 追加
}

リポジトリに jcenter を追加する必要があるので追加します

build.gradle.kts / repositories

repositories {
mavenCentral()
jcenter() // 追加
}

クライアント側に返すクラスを作成する

作成するクラスが今回のJsonに変換するクラスとなるので各種アノテーションも付与します
今回は Responseクラス の下に作成します(本当は別ファイルにするほうがいいです)

handler.kt

@Serializable
data class ResponseBody(
@SerialName("id")
val id: Int = 0,
@SerialName("text")
val text: String = "",
@SerialName("random_numbers")
val randomNumbers: List<Int> = emptyList()
)
  • @Serializable これがついているクラスがシリアライズ/デシリアライズすることができます
  • @SerialName(String) 引数で決められた文字列がkeyとなるようになります, ない場合は変数名が利用されるようになるのでなくても問題ないです

Jsonを返す準備をする

まずは前回までに書いていたコードの一部を削除しスッキリさせます

handler.kt

@JsExport
@JsName("handler")
fun handler(event: Json, context: Json) = GlobalScope.promise {
return@promise Response(body = "")
}

続いて, 先ほど作ったクラスを文字列に変換するために kotlinxのJsonオブジェクト を作成します

handler.kt

@JsExport
@JsName("handler")
fun handler(event: Json, context: Json) = GlobalScope.promise {
val json = kotlinx.serialization.json.Json(JsonConfiguration.Stable) // 追加
return@promise Response(body = "")
}

⚠注意1
今回の記事ではすでにkotlinが実装している Jsonクラス をインポートしているため同じ名前であるkotlinxの Jsonクラス をインポートできません
kotlinのJsonkotlinxのJson とを間違わないよう注意してください

⚠注意2
JsonConfiguration には Stable 以外の設定もありますが, 実験的機能となっていますので利用はお勧めしません

Jsonを返す

まずは先ほど作成した ResponseBodyクラス のオブジェクトを作成します

handler.kt

@JsExport
@JsName("handler")
fun handler(event: Json, context: Json) = GlobalScope.promise {
val json = kotlinx.serialization.json.Json(JsonConfiguration.Stable)
val responseBody = ResponseBody(1, "Lambda Json!!!", List(3) { Random.nextInt(100) }) // 追加
return@promise Response(body = "")
}

最後に stringify関数 を用いてクラスを文字列に変換し レスポンスのbody に設定します

handler.kt

@JsExport
@JsName("handler")
fun handler(event: Json, context: Json) = GlobalScope.promise {
val json = kotlinx.serialization.json.Json(JsonConfiguration.Stable)
val responseBody = ResponseBody(1, "Lambda Json!!!", List(3) { Random.nextInt(100) })
return@promise Response(body = json.stringify(ResponseBody.serializer(), responseBody)) // 変更
}
  • ResponseBody.serializer() この関数は @Serializable アノテーションがついているクラスの場合コンパイラが自動的に作成してくれます

動作確認

関数をデプロイし, API GatewayのURLにアクセスし {"id":1,"text":"Lambda Json!!!","random_numbers":[ランダムな数字が3つ]} 表示されれば成功です

最後に

いかがでしたでしょうか
LambdaでJsonを返すのはありがちです
これを利用することによりKotlinで作成するマルチプラットフォームなアプリケーションではかなり強力になりうると考えます
また, ここまで4つの記事に分け Kotlin/JS をもちいて AWS Lambda関数 を作って便利にしてみました
これがベストプラクティスかどうかはプロジェクトによると思いますが、1つの参考になるとうれしい限りです
ここまで読んでいただきありがとうございました


蛇足

JSON.stringify() を使ってJsonを返してみる

JavaScriptにもとから存在する, JSON.stringify を用いてもJsonを返すことができます

handler.js

@JsExport
@JsName("handler")
fun handler(event: Json, context: Json) = GlobalScope.promise {
val responseBody = ResponseBody(1, "Lambda Json!!!", listOf(0, 1, 2))
return@promise Response(body = JSON.stringify(responseBody))
}

実行結果

{"id":1,"text":"Lambda Json!!!","randomNumbers":[0,1,2]}

しかしこれではJsonのKeyは指定できないので変数をすべて private に変更すると結果が変わるので注意が必要です

privateに変更したときの実行結果

{"id_0":1,"text_0":"Lambda Json!!!","randomNumbers_0":[0,1,2]}

こういうケースもあるため kotlinx.serialization を用いることをお勧めします

json() と JSON.stringify() を使ってJsonを返してみる

一番オーソドックスでわかりやすい返し方だと私は思います

handler.js

@JsExport
@JsName("handler")
fun handler(event: Json, context: Json) = GlobalScope.promise {
val responseBody = json(
"id" to 0,
"text" to "Lambda Json!!!",
"random_numbers" to listOf(0, 1, 2)
)
return@promise Response(body = JSON.stringify(responseBody))
}

実行結果

{"id":1,"text":"Lambda Json!!!","random_numbers":[0,1,2]}

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

AWSソリューションアーキテクトアソシエイト(SAA-C01)に合格しました

f:id:seeds-std:20191021181435p:plain

こんにちは。小國です。

社員の AWS 認定の取得が相次いでいるシーズです。私も 1ヶ月ほど前になりますが、AWSソリューションアーキテクトアソシエイト(SAA-C01)に無事に合格できました。

AWSソリューションアーキテクトアソシエイトを目指す方の参考になればと思い、その時の勉強方法などを紹介します。

まずは自己紹介

  • ソフトウェアエンジニア 12年(主に LAMP 環境で CakePHP、Laravel、他に Android もちょっとできます)
  • 応用情報技術者試験(AP)
  • LAMP や LEMP 程度なら構築できます(ネットワークは要勉強)
  • 過去に LPIC-1(LPIC-2 は前半だけ合格。。。)
  • とりあえず、なんでもやってみる人

勉強前の AWS の知識

AWS は EC2、VPC、RDS のチュートリアル*1を触ったことがある程度で、その他の各種サービスについては以下のような認識でした。

  • S3、聞いたことある
  • ACM、???
  • CloudFront、聞いたことある
  • ELB、聞いたことある
  • リージョン・AZ・エッジロケーション、微妙。。。
  • Lambda、ランブラ?(後にラムダと読むことを知る)
  • DynamoDB、???

勉強方法

勉強期間は約 2週間で、以下の 2冊の本をやりきりました。

AWS認定資格試験テキスト AWS認定 ソリューションアーキテクト-アソシエイト

AWS認定資格試験テキスト AWS認定 ソリューションアーキテクト-アソシエイト

AWS認定資格試験テキスト AWS認定 ソリューションアーキテクト-アソシエイト

  • 作者: NRIネットコム株式会社,佐々木拓郎,林晋一郎,金澤圭
  • 出版社/メーカー: SBクリエイティブ
  • 発売日: 2019/04/20
  • メディア: 単行本
  • この商品を含むブログを見る

AWS の主要なサービスについて要点をがしっかりとまとまっていて、わかりやすくとてもためになりました。章末や模擬試験の解答の解説も丁寧で良かったと思います。

勉強方法はというと、一通り目を通し、章末や模擬試験をすべて正解するまで(間違っている箇所はどこが間違っているか分かるまで)やりました。

おそらく 4、5回はやったかと思います。

徹底攻略 AWS認定 ソリューションアーキテクト – アソシエイト教科書

徹底攻略 AWS認定 ソリューションアーキテクト ? アソシエイト教科書

徹底攻略 AWS認定 ソリューションアーキテクト ? アソシエイト教科書

  • 作者: ??部昭寛,宮?光平,菖蒲淳司,株式会社ソキウス・ジャパン
  • 出版社/メーカー: インプレス
  • 発売日: 2019/01/18
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る

1冊では不安だったので、こちらも購入しました。

こちらは、問題を解いて間違えた箇所について、本で調べました。こちらの本も同様に、すべて正解するまでやりました。

先に購入した対策本で理解ができていなかった箇所がカバーできたのでよかったと思いますが、解答の解説が少なかったように思います。

模擬試験

本試験の 2日前ほどに受けました。思っていたより簡単で、結果も 84%ほどの正答率でした。

いよいよ本試験!

本試験ですが、模擬試験に比べ文章も長く・問題も単純なものが少なく、難しかったです。

40分ほど残して一通り解答し、残りの時間いっぱいを使って見直しをしました。終わったあとはすごく頭が痛かったです。頭がしっかり働く午前中に受験したのが幸いでした。

問題文は、VPC や ELB のネットワークの複合的な問題や、DynanoDB、暗号化についての問題が多かった印象です。

感想

とりあえずは、AWSソリューションアーキテクトアソシエイト(SAA-C01)を取得できよかったです。

ただし、実際に AWS を使いこなせるかは甚だ疑問で、実際に手を動かしてみて知識・技術の定着をして、業務に役に立てるようやっていきたいです。

また、相当の難易度だと聞く、プロフェッショナル取得を目指せるよう勉強したいなと。。。

余談ではありますが、弊社 CTO の、原口さんこと cs_sonar さんが、AWSソリューションアーキテクトプロフェッショナル(SAP-C01)に合格されてます。

プロフェッショナルを目指す方は、ご参考にしてはいかがでしょうか。

blog.seeds-std.co.jp

Kotlin/JSのAWS Lambda関数でPromiseを使うようにする

f:id:seeds-std:20191010160615p:plain
こんにちは, Web事業部の西村です
私の前回の記事 では dyanmic の利用をなくすようにしました
そして今回は, Callbackの呼び出しをやめて, JavaScriptの非同期処理( Promise )になるよう変更したいと思います
また, Promiseスタンダード なものと Coroutines のものの2種類がありますので, この記事ではその両方を紹介したいと思います

目次

過去の記事

なぜ非同期に?

非同期処理することにより複数の処理を同時に実行することができます
例えば, 複数のファイルを受け取り, S3に保存する といったことを考えてみます
非同期処理ではない場合, 1ずつしかファイルのアップロードができません
非同期処理の場合, 複数のファイルを同時にアップロードできるようになるので時間の短縮が図れます

注意事項

この記事では Promise の詳しい解説は行いません
詳しく知りたい方は下記の記事を参考にしてください
qiita.com

開発環境

この記事では下記の環境で開発を行っています

  • AdoptOpenJDK 1.8.0_222-b10
  • IntelliJ IDEA Community 2019.2.2
  • Kotlin 1.3.50

プロジェクト

前回の記事 まで作成したプロジェクトを利用します
プロジェクトの構成は下記のようになっています

KotlinLambda
├─.gradle/
├─.idea/
├─build/
├─gradle/
├─src/
│ └─main/
│   └─kotlin/
│     └─jp.co.seeds_std.lambda.kotlin/
│       └─handler.kt
├─build.gradle.kts
├─compress.zip
├─gradlew
├─gradlew.bat
└─settings.gradle.kts

また, handler.ktは下記のようになっています

package jp.co.seeds_std.lambda.kotlin
import kotlin.js.Json
import kotlin.js.Promise
import kotlin.js.json
@JsExport
@JsName("handler")
fun handler(event: Json, context: Json, callback: (Error?, Response?) -> Unit) {
val response = Response(body = "Hello from Kotlin Class Lambda!")
callback(null, response)
}
data class Response(
val statusCode: Int = 200,
val body: String = "{}",
val isBase64Encoded: Boolean = false,
val headers: Json = json(),
val multiValueHeaders: Json = json()
)

スタンダードなPromise

まずはkotlin-stdlibに実装されている Promise を利用してみます

関数の書き換え

handler 関数を変更します

handler.kt

@JsExport
@JsName("handler")
fun handler(event: Json, context: Json, callback: (Error?, Response?) -> Unit) {
val response = Response(body = "Hello from Kotlin Class Lambda!")
callback(null, response)
}

@JsExport
@JsName("handler")
fun handler(event: Json, context: Json) = Promise<Response> { executor, reject ->
executor(Response(body = "Hello from Kotlin Async Lambda!"))
}
  • executor は処理成功時に実行する関数で, 引数はジェネリクスで指定したクラス (この記事では Response ) となります
  • reject は処理失敗時に実行する関数で, 引数は Throwable クラスのオブジェクトとなります

動作確認

関数をデプロイし, API GatewayのURLにアクセスし Hello from Kotlin Async Lambda! と表示されれば成功です

もう少し恩恵を受けてみる(スタンダード)

今度はNode.jsの setTimeout を利用して本当に非同期に処理されているかを確認したいと思います
その前に, Kotlin/JSにはNode.jsの setTimeout は定義されていないので追加します
今回追加する場所はコードの最下部に追加します

handler.kt

external fun setTimeout(callback: dynamic, delay: Int, vararg args: Any?): Int
external fun clearTimeout(timeout: Int)
  • external : 外部の関数/変数を利用するために記述します。トランスパイルした場合にそのまま残るようになります
  • callback の部分が dynamic となっていますが, 関数の引数が固定値ではないためやむを得ず dynamic としています

続いて handler 関数を変更します

handler.kt

@JsExport
@JsName("handler")
fun handler(event: Json, context: Json) = Promise<Response> { executor, reject ->
val results = (1..10).map {
Promise<Int> { childExecutor, _ ->
setTimeout({childExecutor(it)}, 1000)
}
}
Promise.all(results.toTypedArray()).then {
executor(Response(body = "Result: ${it.sum()} - Standard"))
}
}

このコードでは 1秒待ってから数字を返す というコードを10回繰り返すものとなっています

動作確認

関数をデプロイし, API GatewayのURLにアクセスし約1秒後に Result: 55 - Standard と表示されれば成功です
※初回の実行では2-3秒ほどかかる場合もあります

通常の処理であれば 1秒待って数字を返す を10回行えば10秒かかるはずですが, 非同期に実行されているため約1秒で完了します

CoroutinesのPromise

標準実装の Promise では見にくく感じませんでしたか?(私は少なからず見にくいなと感じました)
非同期処理を書くのであれば, Node.jsみたいに async で書きたいところです
そこで使うのが kotlinx.coroutines です

依存関係を追加する

kotlinx.coroutines は外部ライブラリなので build.gradle.kts に依存関係を追加する必要があります

build.gradle.kts

implementation(kotlin("stdlib-js"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:1.3.2") // 追加

コードを書き換える

まずはただの文字列を返すように handelr 関数を変更します

handler.kt

@JsExport
@JsName("handler")
fun handler(event: Json, context: Json) = GlobalScope.promise {
return@promise Response(body = "Hello from Kotlin Coroutines Lambda!")
}

先ほどのコードと比較するとかなり単調なものになりました
※エラーを返す際は throw NullPointerException() のように throw するだけとなります

動作確認

関数をデプロイし, API GatewayのURLにアクセスし Hello from Kotlin Coroutines Lambda! と表示されれば成功です

もう少し恩恵を受けてみる(Coroutines)

先ほどと同じ関数を実装してみます

handler.kt

@JsExport
@JsName("handler")
fun handler(event: Json, context: Json) = GlobalScope.promise {
val tasks = (1..10).map {
async {
delay(1000)
it
}
}
val results = tasks.awaitAll()
return@promise Response(body = "Result: ${results.sum()} - Coroutines")
}

先ほどとは異なり, CoroutineScope 内で実行できるため, async 関数を利用できます

動作確認

関数をデプロイし, API GatewayのURLにアクセスし約1秒後に Result: 55 - Coroutines と表示されれば成功です
※初回の実行では2-3秒ほどかかる場合もあります

最後に

いかがでしたでしょうか
非同期処理が行えるとよりできることの幅が増えるかと思います
また、Coroutinesの Promise と標準実装の Promise は互換性があるため外部ライブラリが非同期処理を行う場合にも使え,
よりKotlinらしい書き方ができるようになっていくと思います
次回は kotlinx.serialization を用いてJsonのレスポンスを行う記事を書きたいと思います
ここまで読んでいただきありございました

Kotlin/JSのAWS Lambda関数でJsonを返してみる

AWSソリューションアーキテクトプロフェッショナル(SAP-C01) に合格しました

f:id:seeds-std:20191010165532p:plain

クラウド事業部の原口です。

AWSソリューションアーキテクトプロフェッショナル試験を受けてきて一度不合格となった末に合格しました。
合格点は750/1000点と結構お高め…
難しかったです。 勉強を初めてから1ヶ月半ほどかかりました。

一度不合格となってますし偉そうな事は言えないのですが、こんな勉強をしました、という事を書いてみたいと思います。

試験の概要

AWS 認定ソリューションアーキテクト – プロフェッショナル
AWS プラットフォームでの分散アプリケーションおよびシステムを設計するための
高度な技術的スキルと専門知識を認定します。
AWS でのクラウドアーキテクチャの設計およびデプロイに関する 2 年以上の実務経験を持つ方が対象です。
この試験で評価する能力は次のとおりです。
• AWS における、動的なスケーラビリティ、高可用性、フォールトトレランス、
信頼性を備えたアプリケーションの設計およびデプロイ
• 与えられた要件に基づいてアプリケーションを設計およびデプロイするための、適切な AWSサービスの選択
• AWS における複雑な多層アプリケーションの移行
• AWS におけるエンタープライズ全体のスケーラブルな運用の設計およびデプロイ
• コストコントロール戦略の導入

かなりのボリュームの試験です。
AWS 認定ソリューションアーキテクト – アソシエイト の上位版に位置する問題となります。

試験結果は 100~1000 点の範囲のスコアでレポートされます。最低合格スコアは 750 点です。スコ
アによって、試験での全体的な成績と合否がわかります。スケールドスコアモデルは、難易度にわず
かな違いのある複数の試験形式のスコアを平均化するために使用されます。

100点から?という単純な疑問とスケールドスコアモデルというものがわからなかったので調べてみましたが、
あまりよくわからなかったのですが「難易度が異なる試験問題をいい感じに調整」するモデルと解釈しました。
つまり問題によって配点が違ってくる、という事ですかね?

試験時間は170分。問題数は75問ですので1問あたり2~3分程度で解く必要があります。
試験合格の有効期限は3年みたいです。

その他詳細な試験ガイドは以下となります(公式)
試験ガイド

試験勉強

私はAWSを初めて触ってからもう5年くらいは経っていると思うのですが、
業務で触れるサービスが中心となってしまい、どうしてもよくわからないサービスというのは存在しました。
そのため、いったい自分は何が苦手なのか、を把握する事からはじめました

模擬試験

模擬試験を受けて再学習が必要な領域を特定と試験の感じの把握からはじめました
サンプル問題でもわかりますが、言い回しが独特でとにかく文章が長い、という印象です

公式ドキュメントの「よくある質問」

よくある質問を読み込むのを中心に行いました。
サービスの制限など実運用を検討する方へのドキュメントである為、
サービスのネックや落とし穴となりやす部分が集約されているものと感じました。

ホワイトペーパー

DR関連のものとストレージサービス関連のものはよく読みました。
AWS Well-Architectedなどはななめ読みしたくらいです。

blackbelt

よく知らないサービスはblackbeltを見ました。
スライドをぽちぽちーとしてたんですが、ちゃんと動画で見ると理解度が段違いだったのでできれば動画がいいと思います

AWS クラウドサービス活用資料集 | サービス別資料

心構え

膨大な文章量と170分

とにかく文章が長いので単純に疲弊してきます。
集中力を長持ちさせる自分なりの方法を見つける必要がありました
疲れてくると読んでるだけで頭に入ってない事が多くてそういう時に集中しなおせる方法を持ってるといいです
秘訣について「170分くじけない心が必要」と聞いて笑ってたんですが
本当に重要ではないかと思いました

正常性バイアスとの戦い

疲労してくると「正常正バイアス」がかかりやすくなる気がしました。
問題の選択肢はすべてその問題を解決できるものであったり、もっともらしい書き方をされていて
知識が不足していると簡単に迷います。
その状態で出てくるのが以下のような考え。

・実務ではこんなやり方はしないだろう
・こういう時は枯れた技術で望むべき
・問題文に定義されてないからこういう意味に違いない

これらが出てきた時は大体知識不足が原因で明確な説明ができない以上は知識不足であると自分に言い聞かせました。
わりとここで問題の深読みまでしだすと精神的にもハマるし時間も浪費します。

サービスや機能の特性を単語で関連付ける

ストリーミングデータの処理といえばKinesis。
といった形で、仮想的だけど実務的なRFPからAWSにおける提案例を連想できるようにしました。
実際実務として提案を心がけている事もありこの方法は自分に合った勉強法でした。

短期集中で行う

長いスパンで考えるよりも2週間くらいでがーーっとやった方がよいです

その他

試験にはPSIとピアソンVUEがありますが、可能な限りピアソンVUEでの受験をおすすめします!
PSIでは監督員と遠隔でチャットをしながら、カメラで常時監視されながらの試験となり、
「キーボードから手を離してはいけない」などのプレッシャーが半端なかったです笑

ピアソンVUEでも監督員はもちろん居ますが、口元に手をあてたり、頭を掻いたりしても問題はないので
この点は心理的負担がだいぶ減ります!

感想

正直合格の文字を見た時は喜びよりほっとしました。2,3回確認しました。(笑)
社内での人権ができてよかったです。

自信もつき、試験を通じた勉強を行った事で、より深く!AWSの理解が深まったという実感があります。
これからはプロフェッショナル原口としてよりお客様にピッタリのクラウド最適化した提案ができると思います!
お困りの事がありましたら、是非ご相談下さい!!

DevOpsとかの他の試験も受けていくぞー!って思ってたのですが
本当に疲れたので、とりあえずはアイスボーンをやります。
社内でもどんどんAWS認定の取得者を増やしたいですね。
これから試験を受ける方は頑張って下さい!

宣伝枠

AWSの無料相談会というものを行っています!
www.seeds-std.co.jp

弊社ではAWSが大好きなエンジニアを募集しています!じょいなす!
recruit.seeds-std.co.jp

Kotlin/JSのAWS Lambda関数を便利にしてみる

f:id:seeds-std:20191010160615p:plain

こんにちは, Web事業部の西村です
私の前回の記事 ではKotlin/JSを用いてLambda関数を書いてみました
しかし, dynamic を利用していたため使いにくい部分もあったと思います
今回はその点を改良しより使いやすくなるよう変更したいと思います

目次

過去の記事

開発環境

この記事では下記の環境で開発を行っています

  • AdoptOpenJDK 1.8.0_222-b10
  • IntelliJ IDEA Community 2019.2.2
  • Kotlin 1.3.50

プロジェクト

前回の記事 で作成したプロジェクトを利用します
プロジェクトの構成は下記のようになっています

KotlinLambda
├─.gradle/
├─.idea/
├─build/
├─gradle/
├─src/
│ └─main/
│   └─kotlin/
│     └─jp.co.seeds_std.lambda.kotlin/
│       └─handler.kt
├─build.gradle.kts
├─compress.zip
├─gradlew
├─gradlew.bat
└─settings.gradle.kts

Jsonを使って便利にしてみる

dynamicをなくす

Kotlin/JSには dynamic に似た Json があります
dynamic はJavaScriptのデータであったり, オブジェクトを格納することができますが, Json はKey-Value形式でのみ格納できるものとなっています
handler関数の引数, eventcontext はどちらもJavaScriptのJsonオブジェクトですのでKotlinの Json に変換できます
また, Callbackの第二引数もJsonオブジェクトを設定するためこちらも変更できます

handler.kt

fun handler(event: dynamic, context: dynamic, callback: (Error?, dynamic) -> Unit)

↑ が ↓ に変更できます

fun handler(event: Json, context: Json, callback: (Error?, Json?) -> Unit)

このJsonオブジェクトへのアクセスはMapのように利用してアクセスすることができます

event["body"] // こちらか
event.get("body") // こちらでデータの取得ができます

Callbackに渡すのJsonを関数で作れるようにする

API Gatewayでは下記のJsonを処理するようになっています *1

{
"isBase64Encoded": true|false,
"statusCode": httpStatusCode,
"headers": { "headerName": "headerValue", ... },
"multiValueHeaders": { "headerName": ["headerValue", "headerValue2", ...], ... },
"body": "..."
}

こちらをもとに引数が5つの関数を作成したいと思います
記述する場所はhandler関数の下になります

handler.kt

fun responseJson(statusCode: Int = 200, body: String = "{}", isBase64Encoded: Boolean = false, headers: Json = json(), multiValueHeaders: Json = json()) = json(
"statusCode" to statusCode,
"body" to body,
"isBase64Encoded" to isBase64Encoded,
"headers" to headers,
"multiValueHeaders" to multiValueHeaders
)
  • json() Jsonを作成する関数です/引数は vararg Pair<String, Any?> となっており json() と書くと空のJsonを作ることができます

Callbackに渡すよう変更する

現在handler関数は下記のようになっていると思います

handler.kt

@JsExport
@JsName("handler")
fun handler(event: Json, context: Json, callback: (Error?, Json?) -> Unit) {
val response: dynamic = object {}
response["statusCode"] = 200
response.body = "Hello from Kotlin Lambda!"
callback(null, response)
}

この関数を先ほど作成した関数を用いた形に変更します

@JsExport
@JsName("handler")
fun handler(event: Json, context: Json, callback: (Error?, Json?) -> Unit) {
val response = responseJson(body = "Hello from Kotlin Json Lambda!")
callback(null, response)
}

関数をデプロイする

前回と同様の手順を用いて関数をデプロイします
画面右側の Gradle タブを開き, kotlin_lambda -> Tasks -> other の順に開き, その中の compress をダブルクリックして実行します
ビルドに成功するとプロジェクトのディレクトリに compress.zip というファイルが更新されます
この生成されたファイルをコンソールからアップロードし, 保存します

動作確認

あらかじめ作成しておいたAPI GatewayのURLにアクセスし Hello from Kotlin Json Lambda! と表示されれば成功です

レスポンスをクラスに変更する

レスポンスはクラスを用いても影響が少ないためクラスに置き換えます
リクエストにクラスを利用しない理由については蛇足をご覧ください

まずは responseJson関数 を変更します

handler.kt

fun responseJson(statusCode: Int = 200, body: String = "{}", isBase64Encoded: Boolean = false, headers: Json = json(), multiValueHeaders: Json = json()) = json(
"statusCode" to statusCode,
"body" to body,
"isBase64Encoded" to isBase64Encoded,
"headers" to headers,
"multiValueHeaders" to multiValueHeaders
)

data class Response(
val statusCode: Int = 200,
val body: String = "{}",
val isBase64Encoded: Boolean = false,
val headers: Json = json(),
val multiValueHeaders: Json = json()
)

続いて, Callbackの引数を変更します

fun handler(event: dynamic, context: dynamic, callback: (Error?, Json?) -> Unit)

fun handler(event: Json, context: Json, callback: (Error?, Response?) -> Unit)

最後にレスポンスを変更します

@JsExport
@JsName("handler")
fun handler(event: Json, context: Json, callback: (Error?, Json?) -> Unit) {
val response = responseJson(body = "Hello from Kotlin Json Lambda!")
callback(null, response)
}

@JsExport
@JsName("handler")
fun handler(event: Json, context: Json, callback: (Error?, Response?) -> Unit) {
val response = Response(body = "Hello from Kotlin Class Lambda!")
callback(null, response)
}

この変更ができたらデプロイし動作を確認します
Hello from Kotlin Class Lambda! と表示されれば成功です

最後に

いかがでしたでしょうか
Kotlinを用いていると dynamic では少し取り扱いにくく感じてしまいます
しかし Json にしてしまうことで,Mapのように利用でき, より使いやすいのではないかと思います
次回は非同期処理にする記事を書きたいと思います
ここまで読んでいただきありございました

Kotlin/JSのAWS Lambda関数でPromiseを使うようにする


蛇足

リクエストのほうもクラス使ったらだめなの?

注意事項はありますが、クラスを利用しても問題はないです

handler.kt

package jp.co.seeds_std.lambda.kotlin
import kotlin.js.Json
import kotlin.js.json
@JsExport
@JsName("handler")
fun handler(event: Event, context: Json, callback: (Error?, Response?) -> Unit) {
println("body: ${event.body} isBase64Encoded: ${event.isBase64Encoded}")
// event.copy() // Error!
val response = Response(body = "Hello from Kotlin Json Lambda!")
callback(null, response)
}
data class Event(val body: String?, val isBase64Encoded: Boolean)
data class Response(
val statusCode: Int = 200,
val body: String = "{}",
val isBase64Encoded: Boolean = false,
val headers: Json = json(),
val multiValueHeaders: Json = json()
)

この時ログに bodyisBase64Encoded の出力が行われます
その後 copy() を実行しようとした場合, 変数 eventEventクラスではなく Json となっていますので, 関数が見つからず実行時にエラーが出てしまいます
また, 注意点として private な変数はトランスパイルした際に変数名が変更されてしまうためうまく利用できません
そのため, リクエストの eventcontext はJsonで, レスポンスはKotlinのクラスを利用していくことがいいのかなと私は思います
蛇足も読んでいただきありございました

Page 1 of 3

© SEEDS Co.,Ltd.