AWSサービスの概要を一覧化するスクレイピング

背景

AWSにはサービスが多種多様にあり、なかなか全体感が一覧できません。
初学者には「全体的にどんなサービスがあるの?」というのが俯瞰的に見ることができないのか「サービスのカテゴリとサービス名と、その概要を一覧化してくれ」なんてことがあろうかと思います。
対してうるせぇググれよさっと各サービスの概要説明を一覧にして差し上げるのがクラウド時代のエンジニアの所作と思われます。知らんけど。

やってみる

cheerio-httpcliでクローリング&スクレイピングしてみました。
最初はHeadless Chromeで始めたのですが、どうやらSPAはなさそうでcheerio-httpcliでよいだろうと書き直しました。

コードは以下に配置しています。
https://github.com/morimop/aws-services-list

  • 重複あっても気にしない
  • 一応言語切り替えができるかもしれない雰囲気は出してみる

うまくいかなかったところ

サービスごとにレイアウトが統一されていないので、ここを取ってくればいい、という単純な判定はできませんでした。

index.js
            const pElements = [].filter.call(
              $('main').find('p'),
              (pn)=>{
                if($(pn).find('a').length == 0){
                  return true;
                };
                return ($(pn).find('a').text() != $(pn).text());
              });

この箇所はEMRのページのせいです。このページだけHadoopやらSparkやらのリンクが冒頭に付いているので、これを除外しています。

index.js
          return $('p').parent().text().trim();

mainが見つからなかったページはとりあえず何か取ってくる、という雑仕様です。
AWS Marketplaceのページが相当おかしな感じになります。

結果

出力は./tmp/以下にJSONで出力していますので、JSON to CSVしたりするとEXCELで見られると思われます。
sample-services.PNG
圧巻。

詳細を以下に貼り付けます。長いので前半のちょっとだけを転載します。

“category” “services__name” “services__href” “services__abstruct”
“コンピューティング” “Amazon EC2” https://aws.amazon.com/jp/ec2/?nc2=h_m1 “Amazon Elastic Compute Cloud (Amazon EC2) は、安全でサイズ変更可能なコンピューティング性能をクラウド内で提供するウェブサービスです。ウェブスケールのクラウドコンピューティングを開発者が簡単に利用できるよう設計されています。 Amazon EC2 のシンプルなウェブサービスインターフェイスによって、手間をかけず、必要な機能を取得および設定できます。お客様のコンピュートリソースに対して、高機能なコントロールが提供され、Amazon の実績あるインフラストラクチャ上で実行できます。Amazon EC2 では、わずか数分間で新規サーバーインスタンスを取得して起動できるようになります。これにより、コンピューティング要件の変化に合わせて、すばやく容量をスケールアップおよびスケールダウンできます。実際に使用した分のみ料金が発生するため、Amazon EC2 はコンピューティングの経済性も変革します。Amazon EC2 を利用すれば、耐障害性に優れたアプリケーションの構築が可能になり、よくある障害のシナリオとは無縁になります。”
“” “Amazon EC2 Container Registry” https://aws.amazon.com/jp/ecr/?nc2=h_m1 “Amazon EC2 Container Registry (ECR) は、完全マネージド型の Docker コンテナレジストリです。このレジストリを使うと、開発者は Docker コンテナイメージを簡単に保存、管理、デプロイできます。Amazon ECR は Amazon EC2 Container Service (ECS) に統合されているため、開発から本番までのワークフローを簡略化できます。Amazon ECR を使用すると、自前のコンテナリポジトリの運用や、基盤となるインフラストラクチャのスケーリングの検討は不要になります。Amazon ECR は非常に可用性が高くスケーラブルなアーキテクチャでイメージをホストするため、コンテナをアプリケーションに確実にデプロイすることが可能です。AWS Identity and Access Management (IAM) に統合すると、各リポジトリをリソースレベルで制御できます。Amazon ECR に前払い料金などの義務はありません。リポジトリに保存したデータ量とインターネットに送信されたデータ量に対してのみ料金が発生します。 AWS 無料利用枠の一環として、Amazon EC2 Container Registry を無料でお試しいただけます。Amazon ECR では、新しいお客様に、月 500 MB 分のストレージを 1 年間にわたって提供いたします。”
“” “Amazon EC2 Container Service” https://aws.amazon.com/jp/ecs/?nc2=h_m1 “Amazon EC2 Container Service (ECS) は、非常にスケーラブルかつ高性能なコンテナ管理サービスで、Docker コンテナに対応しており、Amazon EC2 インスタンスのマネージド型クラスターでアプリケーションを簡単に実行できます。Amazon ECS を使用すると、自社でクラスター管理インフラストラクチャのインストール、運用、スケールを行う必要がなくなります。簡単な API 呼び出しを使用して、Docker 対応アプリケーションの起動と終了、クラスターの完了状態のクエリ、多くの使い慣れた機能 (セキュリティグループ、Elastic Load Balancing、EBS ボリューム、IAM ロールなど) へのアクセスを実行できます。Amazon ECS を使用することで、リソースニーズと可用性要件に基づいて、クラスター全体のコンテナの配置をスケジューリングできます。ビジネスまたはアプリケーション固有の要件を満たすために、独自のスケジューラーまたはサードパーティ製のスケジューラーを統合することもできます。 Amazon EC2 Container Service に追加料金は発生しません。アプリケーションを保存および実行するために作成した AWS リソース(EC2 インスタンス、EBS ボリュームなど)に対してのみ料金が発生します。”
“” “Amazon Lightsail” https://amazonlightsail.com “Everything you need to jumpstart your project on AWS—compute, storage, and networking—for a low, predictable price. Launch a virtual private server with just a few clicks.”
“” “Amazon VPC” https://aws.amazon.com/jp/vpc/?nc2=h_m1 “Amazon Virtual Private Cloud (Amazon VPC) により、アマゾン ウェブ サービス (AWS) クラウドの論理的に分離したセクションをプロビジョニングできます。これにより、AWS リソースをユーザー定義の仮想ネットワークで起動できます。ユーザーの IP アドレス範囲の選択、サブネットの作成、ルートテーブルとネットワークゲートウェイの構成など、仮想ネットワーク環境を完全にコントロールできます。VPC では、リソースやアプリケーションに安全かつ簡単にアクセスできるよう、IPv4 と IPv6 を両方とも使用できます。 Amazon Virtual Private Cloud のネットワーク設定は容易にカスタマイズすることができます。例えば、インターネットとのアクセスが可能なウェブサーバーのパブリック サブネットを作成し、データベースやアプリケーションサーバーなどのバックエンドシステムをインターネットとのアクセスを許可していないプライベート サブネットに配置できます。セキュリティグループやネットワークアクセスコントロールリストなどの複数のセキュリティレイヤーを活用し、各サブネットの Amazon EC2 インスタンスへのアクセスをコントロールすることができます。”
“” “AWS Batch” https://aws.amazon.com/jp/batch/?nc2=h_m1 “AWS Batch を使用することにより、開発者、科学者、およびエンジニアは、数十万件のバッチコンピューティングジョブを AWS で簡単かつ効率的に実行できます。AWS Batch では、送信されたバッチジョブのボリュームと特別なリソース要件に応じて、コンピューティングリソース (CPU やメモリ最適化インスタンス) の最適な数量とタイプを動的にプロビジョニングできます。AWS Batch を使うと、ジョブを実行するためのバッチコンピューティングソフトウェアやサーバークラスターをインストールしたり、管理したりする必要がなくなります。これにより、結果の分析と問題解決に集中できます。AWS Batch では、Amazon EC2 やスポットインスタンスなどの AWS コンピューティングサービスと機能を最大限に活用して、バッチコンピューティングワークロードを計画、スケジュール作成、実行します。 AWS Batch に対する追加料金はありません。バッチジョブを保存したり実行したりするために作成した AWS リソース (EC2 インスタンスなど) に対してのみ料金が発生します。”
“” “AWS Elastic Beanstalk” https://aws.amazon.com/jp/elasticbeanstalk/?nc2=h_m1 “簡単に開始でき、不足を感じることなく利用 AWS Elastic Beanstalk は、Java、.NET、PHP、Node.js、Python、Ruby、Go および Docker を使用して開発されたウェブアプリケーションやサービスを、Apache、Nginx、Passenger、IIS などの使い慣れたサーバーでデプロイおよびスケーリングするための、使いやすいサービスです。”
“” “AWS Lambda” https://aws.amazon.com/jp/lambda/?nc2=h_m1 “AWS Lambda を使用すれば、サーバーのプロビジョニングや管理なしでコードを実行できます。課金は実際に使用したコンピューティング時間に対してのみ発生し、コードが実行されていないときには料金も発生しません。Lambda を使用すれば、実質どのようなタイプのアプリケーションやバックエンドサービスでも管理を必要とせずに実行できます。コードさえアップロードすれば、高可用性を実現しながらコードを実行およびスケーリングするために必要なことは、すべて Lambda により行われます。コードは、他の AWS サービスから自動的にトリガーするよう設定することも、ウェブやモバイルアプリケーションから直接呼び出すよう設定することもできます。 AWS Lambda とは(日本語字幕)”
“” “Auto Scaling” https://aws.amazon.com/jp/autoscaling/?nc2=h_m1 “Auto Scaling により、アプリケーションの可用性を維持できると同時に、お客様が定義する条件に応じて Amazon EC2 のキャパシティーを動的および自動的に縮小あるいは拡張できます。Auto Scaling を EC2 インスタンスのフリート管理に使用することによって、フリートの状態と可用性を維持し、必要な数の Amazon EC2 インスタンスを確実に実行できます。また、Auto Scaling を EC2 インスタンスの動的スケーリングに使用することによって、需要が急激に上昇したときには Amazon EC2 インスタンスの数を自動的に増やしてパフォーマンスを維持し、需要が落ち着いた状態にあるときには能力を縮小してコストを削減できます。Auto Scaling は需要のパターンが一定のアプリケーションにも、使用量が時間、日、週で変動するアプリケーションにも適しています。Auto Scaling を Amazon EC2 で使用する以外に、Application Auto Scaling を使用して、Amazon ECS、Amazon EC2 スポットフリート、Amazon EMR クラスター、AppStream 2.0 フリート、Amazon DynamoDB といった AWS のその他のサービスのリソースを自動的にスケールできます。 12 か月間の AWS 無料利用枠と、24 時間年中無休のカスタマーサービスやサポートフォーラムなどの AWS の基本的なサポート機能を利用できます。”
“” “Elastic Load Balancing” https://aws.amazon.com/jp/elasticloadbalancing/?nc2=h_m1 “Elastic Load Balancing は、アプリケーションへのトラフィックを複数のターゲット (Amazon EC2 インスタンス、コンテナ、IP アドレスなど) に自動的に分散します。Elastic Load Balancing は、変動するアプリケーショントラフィックの負荷を、1 つのアベイラビリティーゾーンまたは複数のアベイラビリティーゾーンで処理できます。Elastic Load Balancing では、3 種類のロードバランサーが用意されています。これらはすべて、アプリケーションの耐障害性を高めるのに必要な高い可用性、自動スケーリング、堅牢なセキュリティを特徴としています。 Application Load Balancer”
“ストレージ” “Amazon Simple Storage Service (S3)” https://aws.amazon.com/jp/s3/?nc2=h_m1 “今日の企業に求められるのは、大規模なデータを簡単かつ安全に収集、保存、分析する能力です。Amazon S3 は、ウェブサイトやモバイルアプリケーション、社内アプリケーション、IoT センサーやデバイスからのデータなど、どこからの、どのような量のデータでも保存と取得が可能なオブジェクトストレージです。これは 99.999999999% の耐久性を提供し、すべての業界のマーケットリーダーによって使用される何百万ものアプリケーションのデータを保管できるように設計されています。S3 では最も厳格なセキュリティ要件を満たす包括的なセキュリティおよびコンプライアンス機能が提供されます。お客様は柔軟にデータを管理して、コスト最適化、アクセス制御、コンプライアンスに対応できるようになります。S3 は、インプレースクエリ機能を備えた唯一のクラウドストレージソリューションであり、S3 の保管データに強力な分析を直接実行できます。また、Amazon S3 は、ISV ソリューションやシステムインテグレータのパートナーによる最大のエコシステムを備えた、最も強力なサポートが提供されているストレージプラットフォームです。 “

続きを読む

ゼロから始めるLINEBot(AWS×node.js) ②オウム返しする

前回の記事の続きです。
ゼロから始めるLINEBot(AWS×node.js) ①とりあえず動かす https://qiita.com/tenn25/items/498f768a58ba6a6de156

この記事で分かること

  • 投稿したメッセージをそのままオウム返しする
  • 自分がハマったポイント

この記事の対象者

  • いろいろと初心者(自分がそうなので)
  • MessagingAPIのような外部APIを使ったことがない
  • AWSもあんま詳しくない(アカウントは持ってるくらい)

流れ

第1回 とりあえず動かす
1.LINE Developersの登録/設定
2.実装(Lambda/node.js)
3.定期実行の設定(CloudWatch)

第2回 オウム返しの実装 (この記事)
4.ドメイン取得,DNS設定,SSL証明書の設定(Route53/ACM/SNS/S3)
5.イベントの受け取り(APIGateWay)
6.ユーザへの返信(Lambda/node.js)

第3回 Botの自動化 (予定)
7.AccessTokenの自動更新

第4回 ユーザ情報の保存 (予定)
8.DBの連携

4. ドメイン取得,DNS設定(※不要です)

LINE上でのメッセージ投稿や、botの友達追加などのイベントは、
WebHookに指定したURLにHTTPSのPOSTリクエストが送られます。

独自ドメインとSSL証明書を取って・・・などと考えましたが、不要でしたね。。
ユーザーが直接アクセスするAPIの場合は独自ドメインを割り当てた方がユーザーに優しいかもですが、
LINE側から叩かれるだけなので、APIのURLはAPIGateWay作成時のままで良いでしょう。

SSL証明書はAWSのACMで無料で取得できるのですが、
ドメインの認証の為にメールを受け取る必要があり、SESを使う必要があり少々面倒です。

流れは以下の通り
①ドメインを取得
②Route53にDNSレコードの設定
③SESでドメインの認証を行い、Route53にMX,TXTレコードを設定
④ACMのドメイン認証メールを受け取るための受信設定
⑤ACMの証明書リクエスト
⑥届いたメール内の認証用リンクにアクセスして認証

クラスメソッドさんの以下の記事を参考になります。
「[ACM] SSL証明書発行時のドメイン認証メールをSESで受け取ってみた」
https://dev.classmethod.jp/cloud/aws/acm-verifydomain-ses/

5. イベントの受け取り

LINEbotがイベントを受け取ると、
WebHookに指定したAPIにHTTPSのPOSTリクエストを送ります。
APIGateWayは指定のLambdaにリクエストを渡し、
LambdaからReplyAPIに対してHTTPSリクエストを投げることでbotの返信を実現しています。

(APIを使ったり作るのが初めてだったので、この流れを理解しておらず、
てっきりリクエストに対してそのままレスポンスを返すのかと思ってしまいました。。)

ではまず、APIGatewayの設定をする前に、
空でも良いのでLambdaの関数を作っておきましょう。
ここでは[LineBotReplyTest]を作りました。

その後に、APIGateWay上にAPIを作成して、
リソースからPOSTメソッドを以下のように定義しましょう。
(私はなぜかここをPUTで作ってしまい、WebHookが動かず謎に時間を無駄にしました。。)

APIGateWayでは、リクエストの中身をある程度加工してからバックエンドのサービス(今回はLambda)に渡すことができます。

スクリーンショット 2017-11-01 22.31.24.png

APIGateWayを初めて使うとおそらく必ずつまづくポイントですが、
リソースに作成しただけでは使えません。
必ず[APIのデプロイ]するのを忘れないようにしてください。
スクリーンショット 2017-11-01 21.13.02.png

作成したAPIのURLを確認して、LINE側の WebhookURLに設定します。
接続確認が成功すればOKです。

スクリーンショット 2017-11-01 23.07.43.png

6. ユーザへの返信

それでは先ほど作成したLambdaにコードを書きましょう。
node.jsのバージョンは6.10で動作確認済みです。

index.js

var https = require('https');

exports.handler = (event, context, callback) => {
    data = event.events[0];
    var replyToken = data.replyToken;
    var message = data.message;
    var txt = message.text;
    var data = JSON.stringify({
       replyToken: replyToken,
       messages: [
           {
               type: "text", 
               text: txt
           }
        ]
    });
    opts = {
        hostname: 'api.line.me',
        path: '/v2/bot/message/reply',
        headers: {
            "Content-type": "application/json; charset=UTF-8",
            "Content-Length": Buffer.byteLength(data),
            "Authorization": "Bearer " + process.env.CHANNEL_ACCESS_TOKEN
        },
        method: 'POST',
    };

    var req = https.request(opts, function(res) {
        res.on('data', function(res) {
            console.log(res.toString());
        }).on('error', function(e) {
            console.log('ERROR: ' + e.stack);
        });
    });
    callback(null, data);
    req.write(data);
    req.end();

};

APIGateWayから受け取ったリクエストの中身をそのままReplyAPIへ渡すことでオウム返しさせます。

①の記事でも書いてますが、
リクエストヘッダにContent-Lengthを忘れないようにしましょう。

また、process.env.CHANNEL_ACCESS_TOKEN を環境変数から取得しています。

スクリーンショット 2017-11-01 23.26.02.png

これで完成なので、LINEからメッセージを送ってみましょう

スクリーンショット 2017-11-02 00.23.51.png

成功です!

このままでは24時間でTokenが切れてしまうので、
次回は自動更新する仕組みを考えましょう。

所感

結構細かいところにつまずいてしまったのですが、
まとめてみれば、なんてことは無い内容でした。

続きを読む

Serverless FrameworkでExpressを動かす(serverless-httpを使用)

Serverless Frameworkの公式ブログで Deploy a REST API using Serverless, Express and Node.jsという記事を見つけたので、この記事で紹介されているserverless-httpとういミドルウェアを使って、Serverless上でExpressを動かしてみました。

環境

 $ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.12.6
BuildVersion:   16G29
$ node -v && npm -v
v8.8.1
5.4.2

手順

AWS CLIの事前設定とserverlessのインストール

AWS CLIをインストールし、認証情報を登録しておきます。
AWS – Credentials を参考に、使用するAWSアカウントの情報をDefaultプロファイルに設定します。

serverlessをグローバルにインストールしておきます。

$ npm install serverless -g

ココらへんは、以前書いた記事 (serverlessでLambdaのローカル開発環境を整える)でも紹介していますので、こちらを参照してください。

プロジェクトを作成

$ mkdir serverless-express-sample
$ cd serverless-express-sample
$ npm init -f

必要なパッケージのインストール

今回は、次のnpmパッケージを使用します。
* express
* serverless-http

serverless-httpはLambdaでexpressのようなhttpをルーティングするソフトウェアを使用できるようにするミドルウェアです。

$ npm i -S express serverless-http

サンプルAPIの作成

//hogeにアクセスすると、それぞれ違った文字列をjsonで返す簡単なAPIを作成してみます。

まずは、serverlessコマンド(エイリアス: sls)でnodejsベースのプロジェクトを作成します。

$ sls create --template aws-nodejs --name serverless-express-sample 

次に、追加されたhandler.jsを次のように変更します。
Expressを使ったことがあれば、見慣れた内容だと思います。

handler.js
'use strict';

const serverless = require('serverless-http');
const express = require('express');
const app = express();

app.get('/', function (req, res) {
  res.json({ message: 'Hello World!' });
});

app.get('/hoge', function (req, res) {
  res.json({ message: 'Hello Hoge!' });
});

module.exports.main = serverless(app);

serverless.ymlは次のように変更します。

eventsには、全てのpath、全てのhttpリクエストをAPI Gatewayの機能のLambda Proxy IntegrationでExpress側に渡すための設定を記述しています。

serverless.yml
service: serverless-express-sample

provider:
  name: aws
  runtime: nodejs6.10
  stage: dev
  region: ap-northeast-1

functions:
  app:
    handler: handler.main
    events:
      - http:
          method: ANY
          path: '/'
      - http:
          method: ANY
          path: '{proxy+}'

デプロイ

作成したサンプルAPIをデプロイします。

$ sls deploy

動作確認

デプロイ結果のendpoints:に表示されているエンドポイントにブラウザからアクセスしてみると…
次のように、Expressからjsonレスポンスが返ってくることが確認できるかと思います。

スクリーンショット 2017-10-29 17.26.06.png

スクリーンショット 2017-10-29 17.26.10.png

また、WappalyzerというChrome Extensionで確認してみると、Expressが動いていることが確認出ます。

スクリーンショット 2017-10-29 17.26.35.png

参考

https://serverless.com/blog/serverless-express-rest-api/

続きを読む

ゼロから始めるLINEBot(AWS×node.js) ①とりあえず動かす

この記事で分かること

  • 決まった時間に決まった文章を喋るとこまで
  • 自分がハマったポイント

この記事の対象者

  • いろいろと初心者(自分がそうなので)
  • MessagingAPIのような外部APIを使ったことがない
  • AWSもあんま詳しくない(アカウントは持ってるくらい)

流れ

第1回 とりあえず動かす (この記事)
1.LINE Developersの登録/設定
2.実装(Lambda/node.js)
3.定期実行の設定(CloudWatch)

第2回 オウム返しの実装 (予定)
4.ドメイン取得,DNS設定(Route53)
5.SSL証明書の取得(Certificate Manager/SNS/S3)
6.イベントのHook(APIGateWay)
7.ユーザへの返信(Lambda/node.js)

第3回 Botの自動化 (予定)
8.AccessTokenの自動更新

第4回 ユーザ情報の保存 (予定)
9.DBの連携

1. LINE Developersの登録/設定

以下から。
https://developers.line.me/ja/
プロバイダーを作成し、その中にChannelを作成します。
ここまでは公式の説明に沿って行けば大丈夫だと思います。

次に、デフォルトの設定を以下の画像の通りに設定します。
・アクセストークンはこの画面から再発行が可能ですが、最大24時間で切れてしまうので後々自動更新を実装します。
・WebHook URLは次回の記事で設定します。とりあえず空欄でOK
・その他の設定はキャプチャ通りに設定

スクリーンショット 2017-10-29 02.48.17.png

2. 実装(Lambda/node.js)

AWSにログインして、Lambda→関数の作成→一から作成

  • [名前]…好きな名前
  • [ロール]…[カスタムロールの作成]を選択。

IAM設定画面が開くので、以下のように設定

  • [IAMロール]…[新しいIAMロールの作成]
  • [ロール名]…好きな名前

スクリーンショット 2017-10-29 09.49.23.png

ポリシードキュメント(CloudWatchへの権限だけ付与。デフォルト設定)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    }
  ]
}

[許可]を押してIAMロールを作ったら、そのロールを指定して、Lambda関数を作成。
Lambda関数が作成されたら、関数コードに以下をコピペ。

  • ランタイムはnode.js6.10で動作確認済みです。
  • jsファイル名がindex.jsだと思うので、ハンドラ名はindex.handlerとしてください。
index.js
var https = require('https');
exports.handler = (event, context, callback) => {

    var data = JSON.stringify({
       "to": process.env.USER_ID,//CHANNEL設定画面で確認
       "messages": [
         {
           "type": "text", 
           "text": "そろそろ寝ろ!!"
         }
       ]
    });
    opts = {
        hostname: 'api.line.me',
        path: '/v2/bot/message/push',
        headers: {
            "Content-type": "application/json; charset=UTF-8",
            "Content-Length": Buffer.byteLength(data),
            "Authorization": "Bearer " + process.env.CHANNEL_ACCESS_TOKEN//CHANNEL設定画面で確認
        },
        method: 'POST',
    };

    var req = https.request(opts, function(res) {
        res.on('data', function(res) {
            console.log(res.toString());
        }).on('error', function(e) {
            console.log('ERROR: ' + e.stack);
        });
    });
    req.write(data);
    req.end();
};

LINE側のCHANNEL設定画面から、Your UserIDとアクセストークンを確認して、環境変数に設定します。

  • USER_ID…Your UserID。グループIDや他の人のID取得方法は割愛。
  • CHANNEL_ACCESS_TOKEN…アクセストークン。自動更新方法は割愛。

スクリーンショット 2017-10-29 10.34.18.png

ここで注意点。

ネットでググると似た記事が複数出てくるのですが、総じて動きませんでした。。
なんの仕様変更なのか、リクエストヘッダーへのContent-Lengthの指定が必須です。

そのため、body(ここではdataという変数)のバイト長を以下のように取得してヘッダーに含めました。
確か2016/10頃にChromeのバージョンアップでも同様のことがあって仕事で対応した気がします。。

プログラムからリクエストを送る場合は必ずContent-Lengthを付けましょう。
"Content-Length": Buffer.byteLength(data),

テスト実行をしてみると、無事LINE上でメッセージが飛んできました!

スクリーンショット 2017-10-29 10.30.03.png

lambda上は正常終了してるのに反応がない場合はcurlで確認すると良いでしょう。

curlコマンドでリクエスト投げるならこう
curl -X POST -H 'Content-Type:application/json' -H 'Authorization: Bearer [AccesToken]' -d '{  "to": "[User ID]", "messages":[{"type":"text","text":"curlコマンドから投稿"}]}' https://api.line.me/v2/bot/message/push

3.定期実行の設定(CloudWatch)

さて、僕は夜更かしなので24時なったら寝るように促してくれると嬉しいです。
CloudWatchにcronの設定をします。

※標準時で設定してくださいね。

スクリーンショット 2017-10-29 12.41.49.png

以上です。

上にも書きましたが、AccessTokenが切れると使えなくなってしまいます。
続きの記事で対応しますが、とりあえず動かすことができました。

次回は、ユーザの投稿をHookしてオウム返しする機能をつくります。

所感

  • 1年未満の記事ですら動かないものばかり。コピペプログラマの自分にはしんどい
  • ゼロから記事って、どこまでゼロから書くべきなのか悩む。

続きを読む

PythonでAWS Cognito認証

急に自分内Djangoブームが到来したので、Djangoに乗り換えはじめました。で、先日Node.jsで作ったAWSのCognito認証をPythonで作り直したので晒してみます。

事前準備

PythonでAWSにアクセスするにはIAMユーザーが必要となります。ロールにAmazonCognitoPowerUserを設定しておいてください。認証だけならロールをAmazonCognitoReadOnlyとかに落としても動くと思います。
image.png

アクセスキーも必要となりますので、認証情報タブを押してアクセスキーを作っておいてください。

つづいてAWSにPythonでアクセスするためのライブラリであるboto3を入れます

pip install boto3

AWS Cognitoで認証する

.awsに設定をしていなくても、boto3を使う時にregion等の設定を行うことができます。アプリの認証用ユーザーは固定すると思いますので、.awsを使うよりboto3を使う時に設定するほうが現実的だろうと思います。

AWS_ACCESS_KEY_IDやAWS_SECRET_ACCESS_KEY等は直接プログラムに埋め込むのは危険です(特にVCSを使ってる場合は注意)。外部ファイルに記述して読み込んで使ってください。

import boto3

def cognito_auth(user, passwd):
    # 認証開始
    try:
        aws_client = boto3.client('cognito-idp',
            region_name = "ap-northeast-1",
            aws_access_key_id = "ここにAWS_ACCESS_KEY_IDを設定します",
            aws_secret_access_key = "ここにAWS_SECRET_ACCESS_KEYを設定します",
        )

        aws_result = aws_client.admin_initiate_auth(
            UserPoolId = "ここにユーザープールIDを設定します",
            ClientId = "ここにアプリクライアントIDを設定します",
            AuthFlow = "ADMIN_NO_SRP_AUTH",
            AuthParameters = {
                "USERNAME": user,
                "PASSWORD": passwd,
            }
        )

        # 認証完了
        return aws_result

    except :
        # 認証失敗
        return None

認証が成功すれば

aws_result["AuthenticationResult"]["AccessToken"]
aws_result["AuthenticationResult"]["RefreshToken"]
aws_result["AuthenticationResult"]["IdToken"]

のようにしてトークンを拾えます。

トークンが有効かチェック

トークンはjwt形式なのでjwtライブラリで分解して……ってやろうとしたら、VC Build Tool入れて、pycrypto入れて、pyjwt入れて、winrandomの参照先変えて、decode命令呼んだら、RS256デコードしようとしてエラー吐いて死んだ/(^o^)\

どうやってもWindowsだと暗号解除部分でエラー出すので、自力で確認するの諦めて、AWSにおまかせする方向にしました。

というわけで、保有しているトークンを使ってAWSからユーザー情報を取得します。トークンが無効ならAWSがエラーを出してくれるはずです。期限切れの確認はやってないので、もしかしたら間違ってるかもしれません。

認証成功時に

# 認証成功
req.session["aws_cognito"] = {
    "user": "認証したユーザーの名前",
    "access_token": aws_result["AuthenticationResult"]["AccessToken"],
    "refresh_token": aws_result["AuthenticationResult"]["RefreshToken"]
}

のようにして、req.sessionにトークンを保存した前提でのプログラムとなります。

import boto3

def cognito_auth_check(req):
    # 認証開始
    # *********************************************************************
    try:
        # AWSクライアント認証
        aws_client = boto3.client('cognito-idp',
            region_name = "ap-northeast-1",
            aws_access_key_id = "ここにAWS_ACCESS_KEY_IDを設定します",
            aws_secret_access_key = "ここにAWS_SECRET_ACCESS_KEYを設定します",
        )

        try:
            # 認証済みトークンの有効性確認
            return aws_client.get_user(AccessToken=req.session["aws_cognito"]["access_token"])

        except:
            # 失敗。期限切れかもしれないのでREFRESH_TOKENで再認証
            try:
                aws_result = aws_client.admin_initiate_auth(
                    UserPoolId = "ここにユーザープールIDを設定します",
                    ClientId = "ここにアプリクライアントIDを設定します",
                    AuthFlow = "REFRESH_TOKEN_AUTH",
                    AuthParameters = {
                        "USERNAME": req.session["aws_cognito"]["user"],
                        "REFRESH_TOKEN": req.session["aws_cognito"]["refresh_token"]
                    }
                )
                # アクセストークン更新
                req.session["aws_cognito"]["access_token"] = aws_result["AuthenticationResult"]["AccessToken"]

                # 再度認証確認してみる
                return aws_client.get_user(AccessToken=req.session["aws_cognito"]["access_token"])

            except:
                # REFRESH_TOKENすら期限切れや、接続障害によって失敗する可能性
                return None

    except:
        # AWSへの接続失敗。IAM変えた? AWS接続障害の可能性も
        return None

成功すればユーザー情報が取得できます。

Djangoでデコレーターとして使う

デコレーターを作っておくとDjangoのlogin_requiredデコレーターのように

@login_required
def api(req):
    return HttpResponse('認証済みページの表示')

と、関数の前にポンッとデコレーターを置いたらページを認証が必要な状態にできるのが便利そうです。なのでデコレーターも作ってみました。

Djangoでのプログラムです。

from django.http import HttpResponse

# AWS Cognitoで認証済みであることを要求するデコレーター
def cognito_required(func):
    def wrap(req, *args, **kwargs):
        # 認証確認
        req.user = cognito_auth_check(req)
        if req.user != None:
            # デコレータ下の関数を実行
            return func(req, *args, **kwargs)

        # 失敗終了
        return HttpResponse('認証が必要です', status=401)

    wrap.__doc__=func.__doc__
    wrap.__name__=func.__name__
    return wrap

これを、account/aws_auth.pyとかで作っておけば、

from account.aws_auth import cognito_required

@cognito_required
def api(req):
    return HttpResponse('認証済みページの表示')

と、簡単に認証が必要なapiにすることができてステキ♡

続きを読む

nodejs aws-sdkでs3ファイルアップロード

背景

node.jsでaws-sdkを利用してファイルのアップロードしようと思ったけど古い記事が多かったのでまとめさせていただきます。
正規なやつは下記を参照…
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html

最初に

下記の項目は飛ばさせていただきます…
1. node.jsやらnpmやらのインストール
2. awsのアカウント作成
3. awsのs3のバケット作成

やり方

aws-sdkのインストール

 npm init
 npm install --save aws-sdk fs

アクセスキーの取得

AWSの画面の右上の方の「アカウント名」をクリック>「セキュリティ認証情報」を選択
スクリーンショット 2017-10-22 21.27.28.png

アクセスキーの項目で「新しいアクセスキーの作成」をクリックして作成
スクリーンショット 2017-10-22 21.27.48.png

下記のようなファイルがダウンロード出来ます。

rootkey.csv
AWSAccessKeyId=xxxxxxxxxxxxxxxxxxxx
AWSSecretKey=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

これをこんな感じで変更して拡張子をjsonに変更

rootkey.json
{"accessKeyId": "xxxxxxxxxxxxxxxxxxxx",
 "secretAccessKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}

コーディング

上記のファイルをプロジェクトファイルに移動して…

index.js
var AWS = require('aws-sdk');
var fs  = require('fs');

AWS.config.loadFromPath('./rootkey.json');
AWS.config.update({region: 'リージョン名'});

var s3 = new AWS.S3();
var params = {
 Bucket: "バケット名",
 Key: "アップロード後のファイル名.jpg"
};
var v= fs.readFileSync("./アップロード対象ファイル名.jpg");
params.Body=v;
s3.putObject(params, function(err, data) {
  if (err) console.log(err, err.stack);
  else     console.log(data);
});

続きを読む

AWS cognito使ってnode.jsで認証だけしてみる

2017/10/20のAWS-SDKのバージョンで動くもの。とりあえずテスト用に最短で。
テスト時にいちいちSignUpフォームやConfirmフォーム作るとかダルいので、とりあえずConfirmを認証時に一緒に行うようにしてみました。

1.npmでSDKをインストール

npm install aws-sdk --save
npm install amazon-cognito-identity-js --save

2.AWS cognitoでユーザー作成

AWS Cognitoのメニューから適当なユーザープールとアプリクライアント作成します。

ユーザープール ※チェックを全部外しておくと楽

image.png

アプリクライアント ※クライアントシークレットはいらない

image.png

ただしちょっと問題というか、ステータスのところがFORCE_CHANGE_PASSWORDとなっていて、ユーザー登録が完了していない状態です(Confirmが必要)
image.png

これはメールを送ってもConfirm画面に遷移できるわけでもなく、自分でConfirm画面を用意せねばなりません。テストしたいだけなので、ここを飛ばす(仮パスワードでそのままConfirmを行う)ようにプログラムします。

3.パスワードComplete付き認証プログラム

auth.js
var aws_cognito = require('amazon-cognito-identity-js');

// ユーザープール設定
var user_pool = new aws_cognito.CognitoUserPool({
    UserPoolId : 'AWS Cognitoで作ったユーザープールのプールID',
    ClientId : 'AWS Cognitoで作ったアプリクライアントID'
});

// ユーザー決定
const cognito_user = new aws_cognito.CognitoUser({
    Username: 'ユーザー名',
    Pool: user_pool,
});

// パスワードの設定
const authentication_details = new aws_cognito.AuthenticationDetails({
    Password: 'パスワード',
});

// ユーザープール/ユーザー/パスワードを使って認証
cognito_user.authenticateUser(authentication_details, {
    // 成功時
    onSuccess(result){
        // 認証完了トークンを取得。以降はこのトークンで認証済み確認
        console.log(result.getAccessToken().getJwtToken());
    },
    onFailure(err){
        console.error(err);
    },

    // ####################################################################################
    // 初回認証時はパスワードの変更が要求されるので、仮パスワードと同じパスワードを再設定する
    newPasswordRequired(user_attributes, required_attributes){
        cognito_user.completeNewPasswordChallenge(authentication_details.password, user_attributes, this);
    },
    // ####################################################################################
});

設定する時はUsername/Passwordですが、拾う時はusername/passwordと小文字になるところがちょとイヤラシイですね(´・ω・`)

おまけ

アクセストークン検証

・ライブラリを入れる

npm install jsonwebtoken --save

・ライブラリを読み込む

var jwt = require('jsonwebtoken');

・トークンを分解

var decoded = jwt.decode(access_token, {complete: true});
console.log(decoded);

検証方法はAWSのマニュアル辺りを参考に

トークン更新

・RefreshToken保存(有効期限30日)

// RefreshToken文字列を取得
refresh_token_str = result.getRefreshToken().getToken();

・Token更新

// RefreshToken文字列からRefreshTokenオブジェクトへ
refresh_token = new aws_cognito.CognitoRefreshToken({RefreshToken: "保存しておいたRefreshToken文字列"});

// 更新
cognito_user.refreshSession(refresh_token, (err, result)=>{
    // 新しいAccessTokenが取得できる
    var access_token = result.getAccessToken().getJwtToken();

    // RefreshTokenの更新とかも
    ...
});

続きを読む