【CloudFormation】S3のバケットの中身をCloudFormationで作成する
はじめに
CloudFormationでシステムのようなものを組んでいるうちに、本当で適当で良いから入力フォームみたいなものが必要になってきた。 こうしたときに便利なのがS3の静的ホスティング機能だろう。
ただ、CloudFormationは、S3の中身までは作成できないという欠点があった。
強力なS3Objectsマクロ
しかし、このS3Objectsを利用すれば、CloudFormationでS3内のファイルを作成することができる github.com
デプロイ
使い方は非常に簡単で、上記のサイトの手順に従ってマクロをデプロイする。適当なアーティファクトバケットを作成する。 次に、git cloneかファイルのダウンロードし*1、以下のコマンドを実行する。
aws cloudformation package \ --template-file macro.template \ --s3-bucket <your bucket name> \ --output-template-file packaged.template aws cloudformation deploy \ --stack-name s3objects-macro \ --template-file packaged.template \ --capabilities CAPABILITY_IAM
これで、デプロイしたアカウント内ならばこのマクロを利用することができるようになった。 なお、私はコマンドをうつのが面倒だったため、CodeStarを利用した。
テスト
テスト用のコードも用意されているため、実行して有効性を確認する。
aws cloudformation deploy \ --stack-name s3objects-macro-example \ --template-file example.template \ --capabilities CAPABILITY_IAM
使い方
CloudFormationが利用するIAMロールにS3Fullをアタッチし*2、CloudFormationテンプレートの頭に以下のブロックを置くことで、マクロが有効化される。
2019/12/21追記:マクロを動作させるためには、CloudFormationが利用するRoleにAWSLambdaExecuteポリシーをアタッチする必要がある。
Transform: S3Objects
サーバーレス変換など、他のマクロを合わせて利用する場合は、以下のようにリスト表記する。
Transform: - S3Objects - AWS::Serverless-2016-10-31
公式ドキュメントによると、以下の使い方があるようだ。 なお、いずれの項目でも
Target: Bucket: Key:
は必須項目で、アップロード先のバケットとキー名を指定している。
テキストの内容をベタ書き
Transform: S3Objects Resources: Bucket: Type: AWS::S3::Bucket Object: Type: AWS::S3::Object Properties: Target: Bucket: !Ref Bucket Key: README.md ContentType: text/plain Body: Hello, world!
Body以下に書かれた内容がそのままファイルの内部に反映される。 非常に短いコードならば問題にならないが、長文になると修正も非常に面倒になるほか、コードを変更した際にテストが行えない点に注意すること。
エンコードされた文字列を記載する
SinglePixel: Type: AWS::S3::Object Properties: Target: Bucket: !Ref TargetBucket Key: 1pixel.gif Base64Body: R0lGODdhAQABAIABAP///0qIbCwAAAAAAQABAAACAkQBADs=
ここでは、Base64の文字列をテンプレート中に記載する。
S3にアップロードされているファイルを指定する。
CopiedObject: Type: AWS::S3::Object Properties: Source: Bucket: !Ref SourceBucket Key: index.html Target: Bucket: !Ref TargetBucket Key: index.html ACL: public-read
予め他のS3にアップロードしておいたファイルを指定してコピーする。
aws-cliのaws s3 cp ~~~
をcloudFormationで実行していると考えて良いだろう。
結論
S3Objects変換を利用することで、S3バケットの中身をCloudFormationで作成することができるようになった。 工夫すれば、静的サイトをCloudFormationで作成することができるようになると考えられる。 ただし、アップロードする方法がテキストのベタ書き、または他のs3オブジェクトへのリンクのみという弱点もある。*3
【AWS Athena】str型Timestampをクエリする
課題
以下のようなデータがあったとする。
{ "timestamp": "2019-02-01 00:00:00", "data": 100 } { "timestamp": "2019-03-01 00:00:00", "data": 100 } { "timestamp": "2019-04-01 00:00:00", "data": 100 }
このデータを年月ごとに集計するクエリを作成したい。。 正直に以下のSQLでクエリすると次のようになる
SELECT year(timestamp) as year, month(timestamp) as month, sum(data) FROM <your_year(timestamp),month(timestamp)database>.<your_table> GROUP BY year(timestamp),month(timestamp);
SYNTAX_ERROR: line 1:8: Unexpected parameters (varchar) for function year. Expected: year(timestamp) , year(timestamp with time zone) , year(date) , year(interval year to month)
原因と対策
これは、str型のタイムスタンプがタイムスタンプとして認識されていない事が原因である。 したがって以下のように記述する必要がある。
SELECT year(date_parse(timestamp, \'%Y/%m/%d %H:%i:%s\')), month(date_parse(timestamp, \'%Y/%m/%d %H:%i:%s\')), sum(data) FROM <your_year(timestamp),month(timestamp)database>.<your_table> GROUP BY year(date_parse(timestamp, \'%Y/%m/%d %H:%i:%s\')),month(date_parse(timestamp, \'%Y/%m/%d %H:%i:%s\'));
参考
AWS AthenaはPrestoベースであることがわかったため、以下のサイトを参考にした * Prestoでの日付の扱い方 - ★データ解析備忘録★ * prestoの気持ち 時間関係(Date and Time Functions and Operators) - Qiita
【AWS Athena】Lambdaからクエリを実行した際のIAM由来のエラーと対策
Athenaをテストした際に遭遇したエラーを以下にまとめる。別のエラーに遭遇した場合は後日追加していく。
Unable to Verify/Create Output Bucket
Unable to Verify/Create Output Bucket
S3へのアクセス権の不足が原因。以下のポリシーがアタッチされているか確認する。
- s3:ListBucket
- s3:ListBucketMultipartUploads
- s3:GetObject
- s3:GetBucketLocation
- s3:ListMultipartUploadParts
- s3:AbortMultipartUpload
- s3:PutObject
Access Denied
Access Denied (Service: Amazon S3; Status Code: 403; Error Code: AccessDenied;
S3へのアクセス権の不足。 以下のポリシーがアタッチされているか確認する。
- s3:PutObject
- s3:ListObject
Insufficient permissions to execute the query.
Insufficient permissions to execute the query. User: ROLE_NAME is not authorized to perform: glue:GetTable on resource: arn:aws:glue:ap-northeast-1:UserId:catalog
エラーコードの通り、glue:GetTable
のアクセス権が不足している。
HIVE_CANNOT_OPEN_SPLIT
Your query has the following errors:HIVE_CANNOT_OPEN_SPLIT: Error opening Hive split s3://BUCKET_NAME/part-00000-feda4fa5-d862-48ba-88ad-3a468e234a27-c000.snappy.parquet (offset=0, length=7791): com.amazonaws.services.s3.model.AmazonS3Exception: Forbidden (Service: Amazon S3; Status Code:403; Error Code: 403 Forbidden; Request ID: 028A0DA47697C2D7;(以下略)
この記事の通り、glue:GetTablesのアクセス権が不足している。
特に、自分でポリシーを作成した際に忘れやすい。
【CloudFormation】Error: The policy failed legacy parsing
やりたい事
以下のページを参考に、別アカウントのCodePipelineに、CodeCommitの変更を配信したい。また、これをCloudFormationを使ってできる限り省力化したい。
発生したエラー
開発アカウント側で、IAM Policyを作成する際にエラーが発生した。以下がテンプレートである。
AllowTargetAccessPolicy: Type: AWS::IAM::Policy Properties: Roles: - !Ref AllowTargetAccessRole PolicyName: !Join - "" - - !Ref AWS::Region - !Ref AWS::AccountId - To - !Ref TargetAccountID - CodeCommitAccessPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - s3:PutObject - s3:PutObjectAcl Resource: !Sub arn:aws:s3:::artifact-bucket-for-${TargetAccountID}-from-${AWS::AccountId} - Effect: Allow Action: - kms:DescribeKey - "kms:GenerateDataKey*" - kms:Encrypt - "kms:ReEncrypt*" - kms:Decrypt Resource: !Sub "arn:aws:kms:${AWS::Region}:${TargetAccountID}:key/${KMS_ID} - Effect: Allow Action: - codecommit:GetBranch - codecommit:GetCommit - codecommit:UploadArchive - codecommit:GetUploadArchiveStatus - codecommit:CancelUploadArchive Resource: !Sub arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:${RepositoryName}
すると、以下のエラーが発生した。
The policy failed legacy parsing (Service: AmazonIdentityManagement; Status Code: 400; Error Code: MalformedPolicyDocument;)
リソースをコメントアウトして、問題がKMSに関するポリシードキュメントブロックにあることがわかった。
解決方法
なぜか、!Sub の代わりに!Joinを使用することでエラーが回避できた。 (参考) tycoh.hatenablog.com
AllowTargetAccessPolicy: Type: AWS::IAM::Policy Properties: Roles: - !Ref AllowTargetAccessRole PolicyName: !Join - "" - - !Ref AWS::Region - !Ref AWS::AccountId - To - !Ref TargetAccountID - CodeCommitAccessPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - s3:PutObject - s3:PutObjectAcl Resource: !Sub arn:aws:s3:::artifact-bucket-for-${TargetAccountID}-from-${AWS::AccountId} - Effect: Allow Action: - kms:DescribeKey - "kms:GenerateDataKey*" - kms:Encrypt - "kms:ReEncrypt*" - kms:Decrypt Resource: | !Join - "" - - arn:aws:kms - !Ref AWS::Region - ":" - !Ref TargetAccountID - "key/" - !Ref KMSArn - Effect: Allow Action: - codecommit:GetBranch - codecommit:GetCommit - codecommit:UploadArchive - codecommit:GetUploadArchiveStatus - codecommit:CancelUploadArchive Resource: !Sub arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:${RepositoryName}
原因は後日AWS Forumに質問してみることにする。
【Error】CloudFormation:: Parameter is non alphaNumeric
現象
以下のCloudFormationテンプレートのパラメーターブロックでParameter is non alphaNumeric
というエラーが発生した。
--- Parameter: MY_MESSAGE: This is test ---
原因
パラメーターブロックでは、記号を使うことができない。そのため、アンダーバーを使うことができない。 そのため、キャメルケースでパラメーターを設定する。
Parameter: myMessage: This is test # または、MyMessage
参考
CloudFormation Parameter Template Error : Parameter is non alphanumeric
【CloudFormation】!Subと!Refの使い分け
CloudFormationのテンプレートでよく使う組み込み関数に!Refと!Subがあります。 これらの違いを整理します。
- 基本的な使い方
- 基本的な作用
- 使い方
- 誤った使い方
- !Subの問題点
- 文字列との結合
- !Subの場合
- !Refの場合
- 結論
CloudFormationメモ SQSとLambdaの連携
Error
Queue visibility timeout: 120 seconds is less than Function timeout: 900 seconds
エラーが発生したときのコード
SensorDataQueue: Type: AWS::SQS::Queue Properties: VisibilityTimeout: 120 #ここがLambdaのタイムアウト時間より短い calcData: Type: AWS::Serverless::Function Properties: #中略 Events: SQS: Type: SQS Timeout: 900 Properties: Queue: !GetAtt SensorDataQueue.Arn BatchSize: 10 Enabled: True
対策
SQSのVisiblityTimeOut
を、LambdaのTimeout
で設定した値以上にする