Lambda関数に付与するIAMロールについて | AWS豆知識
DynamoDBへのアクセス許可
{ "Version": "2012-10-17", "Statement": [ { "Sid": "SidDynamoDB", "Effect": "Allow", "Action": [ "dynamodb:PutItem", "dynamodb:DeleteItem", "dynamodb:GetItem", "dynamodb:Scan", "dynamodb:Query", "dynamodb:UpdateItem" ], "Resource": [ DynamoDBに作成したテーブルのARN テーブルに設定したLSIやGSIのインデックスについても個別に指定する ] } ] }
S3への許可
ワイルドカードを利用している
s3:*Object → Objectに対する操作すべてということになる
{ "Version": "2012-10-17", "Statement": [ { "Sid": "SidS3ListBucket", "Effect": "Allow", "Action": [ "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::バケット名" ] }, { "Sid": "SidS3ObjectAction", "Effect": "Allow", "Action": [ "s3:*Object" ], "Resource": [ "arn:aws:s3:::バケット名/*" ] } ] }
AWS Cognitoの認証機能をAPI Gatewayの呼び出しに利用する | AWSサーバレス構築
参考にするチュートリアル
・英語版チュートリアル(新しめ)
・日本語チュートリアル(古いけど有益)
・Javascriptコードの在り処
また、事前に当ブログ記事「簡易チュートリアル」を参照のこと。
JavaScript
AWS SDK
amazon-cognito-identity.min.js と aws-cognito-sdk.min.js を、公式チュートリアルのコードから拝借する。(ventorフォルダ)
config.js
Cognitoで作成したユーザープールの「プールID」「アプリクライアントID」「リージョン」を入力する。
window._config = { cognito: { userPoolId: 'プールID', userPoolClientId: 'アプリクライアントID', region: 'リージョン' }, api: { invokeUrl: 'APIGatewayでデプロイしたステージのURL' } };
cognito-auth.js
ログインIDとパスを入力するinput、ログインボタンをhtmlページ上に用意すること。(login.html参照)
// 認証トークン管理変数。全jsファイル間で共有 var tokenManager = window.tokenManager || {}; // 起動時処理 (function cognitoAuthoMain($) { // ユーザープールの取得 var poolData = { UserPoolId: _config.cognito.userPoolId, ClientId: _config.cognito.userPoolClientId }; var userPool; if (!(_config.cognito.userPoolId && _config.cognito.userPoolClientId && _config.cognito.region)) { return; } userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData); if (typeof AWSCognito !== 'undefined') { AWSCognito.config.region = _config.cognito.region; } // === // tokenManager に 関数を設定しておく // サインアウト処理 tokenManager.signOut = function signOut() { userPool.getCurrentUser().signOut(); }; // 認証処理。「check-login-state.js」ファイルにて利用される。 tokenManager.authToken = new Promise(function fetchCurrentAuthToken(resolve, reject) { var cognitoUser = userPool.getCurrentUser(); if (cognitoUser) { cognitoUser.getSession(function sessionCallback(err, session) { if (err) { reject(err); } else if (!session.isValid()) { resolve(null); } else { resolve(session.getIdToken().getJwtToken()); } }); } else { resolve(null); } }); // === // AWS Cognito 認証処理 // ログイン処理 function loginFunc(email, password, onSuccess, onFailure) { var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails({ Username: email, Password: password }); var cognitoUser = createCognitoUser(email); cognitoUser.authenticateUser(authenticationDetails, { onSuccess: onSuccess, onFailure: onFailure }); } function createCognitoUser(email) { return new AmazonCognitoIdentity.CognitoUser({ Username: email, Pool: userPool }); } // === // ページ内処理・イベント // ページ読み込み時の処理 $(function onDocReady() { // ログインボタンにイベント追加 let aTag_login = document.getElementById("a_login"); if( aTag_login ) { aTag_login.href = "javascript:void(0);"; aTag_login.onclick = function() { handleLogin(); }; } // ログアウトボタンにイベント追加 let aTag_logout = document.getElementById("a_logout"); if( aTag_logout ) { aTag_logout.href = "javascript:void(0);"; aTag_logout.onclick = function() { handleLogout(); }; } }); // ログインボタン押下時 function handleLogin() { // フォームに入力された値を取得 var email = $('#login_input_email').val(); var password = $('#login_input_password').val(); // 入力チェック if( (email.length == 0) || (password.length == 0) ) { alert('入力されていない項目があります。ご確認ください。'); return; } // === // ログイン処理実行 // 入力された値でログイン判定、成功したならページ遷移 loginFunc( email, password, function loginSuccess() { console.log('Successfully Logged In'); // ログイン成功でトップページへ遷移 window.location.href = topPageUrl; }, function loginError(err) { console.log('Failed Logged In'); alert('ログインに失敗しました。IDとパスワードをご確認ください。'); } ); } // ログアウトボタン押下時 function handleLogout() { // ログアウト処理 tokenManager.signOut(); // アラート表示 alert('ログアウトしました。'); // ログアウトでログインページへ遷移 window.location.href = loginPageUrl; } }(jQuery));
check-login-state.js
// 認証トークン管理変数。全jsファイル間で共有 var tokenManager = window.tokenManager || {}; // 以降のjsにてAPI認証に使われるトークン。 var authToken; // 起動時処理 (function checkLoginStateMain($) { // 認証失敗時に戻すページのURL。今回はログイン画面に戻す想定。 let loginPageUrl = 'login.html'; // === tokenManager.authToken.then(function setAuthToken(token) { if (token) { authToken = token; } else { alert("ページにアクセスするにはログインを行ってください。ログイン画面に移動します。"); window.location.href = loginPageUrl; } }).catch(function handleTokenError(error) { alert(error); window.location.href = loginPageUrl; }); }(jQuery));
call-api.js
「check-login-state.js」にて取得したトークン「authToken」を、API呼び出し時に渡している。
// 認証トークン管理変数。全jsファイル間で共有 var tokenManager = window.tokenManager || {}; // 引数に、Lambdaに渡すJSONデータと、完了時の関数を渡す // completeFuncは、引数にJSONデータを受け取るので、引数を1つ渡せるような関数とする function requestApi( jsonData, completeFunc ) { $.ajax({ method: 'POST', url: _config.api.invokeUrl + '/admin', headers: { Authorization: authToken }, data: jsonData, contentType: 'application/json', success: completeFunc, error: function ajaxError(jqXHR, textStatus, errorThrown) { console.error('Error requesting: ', textStatus, ', Details: ', errorThrown); console.error('Response: ', jqXHR.responseText); alert("データベースサーバーとの通信に失敗しました。"); } }); }
main.js
call-api.js で準備したAPIコール関数を実行する。APIから応答を受けた後の処理も記述している。
(function MainFunction($) { // Lambda側に渡したいデータをJSON形式で準備 var requestJsonData = { "Key1": "Value1", "Key2": "Value2" }; // Lambda実行完了時の処理 function completeRequest( result ) { // 引数resultは受け取った時点でJSONデータとして認識できる // キーを指定してデータを得られる。複数データも配列として指定して取得すれば良い。 // console.log('Response received from API: ', result); // const responseData = JSON.parse( result ); console.log( result["ReceivedVal1"] ); console.log( result["ReceivedVal2"] ); } // ページ読み込み時の処理 $(function onDocReady() { // Lambda実行 requestApi( JSON.stringify(requestJsonData), completeRequest ); }); }(jQuery));
Html
login.html
<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <title>ログインページ サンプル</title> </head> <body> <h1>ログインページ サンプル</h1> <table> <tr><th>ID:</th><td><input id="login_input_email" class="input_text" type="text" required /></td></tr> <tr><th>パスワード:</th><td><input id="login_input_password" class="input_text" type="password" required /></td></tr> </table> <p id="btn_login"><a id="a_login">ログイン</a></p> <!-- javascript --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <!-- Cognito認証 --> <script src="html/js/vendor/aws-cognito-sdk.min.js"></script> <script src="html/js/vendor/amazon-cognito-identity.min.js"></script> <script src="html/js/config.js"></script> <script src="html/js/cognito-auth.js"></script> </body> </html>
index.html
<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <title>AWSチュートリアル簡易版テストページ</title> </head> <body> <div>Hello World</div> <!-- jquery --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <!-- 認証処理 --> <script src="js/vendor/aws-cognito-sdk.min.js"></script> <script src="js/vendor/amazon-cognito-identity.min.js"></script> <script src="js/config.js"></script> <script src="js/cognito-auth.js"></script> <script src="js/check-login-state.js"></script> <!-- API呼び出し処理 --> <script src="js/config.js"></script> <script src="js/call-api.js"></script> <script src="js/main.js"></script> </body> </html>
LambdaでDynamoDBにアクセスする記述方法(Python) | AWS豆知識
-
import
-
テーブル取得
-
書き込み
-
読み出し
-
消去
Lambda関数に付与する権限
権限を付与するためにはIAMロールを利用する。
IAMについては「IAM ロール ポリシーについて」を、
実際に付与する権限の書き方例については「Lambda関数に付与するIAMロールについて」を山参照
import
import boto3 from boto3.dynamodb.conditions import Key, Attr
テーブル取得
# dynamoDBオブジェクト self.dynamodb = boto3.resource('dynamodb') # テーブル取得 targetTable = self.dynamodb.Table('DynamoDBに存在するテーブル名')
書き込み
# 登録データ registerData = { "name": "名前", "RegisterDate": "登録日" } # 値を書き込む queryResult = targetTable.put_item( Item = registerData }
読み出し
# 全件取得 queryResult = targetTable.scan() # DeviceId が DeviceA のものを取得 # Keyに指定している「DeviceId」がパーティションキーである必要がある。 queryResult = targetTable.query( KeyConditionExpression = Key( "DeviceId" ).eq( "DeviceA" ) ) # DeviceId が DeviceA のものを取得(LSI 登録日の昇順で取得) # LSI:ローカルセカンダリインデックス # DynamoDB側で「DeviceId-RegisterDate-index」という名前でインデックスが登録されている queryResult = targetTable.query( IndexName = "DeviceId-RegisterDate-index", KeyConditionExpression = Key( "DeviceId" ).eq( "DeviceA" ) ) # 指定された顧客名の貸出情報を取得(GSI 貸出日時の昇順で取得) # GSI:グローバルセカンダリインデックス # DynamoDB側で「ClientName-RentalDate-index」という名前でインデックスが登録されている queryResult = targetTable.query( IndexName = "ClientName-RentalDate-index", KeyConditionExpression = Key( "ClientName" ).eq( clientName ) )
消去
# 消去 queryResult = targetTable.delete_item( Key = { "DeviceId": deviceId } ) # LSIやGSIで設定された2項目でデータを指定して消去 queryResult = targetTable.delete_item( Key = { "DeviceId": deviceId, "RegisterDate": registerDate } )
IAM ロール ポリシーについて | AWS豆知識
S3とか、Lambdaとか、サービスには個別に権限を割り当てられます。
例えば、Lambda関数がDynamoDBのデータにアクセスしたい場合、権限を与えていないと拒否されてしまいます。IAMで権限を作成して、それをLambdaに割り当てます。
「ポリシー」と呼ばれるものに権限を構築していきます。ポリシーの記述によってどのサービスのどのアクションを許可するか、細かく指定ができます。
「ロール」と呼ばれるものにいくつかの「ポリシー」を割り当て、その「ロール」を各種AWSサービスに割り当てることで、そのサービスに権限を与えられることができます。
JavaSciptでAWS Gatewayを呼び出す | AWSサーバレス構築 Webページ作成
ロードマップ
-
├ 静的ウェブホスティング : S3
-
├ サーバーレスバックエンド : Lambda
-
├ RESTful API : API Gateway
-
└ Webページ作成 : HTML, JavaScript ← いまココ
公式チュートリアルで参考とするファイル
公式チュートリアルで使われているソースコード
aws-serverless-webapp-workshop(GitHub)
このうち、
・config.js
・cognito-auth.js
・ride.js
を参考にする。
config.js
API GatewayのデプロイURLと、認証を利用する場合にCognitoの情報を保持する構造体データ
複数のJavaScriptファイル間でグローバル変数として振る舞う。
window._config = { cognito: { userPoolId: '', userPoolClientId: '', region: '' }, api: { invokeUrl: 'APIGatewayでデプロイしたステージのURL' } };
cognito-auth.js
APIを呼び出す時に認証を必要とさせたいときに利用する。今回の簡易版チュートリアルでは使用しない。
ride.js(call-api.js)
ajaxを利用してAPIを呼び出している。API呼び出し部分だけを抜き出して、新たにJavaScriptファイル「call-api.js」を用意する。
// 引数に、Lambdaに渡すJSONデータと、完了時の関数を渡す // completeFuncは、引数にJSONデータを受け取るので、引数を1つ渡せるような関数とする function requestApi( jsonData, completeFunc ) { $.ajax({ method: 'POST', url: _config.api.invokeUrl + '/testresource', data: jsonData, contentType: 'application/json', success: completeFunc, error: function ajaxError(jqXHR, textStatus, errorThrown) { console.error('Error requesting: ', textStatus, ', Details: ', errorThrown); console.error('Response: ', jqXHR.responseText); alert("データベースサーバーとの通信に失敗しました。"); } }); }
使い方
main.js
call-api.js で準備したAPIコール関数を実行する。APIから応答を受けた後の処理も記述している。
(function MainFunction($) { // Lambda側に渡したいデータをJSON形式で準備 var requestJsonData = { "Key1": "Value1", "Key2": "Value2" }; // Lambda実行完了時の処理 function completeRequest( result ) { // 引数resultは受け取った時点でJSONデータとして認識できる // キーを指定してデータを得られる。複数データも配列として指定して取得すれば良い。 // console.log('Response received from API: ', result); // const responseData = JSON.parse( result ); console.log( result["ReceivedVal1"] ); console.log( result["ReceivedVal2"] ); } // ページ読み込み時の処理 $(function onDocReady() { // Lambda実行 requestApi( JSON.stringify(requestJsonData), completeRequest ); }); }(jQuery));
index.html
これまでに用意したJavaScriptファイルを順番に読み込んで実行させている。
ブラウザのコンソール上にサーバーから送られている文字列が表示されればOK。
本チュートリアルの目的達成。
<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <title>AWSチュートリアル簡易版テストページ</title> </head> <body> <div>Hello World</div> <!-- javascript --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script src="js/config.js"></script> <script src="js/call-api.js"></script> <script src="js/main.js"></script> </body> </html>
AWS API GatewayでLambda関数を呼び出す | AWSサーバレス構築 RESTful API
ロードマップ
-
├ 静的ウェブホスティング : S3
-
├ サーバーレスバックエンド : Lambda
-
├ RESTful API : API Gateway ← いまココ
-
└ Webページ作成 : HTML, JavaScript
RESTful API とは
REST = 簡易な手順でWebサービスへのアクセスを可能にする仕組みのこと。
RESTful API = REST に則ったAPI
Webページ(html, javascript)とサーバー(Lambda)をつなぐ役目をしているのがAPI Gateway
API作成手順
新規作成
① 4種類あるうちの「REST API」を選択して構築する
② 「新しいAPI」を選択し、API名入力、「エッジ最適化」を選択
リソースの追加
① 「アクション」→「リソースの作成」を選択
② リソース名を入力し、API Gateway CORS を有効にする。例では(testresouceという名前)
③ 作成完了。このリソース名を指定してAPIを呼び出すことになる
メソッドの追加
① 「アクション」→「メソッドの作成」を選択
② メソッドの種類は「POST」
③ 「チェックマーク」を押して確定
④ 統合タイプ「Lambda関数」、「Lambdaプロキシ統合の使用にチェック」、「」で作成した関数を指定
⑤ 関数を呼び出すための権限を Amazon API Gateway に付与するように求められたら、[OK]を選択
デプロイ
① 「アクション」→「APIのデプロイ」を選択
② ステージ名に「prod」を入力
③ APIを呼び出すためのURLが発行される
「発行されたURL + リソース名」でAPIを呼び出せるようになり、APIを呼び出すとそれに紐ついたlambda関数を実行できる。
AWS Lambdaでサーバー側の処理を実装する | AWSサーバレス構築 サーバレスバックエンド
ロードマップ
-
├ 静的ウェブホスティング : S3
-
├ サーバーレスバックエンド : Lambda ← いまココ
-
├ RESTful API : API Gateway
-
└ Webページ作成 : HTML, JavaScript
新規作成
関数を「一から作成」を選択して作成。ランタイムはプログラムを記述する言語を選択するところで、今回はPythonを選択
実行ロールは「基本的なLambdaアクセス権限で新しいロールを作成」を選択すると、Amazon CloudWatch Logs にログをアップロードするアクセス権限を持つIAMロールとポリシーが作成されて、CloudWatchLogsというサービスも自動で登録される。Lambdaの実行ログがCloudWatchに蓄積されるようになる。
記述例
-
lambda_handler関数の引数「event」に、コール元から渡されたjsonデータが含まれる
-
通信で送る場合は文字列データにして送り、受信先でその文字列をデータ型に置き換えて使用する。
import json def lambda_handler(event, context): # 渡されたデータ受け取り event_body = json.loads( event["body"] ) receivedValue1 = event_body["Key1"] receivedValue2 = event_body["Key2"] # 返信するデータを作成(辞書型データ) returnJsonData = { "Name": "Yamada", "ReceivedVal1": receivedValue1, "ReceivedVal2": receivedValue2 } # 返信処理(bodyに辞書型データを文字列型に変換したものをセット) return { 'statusCode': 200, 'headers': { 'Access-Control-Allow-Headers': 'Content-Type', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'OPTIONS,POST,GET' }, 'body': json.dumps( returnJsonData ) }
returnにある「header」については、オリジン間リソース共有 (CORS)を参照