エラー MalformedPolicy: Policy has invalid resource で S3 のバケットポリシーを作成できなかった話 (Terraform)

問題の Terraform コンフィグ

まずは下記のコンフィグを apply する。
(provider 等の指定は省略している。)

resource "aws_s3_bucket" "b" {
  acl    = "private"
}

resource "aws_s3_bucket_policy" "b" {
  bucket = aws_s3_bucket.b.id
  policy = data.aws_iam_policy_document.b.json
}

data "aws_iam_policy_document" "b" {
  statement {
    effect = "Allow"

    principals {
      type = "AWS"
      identifiers = [
        "arn:aws:iam::111111111111:role/iam-role-name",
        "arn:aws:iam::222222222222:role/iam-role-name"
      ]
    }

    actions = [
      "s3:AbortMultipartUpload",
      "s3:GetBucketLocation",
      "s3:GetObject",
      "s3:ListBucket",
      "s3:ListBucketMultipartUploads",
      "s3:PutObject",
      "s3:PutObjectAcl"
    ]

    resources = [
      "${aws_s3_bucket.b.arn}/",
      "${aws_s3_bucket.b.arn}/*"
    ]
  }
}

すると、S3バケットポリシーを作成するタイミングで下記のエラーが発生する。

aws_s3_bucket_policy.b: Still creating... [10s elapsed]
aws_s3_bucket_policy.b: Still creating... [20s elapsed]
aws_s3_bucket_policy.b: Still creating... [30s elapsed]
aws_s3_bucket_policy.b: Still creating... [40s elapsed]
aws_s3_bucket_policy.b: Still creating... [50s elapsed]
╷
│ Error: Error putting S3 policy: MalformedPolicy: Policy has invalid resource

原因

statement 内の resources の指定が誤っていた。

    resources = [
      "${aws_s3_bucket.b.arn}/",
      "${aws_s3_bucket.b.arn}/*"
    ]

${aws_s3_bucket.b.arn} の後の / が余計だった。

    resources = [
      "${aws_s3_bucket.b.arn}",
      "${aws_s3_bucket.b.arn}/*"
    ]

あらためてドキュメントを読む

docs.aws.amazon.com

ARN (Amazon Resource Name ) は下記の共通の形式を持っている。

arn:partition:service:region:namespace:relative-id

S3 の場合は region および namespace が省略されるので以下の通りである。

arn:aws:s3:::relative-id

S3 においてこの relative-id として許容されるのは bucket-name もしくは bucket-name/object-key らしい。
つまり、いずれにも当てはまらない bucket-name/ が存在したことによって MalformedPolicy と判断されたのであった。

AWS アカウントの代替の連絡先 (alternate contacts) を AWS CLI から登録する

はじめに

aws.amazon.com

2021/9/30 の当該アップデートで下記 3つの API が追加されました。

  • DeleteAlternateContact
  • GetAlternateContact
  • PutAlternateContact

その名の通り AWS アカウントの「代替の連絡先」を削除・取得・登録する API です。
勤務先で Organizations を導入したばかりのところにこのアップデートがやってきたので、全アカウントにセキュリティコンタクトを登録してみました。

事前準備

AWS CLI のアップデート(2021/10/03時点)

私の CloudShell の環境では AWS CLI のバージョンが旧く、登録のための aws account コマンドが使用できませんでした。
同様の状況だった場合は下記のコマンドで AWS CLI をアップデートしてください。

$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
$ unzip awscliv2.zip
$ sudo ./aws/install --update
$ aws --version
aws-cli/2.2.43 Python/3.8.8 Linux/4.14.243-185.433.amzn2.x86_64 exec-env/CloudShell exe/x86_64.amzn.2 prompt/off

「信頼されたアクセス」の有効化

Organizations 経由で代替の連絡先を登録するためには「信頼されたアクセス」を有効化してあげる必要があります。
管理アカウントで下記コマンドを実行して有効化します。

$ aws organizations enable-aws-service-access --service-principal account.amazonaws.com

登録手順(メンバーアカウント)

aws.amazon.com

基本的にこのブログにそって登録していきます。 以下の手順は管理アカウントの CloudShell から実施しています。

シェルスクリプトの準備

ブログ内の手順では2つのシェルスクリプトを使用してセキュリティコンタクトの登録を行っています。

loop-through-accounts.sh の準備

下記のコマンドを CloudShell で実行してシェルスクリプトの作成・パーミッションの変更を実施します。

cat << EOF > loop-through-accounts.sh
#! /bin/bash
    managementaccount=\`aws organizations describe-organization --query Organization.MasterAccountId --output text\`

    for account in \$(aws organizations list-accounts --query 'Accounts[].Id' --output text); do

            if [ "\$managementaccount" -eq "\$account" ]
                     then
                         echo 'Skipping management account.'
                         continue
            fi
            ./put-security-contact.sh -a \$account
            sleep 0.2
    done
EOF
chmod 755 loop-through-accounts.sh

このシェルスクリプトでは Organizations の組織配下に存在するメンバーアカウントのアカウント ID をリストし put-security-contact.sh (後述) に渡しています。
if ブロックで管理アカウントのアカウント ID が除外されている理由については後述します。
また、組織配下の AWS アカウント数が 1000 を超える場合は pagination で処理する必要があるのでご注意ください。

Using AWS CLI pagination options - AWS Command Line Interface

put-security-contact.sh の準備

下記のコマンドを CloudShell で実行してシェルスクリプトの作成・パーミッションの変更を実施します。
aws account put-alternate-contact のオプションの Email アドレス・電話番号・役職・氏名は適切なものに変更してください。

cat << EOF > put-security-contact.sh
#! /bin/bash
while getopts a: flag
do
    case "\${flag}" in
        a) account_id=\${OPTARG};;
    esac
done

echo 'Put security contact for account '\$account_id'...'
aws account put-alternate-contact \
  --account-id \$account_id \
  --alternate-contact-type=SECURITY \
  --email-address=security-contact@example.com \
  --phone-number="+1(555)555-5555" \
  --title="Security Contact" \
  --name="Mary Major"
echo 'Done putting security contact for account '\$account_id'.'

EOF
chmod 755 put-security-contact.sh

前述の loop-through-accounts.sh から受け取った AWS アカウント ID を元に代替の連絡先を登録するシェルスクリプトです。

登録の実行

上記2ファイルの準備が終わったら、loop-through-accounts.sh を実行して登録します。

./loop-through-accounts.sh

登録手順(管理アカウント)

管理アカウントに代替の連絡先を登録するためには下記のコマンドを実行します。
メンバーアカウントと同様に、Email アドレス・電話番号・役職・氏名を適切なものに変更したあと実行してください。

aws account put-alternate-contact \
  --alternate-contact-type=SECURITY \
  --email-address=security-contact@example.com \
  --phone-number="+1(555)555-5555" \
  --title="Security Contact" \
  --name="Mary Major"

メンバーアカウント用のコマンドとの差異は --account-id オプションの有無にあります。
代替の連絡先を登録するオペレーションには Standalone contextOrganizations contextの2種類があり、--account-id オプションでアカウント ID を指定しない場合 Standalone context として実行されます。
管理アカウントの代替の連絡先はこの Standalone context でのみ登録可能であるため、loop-through-accounts.sh では明示的に管理アカウントの ID を除外していました。
両 context の詳細については下記ドキュメントを参照してください。

Adding or updating the primary and alternate contact information - AWS Account Management

登録の確認

せっかくなので GetAlternateContact API を利用して各アカウントにセキュリティコンタクトが正しく登録されているか確認してみましょう。
下記のコマンドを CloudShell で実行してシェルスクリプトの作成・パーミッションの変更を実施します。

cat << EOF > check-security-contact.sh
#! /bin/bash
    managementaccount=\`aws organizations describe-organization --query Organization.MasterAccountId --output text\`
    for account in \$(aws organizations list-accounts --query 'Accounts[].Id' --output text); do
            echo AWS Account ID: \$account

            if [ "\$managementaccount" -eq "\$account" ]
                     then
                         aws account get-alternate-contact --alternate-contact-type=SECURITY
                         continue
            fi
            aws account get-alternate-contact --account-id \$account --alternate-contact-type=SECURITY
            sleep 0.2
    done
EOF
chmod 755 check-security-contact.sh

作成したシェルスクリプトを実行します。

$ ./check-security-contact.sh 
AWS Account ID: 111111111111
{
    "AlternateContact": {
        "AlternateContactType": "SECURITY",
        "EmailAddress": "security-contact@example.com",
        "Name": "xiyegen",
        "PhoneNumber": "+81(0X0)XXXX-XXXX",
        "Title": "Security Contact"
    }
}
AWS Account ID: 222222222222
{
    "AlternateContact": {
        "AlternateContactType": "SECURITY",
        "EmailAddress": "security-contact@example.com",
        "Name": "xiyegen",
        "PhoneNumber": "+81(0X0)XXXX-XXXX",
        "Title": "Security Contact"
    }
}
AWS Account ID: 333333333333
{
    "AlternateContact": {
        "AlternateContactType": "SECURITY",
        "EmailAddress": "security-contact@example.com",
        "Name": "xiyegen",
        "PhoneNumber": "+81(0X0)XXXX-XXXX",
        "Title": "Security Contact"
    }
}
(省略)

同一の代替の連絡先がすべてのアカウントに登録できていることを確認できました。

参考

2021/09/30 - AWS Account - 3 new api methods
Programmatically managing alternate contacts on member accounts with AWS Organizations

Lightsail ロードバランサー向けのエイリアスレコードを Route 53 で作成する

本日7月7日はクラスメソッド株式会社の創立記念日です。
なので(?)私もブログを書いてみます。

結論

問題

f:id:xiyegen:20210707190953p:plain

Lightsail でロードバランサーを作成すると、Lightsail 専用のコンソールから DNS 名を確認できます。

f:id:xiyegen:20210707193204p:plain

このロードバランサーに対してエイリアスレコードを設定しようとしてもロードバランサーの一覧に出てきません。
Lightsail で作成したロードバランサーはカスタマーのアカウントの外にあるからですね。
ではどうするか。

解決方法

f:id:xiyegen:20210707191924p:plain

一覧の中にあるロードバランサーを選択するのではなく、ロードバランサーDNS 名を直接入力してください。
このようにすればレコードが作成可能です。
DNS 名は先述のとおり Lightsail コンソールから確認可能です。

参考資料

Lightsail のドキュメントをちゃんと読んでみるとこの方法が書いてありました。

enter or paste the endpoint URL (i.e., DNS name) of your Lightsail load balancer.

Point your domain to your Lightsail load balancer | Lightsail Documentation

子猫のこの先の成長をざっくり把握したかった

5月の2週目に足立区の保護主さんから子猫を譲り受けた。
そいつ(後にピータンと名付けられる)が我が家の一員となってからは毎日かかさず体重を量り Google スプレッドシートに記入している。

やりたいことができた

2ヶ月ほどで初日の3倍程度に成長したので今後どうなるかをざっくり見てみたいと思った。
「傾き」「切片」「最小二乗法」などの言葉がかすかに脳裏をよぎったものの、結論としては Google スプレッドシートの標準機能であっけないほど簡単に実現できてしまった。

やり方

f:id:xiyegen:20210705231312p:plain 対象のグラフを選択した後、
① グラフ右上の縦三点リーダーをクリックする。
② 「グラフを編集」をクリックする。

f:id:xiyegen:20210705231318p:plain
③ グラフエディタ上部の「カスタマイズ」をクリックする。
④ 「系列」をクリックする。
⑤ 系列メニュー内の「トレンドライン」にチェックを入れる。

ただこれだけ。

f:id:xiyegen:20210705232227p:plain

元のグラフに回帰直線が追加されている。
このまま順調に成長すれば3週間後には 2500g を超えそうだ。

参考資料

トレンドラインを追加、編集する - パソコン - ドキュメント エディタ ヘルプ
(Gスプレッドシート)グラフに近似直線(曲線)・近似式・決定係数を表示する - いきなり答える備忘録

GitLab / GitHub でそれぞれ SSH 鍵を使い分けるために準備したこと

現在の勤務先ではコードリポジトリ・CI/CD ツールとして GitLab を使っています。
プライベートのお勉強用に GitHub を使ってみようとおもったので、それぞれ別の SSH 鍵を使うように設定してみました。

実行環境

設定

GitHubSSH 鍵の作成

GitLab 用の SSH 鍵として ~/.ssh/id_rsa(.pub) を作成済みなので、GitHub Docs の手順にしたがい新たに使用するキーのみ作成します。

Generating a new SSH key and adding it to the ssh-agent

$ ssh-keygen -t ed25519 -C "your_email@example.com"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/Users/<User名>/.ssh/id_ed25519): /Users/<User名>/.sshid_ed25519.github.com ※ GitHub 用とわかりやすくするためにファイルを変更してます。
Enter passphrase (empty for no passphrase): ※パスフレーズを入力
Enter same passphrase again: ※パスフレーズを再入力

GitHub への SSH 公開鍵登録

続いて、作成した SSH 公開鍵を GitHub アカウントに登録します。
下記コマンドで SSH 公開鍵をクリップボードにコピーします。

$ pbcopy < ~/.ssh/id_ed25519.pub.github.com

GitHub 画面右上部のアイコン → Settings → SSH and GPG keys に飛んだあと、* New SSH key * をクリックしてから Key 欄に SSH 公開鍵をペーストします。
GitHub Docs にはスクリーンショット付きの手順があるのでわかりやすいです。
Adding a new SSH key to your GitHub account

~/.ssh/config の修正

接続先が GitHub の場合にのみ今回作成した SSH 鍵を使うよう、~/.ssh/config に下記の設定を追記します。

Host github.com
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ~/.ssh/id_ed25519.github.com

動作確認

下記のコマンドを入力し GitHub への SSH 接続 を試します。

% ssh -T git@github.com
Enter passphrase for key '/Users/<User名>/.ssh/id_ed25519.github.com': ※ SSH 鍵生成時に設定したパスフレーズを入力
Hi xiyegen! You've successfully authenticated, but GitHub does not provide shell access.

作成済みのリポジトリの git clone も試してみます。

$ git clone git@github.com:xiyegen/git-lesson.git
Cloning into 'git-lesson'...
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (4/4), done.

ちゃんと動いたようです。