- 日付: 2018/11/26
Amazon Cognito の構築を AWS CloudFormation(cfn) を使って行ってみる。
- Amazon Cognito セットアップ % AWS CloudFormation + IAM
- Amazon Cognito(アイデンティティおよびデータ同期) | AWS https://aws.amazon.com/jp/cognito/
- Amazon Cognito 開発者ガイド https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/what-is-amazon-cognito.html
- AWS CloudFormation (設定管理とオーケストレーション) | AWS https://aws.amazon.com/jp/cloudformation/
- AWS CloudFormation ユーザーガイド https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/Welcome.html
「」抜粋引用
Fn::Join
組み込み関数 Fn::Join
は、一連の値を特定の区切り文字で区切って 1 つの値に追加します。区切り文字が空の文字列の場合、一連の値は区切り文字を使用することなく連結されます。
宣言(YAML)
Fn::Join: [ delimiter, [ comma-delimited list of values ] ]
パラメータ
区切り記号
値を区切る区切り文字を指定する値。区切り文字は値の間にのみ挿入されます。これは最後の値を終了しません。
ListOfValues
結合する値のリスト。
戻り値
結合された文字列。
Ref
組み込み関数 Ref は、指定したパラメーターまたはリソースの値を返します。
- パラメーターの論理名を指定すると、それはパラメーターの値を返します。
- リソースの論理名を指定すると、それはそのリソースを参照するために通常使用できる値を返します
宣言(YAML)
Ref: logicalName
パラメータ logicalName 参照解除するパラメーターまたはリソースの論理名。
戻り値 リソースの物理 ID またはパラメーターの値。
- AWS Identity and Access Management (IAM - ユーザのアクセスを安全に制御)| AWS https://aws.amazon.com/jp/iam/
- IAM とは - AWS Identity and Access Management https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/introduction.html
Cognito に関連するロールの説明
- リソースのアクセス許可 - Amazon Cognito https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/resource-permissions.html
CloudFormation に関連するロールの説明
ー AWS Identity and Access Management によるアクセスの制御 - AWS CloudFormation https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-iam-template.html
- AWS CloudFormation のベストプラクティス - AWS CloudFormation https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/best-practices.html
ほか。
-
(参考) ある程度の規模で運用するAWS CloudFormationの勘所 - Qiita https://qiita.com/datake914/items/14cb4d66570c1efcbc7d
-
CloudFormation テンプレートフォーマットは YAML を使う。可読性とコメント記述のため、JSONフォーマットではなくYAMLをつかう。
-
テンプレートを git で構成管理する。
-
IAM のベストプラクティス - AWS Identity and Access Management https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html
CloudFormation template のチェッカ
差分を確認できる。
- 新 – CloudFormation ドリフト検出 | Amazon Web Services ブログ https://aws.amazon.com/jp/blogs/news/new-cloudformation-drift-detection/
コマンドラインツール
- AWS コマンドラインインターフェイスの使用 - AWS CloudFormation https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-using-cli.html
- AWS Command Line Interface からの AWS Identity and Access Management - AWS Command Line Interface https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-iam.html
プロファイルは aws configure
(= ~/.aws/credential)の代わりに環境変数を使っている。
環境変数は direnv
で設定。
% val "$(direnv hook zsh)"
direnv: loading .envrc
direnv: export +AWS_ACCESS_KEY_ID +AWS_SECRET_ACCESS_KEY
Cognito のコマンドラインツール
describe-user-pool
% aws cognito-idp describe-user-pool --user-pool-id=ap-northeast-1_iMarCxluB
USERPOOL arn:aws:cognito-idp:ap-northeast-1:410408491513:userpool/ap-northeast-1_iMarCxluB 1543223674.289 1 ap-northeast-1_iMarCxluB 1543223674.289 OFFaws-cfn-demo
ADMINCREATEUSERCONFIG False 7
AUTOVERIFIEDATTRIBUTES email
PASSWORDPOLICY 8 True True True True
SCHEMAATTRIBUTES String False False sub True
STRINGATTRIBUTECONSTRAINTS 2048 1
SCHEMAATTRIBUTES String False True name False
STRINGATTRIBUTECONSTRAINTS 2048 0
SCHEMAATTRIBUTES String False True given_name False
STRINGATTRIBUTECONSTRAINTS 2048 0
SCHEMAATTRIBUTES String False True family_name False
STRINGATTRIBUTECONSTRAINTS 2048 0
SCHEMAATTRIBUTES String False True middle_name False
STRINGATTRIBUTECONSTRAINTS 2048 0
SCHEMAATTRIBUTES String False True nickname False
STRINGATTRIBUTECONSTRAINTS 2048 0
SCHEMAATTRIBUTES String False True preferred_username False
STRINGATTRIBUTECONSTRAINTS 2048 0
SCHEMAATTRIBUTES String False True profile False
STRINGATTRIBUTECONSTRAINTS 2048 0
SCHEMAATTRIBUTES String False True picture False
STRINGATTRIBUTECONSTRAINTS 2048 0
SCHEMAATTRIBUTES String False True website False
STRINGATTRIBUTECONSTRAINTS 2048 0
SCHEMAATTRIBUTES String False True email True
STRINGATTRIBUTECONSTRAINTS 2048 0
SCHEMAATTRIBUTES Boolean False True email_verified False
SCHEMAATTRIBUTES String False True gender False
STRINGATTRIBUTECONSTRAINTS 2048 0
SCHEMAATTRIBUTES String False True birthdate False
STRINGATTRIBUTECONSTRAINTS 10 10
SCHEMAATTRIBUTES String False True zoneinfo False
STRINGATTRIBUTECONSTRAINTS 2048 0
SCHEMAATTRIBUTES String False True locale False
STRINGATTRIBUTECONSTRAINTS 2048 0
SCHEMAATTRIBUTES String False True phone_number False
STRINGATTRIBUTECONSTRAINTS 2048 0
SCHEMAATTRIBUTES Boolean False True phone_number_verified False
SCHEMAATTRIBUTES String False True address False
STRINGATTRIBUTECONSTRAINTS 2048 0
SCHEMAATTRIBUTES Number False True updated_at False
NUMBERATTRIBUTECONSTRAINTS 0
VERIFICATIONMESSAGETEMPLATE CONFIRM_WITH_CODE
aws コマンドの標準オプション --output で json を指定すると json 形式で得られる。
例
aws cognito-idp describe-user-pool --user-pool-id ap-northeast-1_60F2FA9PW --output json | jq .UserPool.Policies
{
"PasswordPolicy": {
"MinimumLength": 8,
"RequireUppercase": true,
"RequireLowercase": true,
"RequireNumbers": true,
"RequireSymbols": false
}
}
CloudFormation のコマンドラインツール
validate-template
% aws cloudformation validate-template --template-body file://aws-cfn-cognito-template.yaml
The following resource(s) require capabilities: [AWS::IAM::ManagedPolicy] Example template including Cognito Identity Pool and User Pool.
CAPABILITIES CAPABILITY_IAM
PARAMETERS False EmailIdentityArn
- ユーザーアクティビティを表示してポリシー範囲を狭める - AWS Identity and Access Management https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_access-advisor.html
グループ、ユーザー、ロール、ポリシーについて、付与されているアクセス権限と最後にアクセスされた時間が確認できる。
- AWS CloudTrail (AWS API の呼び出し記録とログファイル送信) | AWS https://aws.amazon.com/jp/cloudtrail/
アクセス権限が無く失敗しているときの調査できる。
想定どおり動かないときは errorCode, errorMessage などから原因を追う。
- コンソールで イベントを表示する - AWS CloudTrail https://docs.aws.amazon.com/ja_jp/awscloudtrail/latest/userguide/view-cloudtrail-events-console.html
CloudFormationの Cognito リソースについて。
- AWS::Cognito::UserPool - AWS CloudFormation https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-cognito-userpool.html
{
"Type" : "AWS::Cognito::UserPool",
"Properties" : {
"AdminCreateUserConfig" : AdminCreateUserConfig,
"AliasAttributes" : [ String ],
"AutoVerifiedAttributes" : [ String ],
"DeviceConfiguration" : DeviceConfiguration,
"EmailConfiguration" : EmailConfiguration,
"EmailVerificationMessage" : String,
"EmailVerificationSubject" : String,
"LambdaConfig" : LambdaConfig,
"MfaConfiguration" : String,
"Policies" : Policies,
"Schema" : [ SchemaAttribute ],
"SmsAuthenticationMessage" : String,
"SmsConfiguration" : SmsConfiguration,
"SmsVerificationMessage" : String,
"UsernameAttributes" : [ String ],
"UserPoolName" : String,
"UserPoolTags" : { 文字列: 文字列, ... }
}
}
- AWS::Cognito::UserPoolClient - AWS CloudFormation https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-cognito-userpoolclient.html
{
"Type" : "AWS::Cognito::UserPoolClient",
"Properties" : {
"ClientName" : String,
"ExplicitAuthFlows" : [ String, ... ],
"GenerateSecret" : Boolean,
"ReadAttributes" : [ String, ... ],
"RefreshTokenValidity" : Integer,
"UserPoolId" : String,
"WriteAttributes" : [ String, ... ]
}
}
こちらのものを追跡試験した。
- CloudFormation で Cognito - Qiita https://qiita.com/y13i/items/1923b47079bdf7c44eec
AWSTemplateFormatVersion: "2010-09-09"
Description: "Example template including Cognito Identity Pool and User Pool."
Parameters:
EmailIdentityArn:
Type: String
Resources:
UserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName:
Fn::Join:
- ""
- - Ref: AWS::StackName
- Users
AdminCreateUserConfig:
AllowAdminCreateUserOnly: false
AliasAttributes:
- email
- preferred_username
AutoVerifiedAttributes:
- email
EmailConfiguration:
SourceArn:
Ref: EmailIdentityArn
Policies:
PasswordPolicy:
MinimumLength: 8
RequireLowercase: true
RequireNumbers: true
RequireSymbols: false
RequireUppercase: true
Schema:
- Name: email
AttributeDataType: String
DeveloperOnlyAttribute: false
Mutable: true
Required: true
- Name: preferred_username
AttributeDataType: String
DeveloperOnlyAttribute: false
Mutable: true
Required: false
UserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName:
Fn::Join:
- ""
- - Ref: AWS::StackName
- Users-client
GenerateSecret: false
RefreshTokenValidity: 7
UserPoolId:
Ref: UserPool
IdentityPool:
Type: AWS::Cognito::IdentityPool
Properties:
AllowUnauthenticatedIdentities: true
IdentityPoolName:
Fn::Join:
- ""
- - Ref: AWS::StackName
- Users
CognitoIdentityProviders:
- ClientId:
Ref: UserPoolClient
ProviderName:
Fn::Join:
- ""
- - cognito-idp.
- Ref: "AWS::Region"
- .amazonaws.com/
- Ref: UserPool
UnauthenticatedPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- mobileanalytics:PutEvents
- cognito-sync:*
Resource:
- "*"
UnauthenticatedRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: "sts:AssumeRoleWithWebIdentity"
Principal:
Federated: cognito-identity.amazonaws.com
Condition:
StringEquals:
"cognito-identity.amazonaws.com:aud":
Ref: IdentityPool
ForAnyValue:StringLike:
"cognito-identity.amazonaws.com:amr": unauthenticated
ManagedPolicyArns:
- Ref: UnauthenticatedPolicy
AuthenticatedPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- mobileanalytics:PutEvents
- cognito-sync:*
- cognito-identity:*
Resource:
- "*"
AuthenticatedRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: "sts:AssumeRoleWithWebIdentity"
Principal:
Federated: cognito-identity.amazonaws.com
Condition:
StringEquals:
"cognito-identity.amazonaws.com:aud":
Ref: IdentityPool
ForAnyValue:StringLike:
"cognito-identity.amazonaws.com:amr": authenticated
ManagedPolicyArns:
- Ref: AuthenticatedPolicy
RoleAttachment:
Type: AWS::Cognito::IdentityPoolRoleAttachment
Properties:
IdentityPoolId:
Ref: IdentityPool
Roles:
unauthenticated:
Fn::GetAtt:
- UnauthenticatedRole
- Arn
authenticated:
Fn::GetAtt:
- AuthenticatedRole
- Arn
Outputs:
UserPool:
Value:
Ref: UserPool
UserPoolClient:
Value:
Ref: UserPoolClient
IdentityPool:
Value:
Ref: IdentityPool
UnauthenticatedRole:
Value:
Ref: UnauthenticatedRole
AuthenticatedRole:
Value:
Ref: AuthenticatedRole
結果、うまくいかなかった。
- Parameter の EmailIdentityArn の内容が把握できなかった。
- 作成時の Cognito の IdentityPool のエラーが修正できなかった
- 作成時の IAM の UnAuthenticatedRole のエラーが修正できなかった。
前のもの変更して再度実行。 UserPool と UserPoolClient のみに変更した。
実行できたものはこちら。
AWSTemplateFormatVersion: "2010-09-09"
Description: "Example template including Cognito Identity Pool and User Pool."
Resources:
UserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName:
Fn::Join:
- ""
- - Ref: AWS::StackName
- Users
AdminCreateUserConfig:
AllowAdminCreateUserOnly: false
AliasAttributes:
- email
- preferred_username
AutoVerifiedAttributes:
- email
Policies:
PasswordPolicy:
MinimumLength: 8
RequireLowercase: true
RequireNumbers: true
RequireSymbols: false
RequireUppercase: true
Schema:
- Name: email
AttributeDataType: String
DeveloperOnlyAttribute: false
Mutable: true
Required: true
- Name: preferred_username
AttributeDataType: String
DeveloperOnlyAttribute: false
Mutable: true
Required: false
UserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName:
Fn::Join:
- ""
- - Ref: AWS::StackName
- Users-client
GenerateSecret: false
RefreshTokenValidity: 7
UserPoolId:
Ref: UserPool
Outputs:
UserPool:
Value:
Ref: UserPool
UserPoolClient:
Value:
Ref: UserPoolClient
上記の template を実行した結果 CloudFormation に cfn-cognito-template-06
stack を作成した。
登録後にtemplateタブから template の言語をJSON にしたものはこちら。
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Example template including Cognito Identity Pool and User Pool.",
"Resources": {
"UserPool": {
"Type": "AWS::Cognito::UserPool",
"Properties": {
"UserPoolName": {
"Fn::Join": [
"",
[
{
"Ref": "AWS::StackName"
},
"Users"
]
]
},
"AdminCreateUserConfig": {
"AllowAdminCreateUserOnly": false
},
"AliasAttributes": [
"email",
"preferred_username"
],
"AutoVerifiedAttributes": [
"email"
],
"Policies": {
"PasswordPolicy": {
"MinimumLength": 8,
"RequireLowercase": true,
"RequireNumbers": true,
"RequireSymbols": false,
"RequireUppercase": true
}
},
"Schema": [
{
"Name": "email",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": true
},
{
"Name": "preferred_username",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false
}
]
}
},
"UserPoolClient": {
"Type": "AWS::Cognito::UserPoolClient",
"Properties": {
"ClientName": {
"Fn::Join": [
"",
[
{
"Ref": "AWS::StackName"
},
"Users-client"
]
]
},
"GenerateSecret": false,
"RefreshTokenValidity": 7,
"UserPoolId": {
"Ref": "UserPool"
}
}
}
},
"Outputs": {
"UserPool": {
"Value": {
"Ref": "UserPool"
}
},
"UserPoolClient": {
"Value": {
"Ref": "UserPoolClient"
}
}
}
}
その結果、Cognito に cfn-cognito-template-06Users
という UserPool が作成された。
Cognito に CloudFormation で作成した UserPool stack cfn-cognito-template-06Users
の内容確認。
% aws cognito-idp describe-user-pool --user-pool-id ap-northeast-1_60F2FA9PW --output json
{
"UserPool": {
"Id": "ap-northeast-1_60F2FA9PW",
"Name": "cfn-cognito-template-06Users",
"Policies": {
"PasswordPolicy": {
"MinimumLength": 8,
"RequireUppercase": true,
"RequireLowercase": true,
"RequireNumbers": true,
"RequireSymbols": false
}
},
"LambdaConfig": {},
"LastModifiedDate": 1543314244.84,
"CreationDate": 1543314244.84,
"SchemaAttributes": [
{
"Name": "sub",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": false,
"Required": true,
"StringAttributeConstraints": {
"MinLength": "1",
"MaxLength": "2048"
}
},
{
"Name": "name",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "given_name",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "family_name",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "middle_name",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "nickname",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "preferred_username",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "profile",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "picture",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "website",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "email",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": true,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "email_verified",
"AttributeDataType": "Boolean",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false
},
{
"Name": "gender",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "birthdate",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "10",
"MaxLength": "10"
}
},
{
"Name": "zoneinfo",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "locale",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "phone_number",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "phone_number_verified",
"AttributeDataType": "Boolean",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false
},
{
"Name": "address",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"StringAttributeConstraints": {
"MinLength": "0",
"MaxLength": "2048"
}
},
{
"Name": "updated_at",
"AttributeDataType": "Number",
"DeveloperOnlyAttribute": false,
"Mutable": true,
"Required": false,
"NumberAttributeConstraints": {
"MinValue": "0"
}
}
],
"AutoVerifiedAttributes": [
"email"
],
"AliasAttributes": [
"email",
"preferred_username"
],
"VerificationMessageTemplate": {
"DefaultEmailOption": "CONFIRM_WITH_CODE"
},
"MfaConfiguration": "OFF",
"EstimatedNumberOfUsers": 0,
"EmailConfiguration": {},
"UserPoolTags": {},
"AdminCreateUserConfig": {
"AllowAdminCreateUserOnly": false,
"UnusedAccountValidityDays": 7
},
"Arn": "arn:aws:cognito-idp:ap-northeast-1:410408491513:userpool/ap-northeast-1_60F2FA9PW"
}
}
- 日本語解説 https://aws.amazon.com/jp/getting-started/serverless-web-app/
- ソースコード https://github.com/aws-samples/aws-serverless-workshops
作成手順が5つのモジュールに分かれている。 モジュール1と2はCloudFormation templateがある。
CloudFormationの実行は 1, 2 ともに失敗した。 ワークショップの手順どおりに実行したところ、一部修正があったが完了できた。
作成したサイトの URL はこちら。
提供されている CloudFormation template を引用。
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Creates a static website using S3 for the Wild Rydes serverless web application workshop",
"Parameters": {
"BucketName": {
"Type": "String",
"Description": "The name for the bucket hosting your website, e.g. 'wildrydes-yourname'"
},
"CodeBucket": {
"Type": "String",
"Default": "wildrydes-ap-northeast-1",
"Description": "S3 bucket containing the code deployed by this template"
},
"CodeKeyPrefix": {
"Type": "String",
"Default": "WebApplication/1_StaticWebHosting",
"Description": "Key prefix for resources referenced from the CodeBucket"
}
},
"Metadata": {
"AWS::CloudFormation::Interface": {
"ParameterGroups": [
{
"Label": {
"default": "Website Configuration"
},
"Parameters": [
"BucketName"
]
},
{
"Label": {
"default": "Advanced Configuration"
},
"Parameters": [
"CodeBucket",
"CodeKeyPrefix"
]
}
],
"ParameterLabels": {
"BucketName": {
"default": "Website Bucket Name"
}
}
}
},
"Resources": {
"WebsiteBucket": {
"Properties": {
"BucketName": {
"Ref": "BucketName"
},
"WebsiteConfiguration": {
"IndexDocument": "index.html"
}
},
"Type": "AWS::S3::Bucket"
},
"WebsiteBucketPolicy": {
"Properties": {
"Bucket": {
"Ref": "WebsiteBucket"
},
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": {
"Fn::Sub": "arn:aws:s3:::${WebsiteBucket}/*"
}
}
]
}
},
"Type": "AWS::S3::BucketPolicy"
},
"WebsiteContent": {
"Properties": {
"ServiceToken": {
"Fn::GetAtt": [
"CopyS3ObjectsFunction",
"Arn"
]
},
"SourceBucket": {
"Ref": "CodeBucket"
},
"SourcePrefix": {
"Fn::Sub": "${CodeKeyPrefix}/website/"
},
"Bucket": {
"Ref": "WebsiteBucket"
}
},
"Type": "Custom::S3Objects"
},
"S3CopyRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"Path": "/wildrydes/",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"Policies": [
{
"PolicyName": "S3Access",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowLogging",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
},
{
"Sid": "SourceBucketReadAccess",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetObject"
],
"Resource": [
{
"Fn::Sub": "arn:aws:s3:::${CodeBucket}"
},
{
"Fn::Sub": "arn:aws:s3:::${CodeBucket}/${CodeKeyPrefix}/*"
}
]
},
{
"Sid": "DestBucketWriteAccess",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetObject",
"s3:PutObject",
"s3:PutObjectAcl",
"s3:PutObjectVersionAcl",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:CopyObject"
],
"Resource": [
{
"Fn::Sub": "arn:aws:s3:::${WebsiteBucket}"
},
{
"Fn::Sub": "arn:aws:s3:::${WebsiteBucket}/*"
}
]
}
]
}
}
]
}
},
"CopyS3ObjectsFunction": {
"Properties": {
"Description": "Copies objects from a source S3 bucket to a destination",
"Handler": "index.handler",
"Runtime": "python2.7",
"Role": {
"Fn::GetAtt": [
"S3CopyRole",
"Arn"
]
},
"Timeout": 120,
"Code": {
"ZipFile": "import os\nimport json\nimport cfnresponse\n\nimport boto3\nfrom botocore.exceptions import ClientError\nclient = boto3.client('s3')\n\nimport logging\nlogger = logging.getLogger()\nlogger.setLevel(logging.INFO)\n\ndef handler(event, context):\n logger.info(\"Received event: %s\" % json.dumps(event))\n source_bucket = event['ResourceProperties']['SourceBucket']\n source_prefix = event['ResourceProperties'].get('SourcePrefix') or ''\n bucket = event['ResourceProperties']['Bucket']\n prefix = event['ResourceProperties'].get('Prefix') or ''\n\n result = cfnresponse.SUCCESS\n\n try:\n if event['RequestType'] == 'Create' or event['RequestType'] == 'Update':\n result = copy_objects(source_bucket, source_prefix, bucket, prefix)\n elif event['RequestType'] == 'Delete':\n result = delete_objects(bucket, prefix)\n except ClientError as e:\n logger.error('Error: %s', e)\n result = cfnresponse.FAILED\n\n cfnresponse.send(event, context, result, {})\n\n\ndef copy_objects(source_bucket, source_prefix, bucket, prefix):\n paginator = client.get_paginator('list_objects_v2')\n page_iterator = paginator.paginate(Bucket=source_bucket, Prefix=source_prefix)\n for key in {x['Key'] for page in page_iterator for x in page['Contents']}:\n dest_key = os.path.join(prefix, os.path.relpath(key, source_prefix))\n if not key.endswith('/'):\n print 'copy {} to {}'.format(key, dest_key)\n client.copy_object(CopySource={'Bucket': source_bucket, 'Key': key}, Bucket=bucket, Key = dest_key)\n return cfnresponse.SUCCESS\n\ndef delete_objects(bucket, prefix):\n paginator = client.get_paginator('list_objects_v2')\n page_iterator = paginator.paginate(Bucket=bucket, Prefix=prefix)\n objects = [{'Key': x['Key']} for page in page_iterator for x in page['Contents']]\n client.delete_objects(Bucket=bucket, Delete={'Objects': objects})\n return cfnresponse.SUCCESS\n"
}
},
"Type": "AWS::Lambda::Function"
}
},
"Outputs": {
"WebsiteURL": {
"Value": {
"Fn::GetAtt": [
"WebsiteBucket",
"WebsiteURL"
]
}
}
}
}
こちらの実行を試したところ失敗した。以下のイベント出力が出ていた。
以下のドキュメントを元に原因調査。
- AWS CloudFormation のトラブルシューティング - AWS CloudFormation https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/troubleshooting.html
case 1.
WebsiteContent
CREATE_FAILED
Failed to create resource. See the details in CloudWatch Log Stream: 2018/11/29/[$LATEST]c64242fcc9374bfeb18b5c88c170f25a
これは CloudFormation が対応していないリソースの生成をインラインで書いた Lambda Function でやらせる箇所でエラーがでている。
case2.
Custom Resource failed to stabilize in expected time
リソースの WebsiteContent のログにとのメッセージがあった。 こちらは先のCREATE_FAILDのイベントの「更新のロールバックの失敗」に該当する箇所になる。
引用する。
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Creates a Cognito User Pool for the Wild Rydes serverless web application workshop",
"Parameters": {
"WebsiteBucket": {
"Type": "String",
"Description": "The name for the bucket hosting your website, e.g. 'wildrydes-yourname.'"
}
},
"Metadata": {
"AWS::CloudFormation::Interface": {
"ParameterGroups": [
{
"Label": {
"default": "Module 1 Details"
},
"Parameters": [
"WebsiteBucket"
]
}
],
"ParameterLabels": {
"WebsiteBucket": {
"default": "Website Bucket Name"
}
}
}
},
"Resources": {
"UserPool": {
"Type": "AWS::Cognito::UserPool",
"Properties": {
"UserPoolName": "WildRydes",
"AliasAttributes": [
"email"
],
"AutoVerifiedAttributes": [
"email"
]
}
},
"UserPoolClient": {
"Type": "AWS::Cognito::UserPoolClient",
"Properties": {
"ClientName": "WildRydesWeb",
"UserPoolId": {
"Ref": "UserPool"
},
"GenerateSecret": false
}
},
"UpdateConfig": {
"Properties": {
"ServiceToken": {
"Fn::GetAtt": [
"UpdateConfigFunction",
"Arn"
]
},
"UserPool": {
"Ref": "UserPool"
},
"Client": {
"Ref": "UserPoolClient"
},
"Region": {
"Ref": "AWS::Region"
},
"Bucket": {
"Ref": "WebsiteBucket"
}
},
"Type": "Custom::ConfigFile"
},
"CognitoConfigRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"Path": "/wildrydes/",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"Policies": [
{
"PolicyName": "CognitoConfig",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Logging",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
},
{
"Sid": "Cognito",
"Effect": "Allow",
"Action": [
"cognito-idp:CreateUserPool",
"cognito-idp:DeleteUserPool",
"cognito-idp:CreateUserPoolClient",
"cognito-idp:DeleteUserPoolClient"
],
"Resource": "*"
},
{
"Sid": "ConfigBucketWriteAccess",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl",
"s3:PutObjectVersionAcl"
],
"Resource": [
{
"Fn::Sub": "arn:aws:s3:::${WebsiteBucket}/*"
}
]
}
]
}
}
]
}
},
"UpdateConfigFunction": {
"Properties": {
"Description": "Copies objects from a source S3 bucket to a destination",
"Handler": "index.handler",
"Runtime": "python2.7",
"Role": {
"Fn::GetAtt": [
"CognitoConfigRole",
"Arn"
]
},
"Timeout": 120,
"Code": {
"ZipFile": "import json\nimport boto3\nimport cfnresponse\n\ns3 = boto3.resource('s3')\n\ndef create(properties, physical_id):\n userPoolId = properties['UserPool']\n clientId = properties['Client']\n region = properties['Region']\n bucket = properties['Bucket']\n\n object = s3.Object(bucket, 'js/config.js')\n config_content = \"\"\"\nvar _config = {\n cognito: {\n userPoolId: '%s', // e.g. us-east-2_uXboG5pAb\n userPoolClientId: '%s', // e.g. 25ddkmj4v6hfsfvruhpfi7n4hv\n region: '%s', // e.g. us-east-2\n },\n api: {\n invokeUrl: 'Base URL of your API including the stage', // e.g. https://rc7nyt4tql.execute-api.us-west-2.amazonaws.com/prod'\n }\n};\n \"\"\"\n config_content = config_content % (userPoolId, clientId, region)\n config = s3.Object(bucket,'js/config.js')\n config.put(Body=config_content)\n return cfnresponse.SUCCESS, None\n\ndef update(properties, physical_id):\n return create(properties, physical_id)\n\ndef delete(properties, physical_id):\n return cfnresponse.SUCCESS, physical_id\n\ndef handler(event, context):\n print \"Received event: %s\" % json.dumps(event)\n\n status = cfnresponse.FAILED\n new_physical_id = None\n\n try:\n properties = event.get('ResourceProperties')\n physical_id = event.get('PhysicalResourceId')\n\n status, new_physical_id = {\n 'Create': create,\n 'Update': update,\n 'Delete': delete\n }.get(event['RequestType'], lambda x, y: (cfnresponse.FAILED, None))(properties, physical_id)\n except Exception as e:\n print \"Exception: %s\" % e\n status = cfnresponse.FAILED\n finally:\n cfnresponse.send(event, context, status, {}, new_physical_id)\n"
}
},
"Type": "AWS::Lambda::Function"
}
}
}
実行したところ失敗した。
UpdateConfig
CREATE_FAILED Failed to create resource. See the details in CloudWatch Log Stream: 2018/11/29/[$LATEST]33b8db152ea446ad8183e23523916a2f
こちら LambdaFunction の実行でエラーが出たために失敗となっている。 UpdateConfig を読むと、UpdateConfigFunction を呼びだしており、内容は S3 に展開済みの js/config.js の読み込みで失敗していると思われる。
ドキュメントの各モジュールを手動で作成した。
モジュール 3 の作成で、権限エラーがでた。
– AWS Lambda および Amazon DynamoDBによるサーバーレスバックエンド https://aws.amazon.com/jp/getting-started/serverless-web-app/module-3/
DynamoDB への書き込みを作成したテーブルのみに制限する指定が期待した動作をしなかった。 対応として、全テーブルへのアクセス権を付与した。
こちらを参照した。
- ユーザープールへのソーシャル ID プロバイダーの追加 - Amazon Cognito https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pools-social-idp.html
ドキュメント内容に従って Facebook デベロッパーを登録し、アプリを作成してIDを取得した。
ユーザプール側にIDプロバイダとクライアントアプリの登録を行った。
認証設定の「アプリクライアント設定」でコールバックURLがhttp のURLが不許可になっていた。 これに対応するため CloudFront で SSL Offloading を行う設定を追加した。
http://wildrydes-userpool-demo-02.s3-website-ap-northeast-1.amazonaws.com/index.html -> https://wildrydes-userpool-demo-02.s3.amazonaws.com/index.html
以下のURLでサードパーティ認証の画面が出るとの記述だったが、エラーしか得られていない。
- 組み込みのサインアップおよびサインイン Web ページの使用 - Amazon Cognito https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pools-app-integration.html
前の 2 つのセクションの要素を使用してログイン URL を作成できます。この URL を使用してソーシャルIdP の設定をテストします。 https://your_user_pool_domain/login? response_type=code&client_id=your_client_id&redirect_uri=https://www.example.com ドメインは、コンソールにあるユーザープールの [ドメイン名] ページで確認できます。クライアント IDは [アプリクライアントの設定] ページにあります。redirect_uri パラメータのコールバック URL を使用します。コールバック URL アドレスは、ユーザープールのドメインとは異なります。
- Getting Started - Amplify Javascript https://aws-amplify.github.io/docs/js/start?platform=purejs
こちらを使用して Cognito UserPool と SNS Login の両対応を試みる。
- 参考) AWSの次世代JavaScriptライブラリ「AWS Amplify」の概要とReactアプリに導入する手順 #serverless #adventcalendar | DevelopersIO https://dev.classmethod.jp/server-side/serverless/aws-amplify-getting-started/
各参考資料をもとに create-react-app
の出力コードにCognito に設定済みの各IDを指定し、手を加えた。
現状のソースコードは以下のとおり。
import React, { Component } from 'react';
import Amplify from 'aws-amplify';
import { Authenticator } from 'aws-amplify-react';
import { withAuthenticator } from 'aws-amplify-react';
Amplify.configure({
Auth: {
identityPoolId: 'ap-northeast-1:736edc8f-4c51-4795-8548-4582105fd945', // FederatedID連携後に追加する
region: 'ap-northeast-1', // REQUIRED - Amazon Cognito Region
userPoolId: 'ap-northeast-1_wl3scNdwp', //OPTIONAL - Amazon Cognito User Pool ID
userPoolWebClientId: '7rubtdlc3c0n2fmv6i0f714335', //OPTIONAL - Amazon Cognito Web Client ID
}
});
const federated = {
//google_client_id: '', // Enter your google_client_id here
facebook_app_id: '216273885918169', // Enter your facebook_app_id here
//amazon_client_id: '' // Enter your amazon_client_id here
};
class App extends Component {
}
export default withAuthenticator(App, true, <Authenticator federated={federated}/>, federated);
こちらで Facebook ログインのアイコンが表示されたが、ログイン用のポップアップダイアログは表示されない。 ブラウザの Developper Tools では、以下のメッセージが出ている。
The resource at “https://connect.facebook.net/en_US/sdk.js” was blocked because content blocking is enabled.[Learn More] localhost:3000 Loading failed for the <script> with source “https://connect.facebook.net/en_US/sdk.js”. localhost:3000:1:1 TypeError: fb is undefined
解決していない。
ダイアログの表示はできた。 正常なログインフォーム入力後のリダイレクトができていない。
- Facebook Login with Cognito using AWS Amplify | Serverless Stack https://serverless-stack.com/chapters/facebook-login-with-cognito-using-aws-amplify.html
類似の実装を行っているコードを実行してみた。
こちらは Facebook ログインのダイアログは表示される。しかし、以下のメッセージが出てログインはできなかった。
Warning Can't Load URL: The domain of this URL isn't included in the app's domains. To be able to load this URL, add all domains and subdomains of your app to the App Domains field in your app settings.
AWS Well-Architected フレームワークにしたがって、本番環境とは別に開発とステージングは必要時に構築することを想定する。
-
- 現在の Cognito ユーザプールの設定と動作確認
-
- 作業用のユーザプールを CloudFormation テンプレートで設定し動作確認
-
- 手動でユーザプールの設定変更
-
- CloudFormation で変更の drift 確認
-
- drift を元にCloudFormation テンプレートを更新
-
- 更新した CloudFormation テンプレートで設定と動作確認
-
- 本番の Cognito ユーザプールに更新を適用
構成案
- 大きく開発グループと,本番サービス運用グループを別アカウントに分ける
- AWSサポートのプランを変えるため
- 構成するクラウドは開発と、本番サービス & ステージングのあわせて 3 つ
- ステージングは、本番サービスの CloudFormation のスタックをクローン、接続先データベース等をパラメータ入力で変更
- 本番サービスでは IAM で Cognito UserPool の管理者と、クライアントアプリの管理者(ウェブマスター)を分ける
- UserPool に利用者が登録する情報は個人情報なので。
注意点
- drift を確認するには、登録している CloudFormation テンプレートに空値でも項目が必要。新しい追跡項目をテンプレートに書いて更新を行う。
本番サービスの運用に対して考慮する点。
-
20180313 AWS Black Belt Online Seminar AWS Well-Architected Framework… https://www.slideshare.net/AmazonWebServicesJapan/20180313-aws-black-belt-online-seminar-wellarchitected-framework-97366614
-
本番環境用アカウントでは「Trusted Advisor(後述)」も利用できるビジネス プラン以上を推奨
-
プラン:デベロッパー は開発環境用におすすめ
サーバーレスアプリケーション開発者用ツール https://aws.amazon.com/jp/serverless/developer-tools/
- Chalice
- Zappa
- GitHub - aws/chalice: Python Serverless Microframework for AWS https://github.com/aws/chalice/
% chalice deploy
Creating deployment package.
Creating IAM role: aws-cognito-chalice-dev
Creating lambda function: aws-cognito-chalice-dev
Creating Rest API
Resources deployed:
- Lambda ARN: arn:aws:lambda:ap-northeast-1:410408491513:function:aws-cognito-chalice-dev
- Rest API URL: https://7rb15x8ite.execute-api.ap-northeast-1.amazonaws.com/api/
CloudFormation template 出力。
CodeBuild, CodePipeline, S3, IAM を使っている。 https://aws.amazon.com/jp/codebuild/
% chalice generate-pipeline "`basename $PWD`"_"`date +%Y%m%d-%H%M%S`".json
% ls -la *.json
-rw-r--r-- 1 nicdk staff 11954 Dec 10 15:56 aws-cognito-chalice_20181210-155646.json
% cat aws-cognito-chalice_20181210-155646.json
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"ApplicationName": {
"Default": "aws-cognito-chalice",
"Type": "String",
"Description": "Enter the name of your application"
},
"CodeBuildImage": {
"Default": "aws/codebuild/python:3.6.5",
"Type": "String",
"Description": "Name of codebuild image to use."
}
},
"Resources": {
"SourceRepository": {
"Type": "AWS::CodeCommit::Repository",
"Properties": {
"RepositoryName": {
"Ref": "ApplicationName"
},
"RepositoryDescription": {
"Fn::Sub": "Source code for ${ApplicationName}"
}
}
},
"ApplicationBucket": {
"Type": "AWS::S3::Bucket"
},
"CodeBuildRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"sts:AssumeRole"
],
"Effect": "Allow",
"Principal": {
"Service": [
"codebuild.amazonaws.com"
]
}
}
]
}
}
},
"CodeBuildPolicy": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "CodeBuildPolicy",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::*",
"Effect": "Allow"
}
]
},
"Roles": [
{
"Ref": "CodeBuildRole"
}
]
}
},
"AppPackageBuild": {
"Type": "AWS::CodeBuild::Project",
"Properties": {
"Artifacts": {
"Type": "CODEPIPELINE"
},
"Environment": {
"ComputeType": "BUILD_GENERAL1_SMALL",
"Image": {
"Ref": "CodeBuildImage"
},
"Type": "LINUX_CONTAINER",
"EnvironmentVariables": [
{
"Name": "APP_S3_BUCKET",
"Value": {
"Ref": "ApplicationBucket"
}
}
]
},
"Name": {
"Fn::Sub": "${ApplicationName}Build"
},
"ServiceRole": {
"Fn::GetAtt": "CodeBuildRole.Arn"
},
"Source": {
"Type": "CODEPIPELINE",
"BuildSpec": "version: 0.1\nphases:\n install:\n commands:\n - sudo pip install --upgrade awscli\n - aws --version\n - sudo pip install 'chalice>=1.6.0,<1.7.0'\n - sudo pip install -r requirements.txt\n - chalice package /tmp/packaged\n - aws cloudformation package --template-file /tmp/packaged/sam.json --s3-bucket ${APP_S3_BUCKET} --output-template-file transformed.yaml\nartifacts:\n type: zip\n files:\n - transformed.yaml\n"
}
}
},
"AppPipeline": {
"Type": "AWS::CodePipeline::Pipeline",
"Properties": {
"Name": {
"Fn::Sub": "${ApplicationName}Pipeline"
},
"ArtifactStore": {
"Type": "S3",
"Location": {
"Ref": "ArtifactBucketStore"
}
},
"RoleArn": {
"Fn::GetAtt": "CodePipelineRole.Arn"
},
"Stages": [
{
"Name": "Source",
"Actions": [
{
"ActionTypeId": {
"Category": "Source",
"Owner": "AWS",
"Version": 1,
"Provider": "CodeCommit"
},
"Configuration": {
"BranchName": "master",
"RepositoryName": {
"Fn::GetAtt": "SourceRepository.Name"
}
},
"OutputArtifacts": [
{
"Name": "SourceRepo"
}
],
"RunOrder": 1,
"Name": "Source"
}
]
},
{
"Name": "Build",
"Actions": [
{
"InputArtifacts": [
{
"Name": "SourceRepo"
}
],
"Name": "CodeBuild",
"ActionTypeId": {
"Category": "Build",
"Owner": "AWS",
"Version": 1,
"Provider": "CodeBuild"
},
"OutputArtifacts": [
{
"Name": "CompiledCFNTemplate"
}
],
"Configuration": {
"ProjectName": {
"Ref": "AppPackageBuild"
}
},
"RunOrder": 1
}
]
},
{
"Name": "Beta",
"Actions": [
{
"ActionTypeId": {
"Category": "Deploy",
"Owner": "AWS",
"Version": 1,
"Provider": "CloudFormation"
},
"InputArtifacts": [
{
"Name": "CompiledCFNTemplate"
}
],
"Name": "CreateBetaChangeSet",
"Configuration": {
"ActionMode": "CHANGE_SET_REPLACE",
"ChangeSetName": {
"Fn::Sub": "${ApplicationName}ChangeSet"
},
"RoleArn": {
"Fn::GetAtt": "CFNDeployRole.Arn"
},
"Capabilities": "CAPABILITY_IAM",
"StackName": {
"Fn::Sub": "${ApplicationName}BetaStack"
},
"TemplatePath": "CompiledCFNTemplate::transformed.yaml"
},
"RunOrder": 1
},
{
"RunOrder": 2,
"ActionTypeId": {
"Category": "Deploy",
"Owner": "AWS",
"Version": 1,
"Provider": "CloudFormation"
},
"Configuration": {
"StackName": {
"Fn::Sub": "${ApplicationName}BetaStack"
},
"ActionMode": "CHANGE_SET_EXECUTE",
"ChangeSetName": {
"Fn::Sub": "${ApplicationName}ChangeSet"
},
"OutputFileName": "StackOutputs.json"
},
"Name": "ExecuteChangeSet",
"OutputArtifacts": [
{
"Name": "AppDeploymentValues"
}
]
}
]
}
]
}
},
"ArtifactBucketStore": {
"Type": "AWS::S3::Bucket",
"Properties": {
"VersioningConfiguration": {
"Status": "Enabled"
}
}
},
"CodePipelineRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"Policies": [
{
"PolicyName": "DefaultPolicy",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:GetBucketVersioning",
"s3:CreateBucket",
"s3:PutObject",
"s3:PutBucketVersioning"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"codecommit:CancelUploadArchive",
"codecommit:GetBranch",
"codecommit:GetCommit",
"codecommit:GetUploadArchiveStatus",
"codecommit:UploadArchive"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"cloudwatch:*",
"iam:PassRole"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"lambda:InvokeFunction",
"lambda:ListFunctions"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"cloudformation:CreateStack",
"cloudformation:DeleteStack",
"cloudformation:DescribeStacks",
"cloudformation:UpdateStack",
"cloudformation:CreateChangeSet",
"cloudformation:DeleteChangeSet",
"cloudformation:DescribeChangeSet",
"cloudformation:ExecuteChangeSet",
"cloudformation:SetStackPolicy",
"cloudformation:ValidateTemplate",
"iam:PassRole"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"codebuild:BatchGetBuilds",
"codebuild:StartBuild"
],
"Resource": "*",
"Effect": "Allow"
}
]
}
}
],
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"sts:AssumeRole"
],
"Effect": "Allow",
"Principal": {
"Service": [
"codepipeline.amazonaws.com"
]
}
}
]
}
}
},
"CFNDeployRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"Policies": [
{
"PolicyName": "DeployAccess",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "*",
"Resource": "*",
"Effect": "Allow"
}
]
}
}
],
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"sts:AssumeRole"
],
"Effect": "Allow",
"Principal": {
"Service": [
"cloudformation.amazonaws.com"
]
}
}
]
}
}
}
},
"Outputs": {
"SourceRepoURL": {
"Value": {
"Fn::GetAtt": "SourceRepository.CloneUrlHttp"
}
},
"S3ApplicationBucket": {
"Value": {
"Ref": "ApplicationBucket"
}
},
"CodeBuildRoleArn": {
"Value": {
"Fn::GetAtt": "CodeBuildRole.Arn"
}
},
"S3PipelineBucket": {
"Value": {
"Ref": "ArtifactBucketStore"
}
},
"CodePipelineRoleArn": {
"Value": {
"Fn::GetAtt": "CodePipelineRole.Arn"
}
},
"CFNDeployRoleArn": {
"Value": {
"Fn::GetAtt": "CFNDeployRole.Arn"
}
}
}
}
(12/10時点) 使用している AWSアカウントでは、CodeBuild と CodePipeline の利用権限を付与していないので、動作確認できていない。 CloudTrailの履歴には「十分な AWS Config 権限がありません。」とのメッセージがでている。
IAM 管理権限を付与して再度実行。
chalice deploy
Creating deployment package.
Could not install dependencies:
pyyaml==3.13
You will have to build these yourself and vendor them in
the chalice vendor folder.
Your deployment will continue but may not work correctly
if missing dependencies are not present. For more information:
http://chalice.readthedocs.io/en/latest/topics/packaging.html
Updating policy for IAM role: aws-cognito-chalice-dev
Updating lambda function: aws-cognito-chalice-dev
Traceback (most recent call last):
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/chalice/awsclient.py", line 283, in _update_function_code
FunctionName=function_name, ZipFile=zip_contents)
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/botocore/client.py", line 320, in _api_call
return self._make_api_call(operation_name, kwargs)
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/botocore/client.py", line 624, in _make_api_call
raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (RequestEntityTooLargeException) when calling the UpdateFunctionCode operation: Request must be smaller than 69905067 bytes for the UpdateFunctionCode operation
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/chalice/deploy/deployer.py", line 330, in deploy
return self._deploy(config, chalice_stage_name)
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/chalice/deploy/deployer.py", line 343, in _deploy
self._executor.execute(plan)
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/chalice/deploy/executor.py", line 31, in execute
self._default_handler)(instruction)
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/chalice/deploy/executor.py", line 43, in _do_apicall
result = method(**final_kwargs)
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/chalice/awsclient.py", line 263, in update_function
zip_contents=zip_contents)
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/chalice/awsclient.py", line 290, in _update_function_code
raise self._get_lambda_code_deployment_error(e, context)
chalice.awsclient.DeploymentPackageTooLargeError: An error occurred (RequestEntityTooLargeException) when calling the UpdateFunctionCode operation: Request must be smaller than 69905067 bytes for the UpdateFunctionCode operation
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/chalice/cli/__init__.py", line 447, in main
return cli(obj={})
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/click/core.py", line 722, in __call__
return self.main(*args, **kwargs)
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/click/core.py", line 697, in main
rv = self.invoke(ctx)
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/click/core.py", line 1066, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/click/core.py", line 895, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/click/core.py", line 535, in invoke
return callback(*args, **kwargs)
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/click/decorators.py", line 17, in new_func
return f(get_current_context(), *args, **kwargs)
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/chalice/cli/__init__.py", line 183, in deploy
deployed_values = d.deploy(config, chalice_stage_name=stage)
File "/Users/nicdk/.pyenv/versions/3.6.0/lib/python3.6/site-packages/chalice/deploy/deployer.py", line 332, in deploy
raise ChaliceDeploymentError(e)
chalice.deploy.deployer.ChaliceDeploymentError: ERROR - While sending your chalice handler code to Lambda to update function
"aws-cognito-chalice-dev", received the following error:
An error occurred (RequestEntityTooLargeException) when calling the
UpdateFunctionCode operation: Request must be smaller than 69905067 bytes for
the UpdateFunctionCode operation
This is likely because the deployment package is 106.1 MB. Lambda only allows
deployment packages that are 50.0 MB or less in size. To avoid this error,
decrease the size of your chalice application by removing code or removing
dependencies from your chalice application.
エラーはでているが作成されている。
API Gateway に作成された helloworld
より、ダッシュボードで URL を確認。
- URL
https://7rb15x8ite.execute-api.ap-northeast-1.amazonaws.com/api/
- GitHub - Miserlou/Zappa: Serverless Python https://github.com/Miserlou/Zappa
実行。
% zappa init
███████╗ █████╗ ██████╗ ██████╗ █████╗
╚══███╔╝██╔══██╗██╔══██╗██╔══██╗██╔══██╗
███╔╝ ███████║██████╔╝██████╔╝███████║
███╔╝ ██╔══██║██╔═══╝ ██╔═══╝ ██╔══██║
███████╗██║ ██║██║ ██║ ██║ ██║
╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝
Welcome to Zappa!
Zappa is a system for running server-less Python web applications on AWS Lambda and AWS API Gateway.
This `init` command will help you create and configure your new Zappa deployment.
Let's get started!
Your Zappa configuration can support multiple production stages, like 'dev', 'staging', and 'production'.
What do you want to call this environment (default 'dev'):
AWS Lambda and API Gateway are only available in certain regions. Let's check to make sure you have a profile set up in one that will work.
We found the following profiles: default, and bd. Which would you like us to use? (default 'default'):
Your Zappa deployments will need to be uploaded to a private S3 bucket.
If you don't have a bucket yet, we'll create one for you too.
What do you want to call your bucket? (default 'zappa-4qmujxmm7'):
What's the modular path to your app's function?
This will likely be something like 'your_module.app'.
Where is your app's function?:
Where is your app's function?: hello.app
You can optionally deploy to all available regions in order to provide fast global service.
If you are using Zappa for the first time, you probably don't want to do this!
Would you like to deploy this application globally? (default 'n') [y/n/(p)rimary]:
Okay, here's your zappa_settings.json:
{
"dev": {
"app_function": "hello.app",
"aws_region": "ap-northeast-1",
"profile_name": "default",
"project_name": "aws-cognito-zap",
"runtime": "python3.6",
"s3_bucket": "zappa-4qmujxmm7"
}
}
Does this look okay? (default 'y') [y/n]: y
Done! Now you can deploy your Zappa application by executing:
$ zappa deploy dev
After that, you can update your application code with:
$ zappa update dev
To learn more, check out our project page on GitHub here: https://github.com/Miserlou/Zappa
and stop by our Slack channel here: https://slack.zappa.io
Enjoy!,
~ Team Zappa!
% zappa deploy dev
Calling deploy for stage dev..
Creating aws-cognito-zap-dev-ZappaLambdaExecutionRole IAM Role..
Error: Failed to manage IAM roles!
You may lack the necessary AWS permissions to automatically manage a Zappa execution role.
To fix this, see here: https://github.com/Miserlou/Zappa#custom-aws-iam-roles-and-policies-for-deployment
実行失敗。IAM設定権限不足。
- Serverless Framework - AWS Lambda Guide - Quick Start https://serverless.com/framework/docs/providers/aws/guide/quick-start/
QuickStart だけで動作する。
(コマンドは短縮別名のsls
を使用)
% sls create --template
Serverless: Generating boilerplate...
Serverless Error ---------------------------------------
Template "true" is not supported. Supported templates are: "aws-clojure-gradle", "aws-clojurescript-gradle", "aws-nodejs", "aws-nodejs-typescript", "aws-alexa-typescript", "aws-nodejs-ecma-script", "aws-python", "aws-python3", "aws-groovy-gradle", "aws-java-maven", "aws-java-gradle", "aws-kotlin-jvm-maven", "aws-kotlin-jvm-gradle", "aws-kotlin-nodejs-gradle", "aws-scala-sbt", "aws-csharp", "aws-fsharp", "aws-go", "aws-go-dep", "aws-go-mod", "aws-ruby", "azure-nodejs", "cloudflare-workers", "cloudflare-workers-enterprise", "fn-nodejs", "fn-go", "google-nodejs", "kubeless-python", "kubeless-nodejs", "openwhisk-java-maven", "openwhisk-nodejs", "openwhisk-php", "openwhisk-python", "openwhisk-ruby", "openwhisk-swift", "spotinst-nodejs", "spotinst-python", "spotinst-ruby", "spotinst-java8", "plugin" and "hello-world".
Get Support --------------------------------------------
Docs: docs.serverless.com
Bugs: github.com/serverless/serverless/issues
Issues: forum.serverless.com
Your Environment Information -----------------------------
OS: darwin
Node Version: 10.7.0
Serverless Version: 1.34.1
テンプレート aws-nodejs を使用して作成。
% sls create --template aws-nodejs
Serverless: Generating boilerplate...
_______ __
| _ .-----.----.--.--.-----.----| .-----.-----.-----.
| |___| -__| _| | | -__| _| | -__|__ --|__ --|
|____ |_____|__| \___/|_____|__| |__|_____|_____|_____|
| | | The Serverless Application Framework
| | serverless.com, v1.34.1
-------'
Serverless: Successfully generated boilerplate for template: "aws-nodejs"
Serverless: NOTE: Please update the "service" property in serverless.yml with your service name
配備されるプログラム。
% cat handler.js
'use strict';
module.exports.hello = async (event, context) => {
return {
statusCode: 200,
body: JSON.stringify({
message: 'Go Serverless v1.0! Your function executed successfully!',
input: event,
}),
};
// Use this code if you don't use the http event with the LAMBDA-PROXY integration
// return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};
AWSに配備。 リージョンは初期値だと us-east-1 になる。
% sls deploy -v
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
CloudFormation - CREATE_IN_PROGRESS - AWS::CloudFormation::Stack - aws-nodejs-dev
CloudFormation - CREATE_IN_PROGRESS - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_IN_PROGRESS - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_COMPLETE - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_COMPLETE - AWS::CloudFormation::Stack - aws-nodejs-dev
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (608 B)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
CloudFormation - UPDATE_IN_PROGRESS - AWS::CloudFormation::Stack - aws-nodejs-dev
CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - HelloLogGroup
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - HelloLogGroup
CloudFormation - CREATE_COMPLETE - AWS::Logs::LogGroup - HelloLogGroup
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_COMPLETE - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_COMPLETE - AWS::Lambda::Function - HelloLambdaFunction
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Version - HelloLambdaVersioncKAkuSOBXp8SGsMAYNd3d283HOANAFU08sRBTM9ins
CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Version - HelloLambdaVersioncKAkuSOBXp8SGsMAYNd3d283HOANAFU08sRBTM9ins
CloudFormation - CREATE_COMPLETE - AWS::Lambda::Version - HelloLambdaVersioncKAkuSOBXp8SGsMAYNd3d283HOANAFU08sRBTM9ins
CloudFormation - UPDATE_COMPLETE_CLEANUP_IN_PROGRESS - AWS::CloudFormation::Stack - aws-nodejs-dev
CloudFormation - UPDATE_COMPLETE - AWS::CloudFormation::Stack - aws-nodejs-dev
Serverless: Stack update finished...
Service Information
service: aws-nodejs
stage: dev
region: us-east-1
stack: aws-nodejs-dev
api keys:
None
endpoints:
None
functions:
hello: aws-nodejs-dev-hello
layers:
None
Stack Outputs
HelloLambdaFunctionQualifiedArn: arn:aws:lambda:us-east-1:410408491513:function:aws-nodejs-dev-hello:1
ServerlessDeploymentBucketName: aws-nodejs-dev-serverlessdeploymentbucket-1ofy7squo5qfp
配備したAPIの応答を確認。
% sls invoke -f hello -l
{
"statusCode": 200,
"body": "{\"message\":\"Go Serverless v1.0! Your function executed successfully!\",\"input\":{}}"
}
--------------------------------------------------------------------
START RequestId: d66c373e-fd2a-11e8-8943-9bf3efef67d8 Version: $LATEST
END RequestId: d66c373e-fd2a-11e8-8943-9bf3efef67d8
REPORT RequestId: d66c373e-fd2a-11e8-8943-9bf3efef67d8 Duration: 3.29 ms Billed Duration: 100 ms Memory Size: 1024 MB Max Memory Used: 45 MB
更新向けのプログラムのみの配備。
% sls deploy function -f hello
Serverless: Packaging function: hello...
Serverless: Excluding development dependencies...
Serverless: Code not changed. Skipping function deployment.
Serverless: Successfully updated function: hello
配備したプログラムを削除。
% sls remove
Serverless: Getting all objects in S3 bucket...
Serverless: Removing objects in S3 bucket...
Serverless: Removing Stack...
Serverless: Checking Stack removal progress...
.........
Serverless: Stack removal finished...
ユーザプールは消えない。
ローカルでの応答確認。
sls invoke local -f hello -l
{
"statusCode": 200,
"body": "{\"message\":\"Go Serverless v1.0! Your function executed successfully!\",\"input\":\"\"}"
}
-
serverless frameworkでAWS Lambdaを開発してハマった話とか - kdnakt blog http://kdnakt.hatenablog.com/entry/2018/08/22/080000
-
- メモリの初期設定が128MBではなく1024MBで作成される。
1024MBのメモリで100ms実行すると、$0.000001667。
メモリが8倍になると、料金も約8倍になっている。
...
因みにserverless.ymlのメモリサイズの設定例は以下の通り。
provider:
name: aws
runtime: nodejs6.10
memorySize: 128 # Overwrite the default memory size. Default is 1024
Well-Architected Framework のベストプラクティスに従って、ユーザではなくグループを作成してポリシーを割り当て、ユーザはグループに所属させる形で使用していた。 しかし、グループのポリシーのアタッチ数は10個までなので、限界がある。
そこで、必要なものを組み合わせたポリシーを作成し、それをグループに割り当てする。
- ServerlessインストールからLambdaへのデプロイ - Qiita https://qiita.com/jumjamjohn/items/abbc060fd2c1c6791ef3
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"iam:*",
"s3:*",
"apigateway:*",
"logs:*",
"lambda:*",
"cloudformation:*",
"events:*"
],
"Resource": "*"
}
]
}
元記事では JSON エディタを使っていたが、ビジュアルエディタで作成した。
- Serverless Framework - AWS Lambda Guide - Services https://serverless.com/framework/docs/providers/aws/guide/services/
DynamoDB テーブルを resource で指定。
% cat serverless.yml
service: aws-nodejs
provider:
name: aws
runtime: nodejs8.10
memorySize: 128
region: ap-northeast-1
# (function ブロック省略)
# you can add CloudFormation resource templates here
resources:
Resources:
usersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: usersTable
AttributeDefinitions:
- AttributeName: email
AttributeType: S
KeySchema:
- AttributeName: email
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
DynamoDB を配備。
% sls deploy
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (625 B)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
.......................
Serverless: Stack update finished...
Service Information
service: aws-nodejs
stage: dev
region: ap-northeast-1
stack: aws-nodejs-dev
api keys:
None
endpoints:
None
functions:
hello: aws-nodejs-dev-hello
user: aws-nodejs-dev-user
layers:
None
- examples/aws-node-auth0-custom-authorizers-api at master · serverless/examples · GitHub https://github.com/serverless/examples/tree/master/aws-node-auth0-custom-authorizers-api
Auth0 に登録。
配備。
sls deploy
Serverless Warning --------------------------------------
A valid file to satisfy the declaration 'file(./public_key)' could not be found.
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (127.04 KB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..............................................................................
Serverless: Stack update finished...
Service Information
service: aws-custom-authorizer-auth0
stage: dev
region: ap-northeast-1
stack: aws-custom-authorizer-auth0-dev
api keys:
None
endpoints:
POST - https://qu163to86a.execute-api.ap-northeast-1.amazonaws.com/dev/api/public
POST - https://qu163to86a.execute-api.ap-northeast-1.amazonaws.com/dev/api/private
functions:
auth: aws-custom-authorizer-auth0-dev-auth
publicEndpoint: aws-custom-authorizer-auth0-dev-publicEndpoint
privateEndpoint: aws-custom-authorizer-auth0-dev-privateEndpoint
layers:
None
endpoint を frontend/app.js に反映。
[EOF]