JAWS-UG CLI 専門支部 #168R S3基礎 Webサイト&ログ に参加したので、そのレポートです。

connpass のイベントページはこちら。

前回のレポートはこちら。

目次

S3 の概要

今回も前半部分は S3 の概要についての説明だったので、メモを残しておきます。

S3 の全体像

  • マネジメントコンソールでは S3
  • API としては s3s3control
  • CLI としては s3s3apis3control
  • s3control はパワーキット的な感じ
    • バッチオペレーション など

サービス概要

  • オブジェクトストレージ (所謂ブロックストレージとは違う)
  • アクセス方法
    • ブラウザ、 http クライアント
    • CLI、 SDK、マネジメントコンソール
  • アクセス制御方法
    • パブリックアクセスブロック
    • バケットポリシー
    • バケット ACL

要素概要

  • インプット/アクセス
    • AWS リソース
    • 利用者 (一般)
    • 利用者 with IAM ポリシー
    • インプット側からオブジェクトまでのアクセス制御
      • バケット
        • パブリックアクセス
          • CORS
          • Web サイトホスティング
        • 署名付き URL
        • バケットポリシー、バケット ACL
      • オブジェクト
        • オブジェクト ACL
        • オブジェクト属性
        • オブジェクトロック
  • アウトプット
    • S3 バケット
    • ライフサイクル
      • S3 Gracier
    • メトリクス
      • CloudWatch
    • 通知
      • SNS
      • SQS
      • Lambda

ハンズオン

今回のハンズオンでは、 S3 の Static website hosting の機能を使って静的なサイトを構築し、そのサイトへのアクセスログを別のバケットに出力するという構成を AWS CLI で構築しました。

ハンズオンの詳細な手順についてはイベントページの資料 及び下記のハンズオン資料におまかせするとして、個人的に気になった部分のコマンドについて書いておきます。

1.2. S3バケットACL更新

アクセスログを出力するバケットに対して、下記のコマンドでバケット ACL を変更しました。

$ aws s3api put-bucket-acl \
--bucket ${S3_BUCKET_NAME} \
--grant-write 'URI="http://acs.amazonaws.com/groups/s3/LogDelivery"' \
--grant-read-acp 'URI="http://acs.amazonaws.com/groups/s3/LogDelivery"'

ここで出てくる LogDelivery とは、 ACL を設定する際に S3 で予め定義されたグループのことだそうです。公式ドキュメントには下記のように書かれていました。

バケットの WRITE 許可により、このグループはサーバーアクセスログ (「Amazon S3 サーバーアクセスのログ記録」を参照) をバケットに書き込むことができます。

アクセスコントロールリスト (ACL) の概要 - Amazon Simple Storage Service

2.6. S3バケット Webサイトホスティング設定の作成

S3 バケットに対して Static website hosting の機能を有効にするために、 s3api put-bucket-website コマンドを使いました。 --website-configuration オプションで Static website hosting の設定をしますが、ハンズオンでは下記のような設定をしました。

{
  "ErrorDocument": {
    "Key": "error.html"
  },
  "IndexDocument": {
    "Suffix": "index.html"
  }
}

その他、詳細な設定としては下記のようなものがあるようです。

$ aws s3api put-bucket-website \
--generate-cli-skeleton yaml-input
Bucket: ''  # [REQUIRED] The bucket name.
ContentMD5: '' # The base64-encoded 128-bit MD5 digest of the data.
WebsiteConfiguration: # [REQUIRED] Container for the request.
  ErrorDocument:  # The name of the error document for the website.
    Key: ''  # [REQUIRED] The object key name to use when a 4XX class error occurs.
  IndexDocument: # The name of the index document for the website.
    Suffix: ''  # [REQUIRED] A suffix that is appended to a request that is for a directory on the website endpoint (for example,if the suffix is index.
  RedirectAllRequestsTo: # The redirect behavior for every request to this bucket's website endpoint.
    HostName: ''  # [REQUIRED] Name of the host where requests are redirected.
    Protocol: https # Protocol to use when redirecting requests. Valid values are: http, https.
  RoutingRules: # Rules that define when a redirect is applied and the redirect behavior.
  - Condition:  # A container for describing a condition that must be met for the specified redirect to apply.
      HttpErrorCodeReturnedEquals: ''  # The HTTP error code when the redirect is applied.
      KeyPrefixEquals: '' # The object key name prefix when the redirect is applied.
    Redirect: # [REQUIRED] Container for redirect information.
      HostName: ''  # The host name to use in the redirect request.
      HttpRedirectCode: '' # The HTTP redirect code to use on the response.
      Protocol: https # Protocol to use when redirecting requests. Valid values are: http, https.
      ReplaceKeyPrefixWith: '' # The object key prefix to use in the redirect request.
      ReplaceKeyWith: '' # The specific object key to use in the redirect request.
ExpectedBucketOwner: '' # The account id of the expected bucket owner.

ちなみにこの --generate-cli-skeleton yaml-input を使うと、上記のようにコメント付きで情報が出力されるようです。

4. S3オブジェクトの確認 (ログの確認)

今回のハンズオン手順では、 s3://{S3_BUCKET_NAME}-log/Logs/ にアクセスログが出力されるはずでしたが、途中の手順をミスっていたためログの出力先がバケット直下になっていました。

$ aws s3api get-bucket-logging \
--bucket ${S3_BUCKET_NAME}
{
    "LoggingEnabled": {
        "TargetBucket": "handson-cli-s3-website-logging-website-1234XXXXXXXX-log",
        "TargetPrefix": "/"
    }
}

本当は TargetPrefixLogs/ になっているはずでした。

これの何が辛かった (辛くはないけど気持ち悪かった) かというと、マネジメントコンソールで見たときに無名のディレクトリ (プレフィックス) が存在する形になってしまいます。

empty prefix

CLI で確認するとこんな感じ。

$ aws s3 ls s3://${S3_BUCKET_NAME}-log/
                           PRE /

こうなった場合、 CLI では次のようにして確認します。

$ aws s3 ls s3://${S3_BUCKET_NAME}-log//
2020-09-24 11:17:40        636 2020-09-24-11-17-39-D5F59C25B3841CAF
2020-09-24 11:17:43       2981 2020-09-24-11-17-42-83D41566B32CB6A7
2020-09-24 11:17:47        720 2020-09-24-11-17-46-2283C60C4B76D482
2020-09-24 11:17:50        656 2020-09-24-11-17-49-F938F6B946395166
2020-09-24 11:17:52        720 2020-09-24-11-17-51-70439B9BAD87B553
2020-09-24 11:18:12        720 2020-09-24-11-18-11-2FEBA44AFEB0C91D
2020-09-24 11:18:15        720 2020-09-24-11-18-14-A7A7F94850ED3EAD
2020-09-24 11:19:10        720 2020-09-24-11-19-09-56EBF4C2C6EA75BB
2020-09-24 11:19:36       1387 2020-09-24-11-19-35-0F6E11B37CB0113E

S3 はオブジェクトストレージであってファイルシステムではないので // という指定もおかしくはないのですが、ちょっと気持ち悪いですね。

CDK でやる場合との比較

今回はハンズオン後の LT で、同じような構成を CDK で構築した場合との比較についての話がありました。CDK は詳細な部分が隠蔽されて楽な反面、裏で動いている処理 (設定) については、 CLI を使ったほうが確認しやすいという話でした。CDK でサクッと作れるものでも、抽象化されている部分を CLI の操作で学んでおくのは大事だなと感じました。

で、実は自分も前に CDK を使って同じような構成を作っていました。

確かに CDK だと簡単に作れてしまう構成を CLI でポチポチやるのは時間がかかります。が、その分 API レベルで何をやっているのかがわかるので、今回のハンズオンも勉強になりました。

CDK で AWS アカウント ID を扱う

CDK と CLI との比較に関する LT 後の話の中で、 今回のように S3 を扱うハンズオンの場合、バケット名の衝突を防ぐために AWS アカウント ID をバケット名に組み込むと幸せになれる という話がありました。
後述しますが、今回のハンズオンでは sts get-caller-identity から AWS アカウント ID を取得してバケット名に組み込んでいましたが、 CDK でも同じことができないか調べてみました。

結論から言うと、できました。

今回のように AWS アカウント ID をバケット名に含む S3 バケットを作成するには、次のようにします。

import * as cdk from '@aws-cdk/core';
import s3 = require('@aws-cdk/aws-s3');

export class S3WithAwsIdStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const bucketName: string = `sample-bucket-${this.account}`;
    const bucket: s3.Bucket = new s3.Bucket(this, 'SampleBucketWithAccountID', {
      bucketName,
      publicReadAccess: true,
      websiteIndexDocument: 'index.html',
      websiteErrorDocument: 'error.html',
      removalPolicy: cdk.RemovalPolicy.DESTROY
    });
  }
}

これで生成される CloudFormation テンプレートは下記のようになります。

Resources:
  SampleBucketWithAccountID62145D3E:
    Type: AWS::S3::Bucket
    Properties:
      BucketName:
        Fn::Join:
          - ""
          - - sample-bucket-
            - Ref: AWS::AccountId
      WebsiteConfiguration:
        ErrorDocument: error.html
        IndexDocument: index.html
    UpdateReplacePolicy: Delete
    DeletionPolicy: Delete

cdk deploy すれば sample-bucket-XXXXXXXXXXXX という形で、バケット名に AWS アカウント ID を含んだ S3 バケットが作成されます。

知らんかったぞ、それ

今回に限らずハンズオンでは新しく知ることがたくさんあるので、この 知らんかったぞ、それ 項目では新しく知ったことを自分なりに深堀りしてみたいと思います。(恒例化したい)

というのを前回のレポートでも書いたので、今回も一つ取り上げてみます。

AWS アカウント ID の取得

今回のハンズオンでは、 S3 バケット名が衝突しないようにバケット名に AWS アカウント ID を含むようになっており、 AWS アカウント ID を取得するために下記のようなコマンドを実行しました。

$ AWS_ID=$( \
  aws sts get-caller-identity \
    --query 'Account' \
    --output text \
) \
&& echo ${AWS_ID}

実行しているコマンドは sts get-caller-identity で、その出力結果から AWS アカウント ID を --query で抜き出しています。

この sts get-caller-identity コマンドでそもそも何だ?というのを調べてみました。といっても、 CLI のヘルプを確認です。

$ aws sts get-caller-identity help
...
NAME
       get-caller-identity -

DESCRIPTION
       Returns  details  about the IAM user or role whose credentials are used
       to call the operation.

       NOTE:
          No permissions are required to perform this operation. If an  admin-
          istrator  adds  a  policy  to  your IAM user or role that explicitly
          denies access to the sts:GetCallerIdentity  action,  you  can  still
          perform  this  operation.  Permissions  are not required because the
          same information is returned when an IAM  user  or  role  is  denied
          access. To view an example response, see I Am Not Authorized to Per-
          form: iam:DeleteVirtualMFADevice in the IAM User Guide .

       See also: AWS API Documentation

       See 'aws help' for descriptions of global parameters.

SYNOPSIS
            get-caller-identity
          [--cli-input-json | --cli-input-yaml]
          [--generate-cli-skeleton <value>]
          [--cli-auto-prompt <value>]

DESCRIPTION にもあるように、 CLI コマンドを実行する IAM ユーザー または IAM ロールの詳細を取得できるコマンドのようです。 --query で結果を絞らずに実行すると、結果は下記の通り。

$ aws sts get-caller-identity                                                                                                            
{
    "UserId": "AROAJC7BA46AREMWOJDPW:i-12345abcde8cXXXXX",
    "Account": "1234XXXXXXXX",
    "Arn": "arn:aws:sts::1234XXXXXXXX:assumed-role/handson-cloud9-environment-role/i-12345abcde8cXXXXX"
}

Cloud 9 環境で実行したので、ユーザーは EC2 インスタンス、 Arn はアタッチされている IAM ロールのものが出力されています。

ちなみにこのコマンドに関してもクラメソさんのブログがヒットしました。

記事によると、2016 年 当時の AWS CLI バージョンは 1.10.18 だったようです。

まとめ

JAWS-UG CLI 専門支部 #168R S3基礎 Webサイト&ログ の参加レポートでした。

今回は S3 の Static website hosting 機能を使ったサイトの構築とログの確認ということで、以前に CDK で同じような構成を作っていた内容でした。CDK だと少ない記述でサクッと作れて楽だなーと思っていたので、それを CLI でやるとなるとちょっと面倒なのでは?と思っていました。実際にやってみるとそう思う部分もありましたが、やはり CDK で抽象化されている部分について CLI の操作で確認することができたので、より理解が深まった気がします。

個人的には同じ構成を違う方法で構築してみると更に理解が深まるという感覚があるので、今回のハンズオンも大変勉強になりました。

しばらくは S3 に関する内容が続くようなので、意外と知らない S3 の機能詳細について勉強していけるといいなと思います。


以上、よっしー (michimani) でした。

Share to ...

0

Follow on Feedly