SSM SessionManagerのログをS3 bucket/CloudWatchLogに保存する

SSM SessionManagerでのコンソールの操作ログをS3やCloudWatchLogsに保存できるということなので試してみた。Terraformで設定。

1. IAMrole / Instance Profileを作成

ec2インスタンスにインストールされているSSMエージェントがログを書き込むので、インスタンスに権限を付与してあげる必要がある。こちらのドキュメントを参考に設定。今回は AmazonSSMManagedInstanceCore に権限を追加してみた。Terraformのコードはこんな感じ。

data "aws_iam_policy" "amazon_ssm_managed_instance_core" {
    arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

data "aws_iam_policy_document" "ec2_ssm_test_policy_doc" {
    source_json = data.aws_iam_policy.amazon_ssm_managed_instance_core.policy

    statement {
        effect    = "Allow"
        resources = ["*"]

        actions = [
            "s3:PutObject",
            "s3:GetEncryptionConfiguration",
            "logs:PutLogEvents",
            "logs:CreateLogStream",
            "logs:DescribeLogGroups",
            "logs:DescribeLogStreams",
            "ssm:GetParameter",
            "ssm:GetParameters",
            "ssm:GetParametersByPath",
            "kms:Decrypt",
            "kms:GenerateDataKey",
        ]
    }
}

resource "aws_iam_policy" "ec2_ssm_test_policy" {
    name = "ec2-ssm-test-policy"
    policy = data.aws_iam_policy_document.ec2_ssm_test_policy_doc.json
}

resource "aws_iam_role" "ec2_ssm_test_role" {
    name = "ec2-ssm-test-role"

    assume_role_policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "sts:AssumeRole",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Effect": "Allow",
            "Sid": ""
        }
    ]
}
EOF

    tags = {
        Name = "ec2-ssm-test-role"
    }
}


resource "aws_iam_role_policy_attachment" "ssm_role_attachment" {
    role       = aws_iam_role.ec2_ssm_test_role.name
    policy_arn = aws_iam_policy.ec2_ssm_test_policy.arn
}

resource "aws_iam_instance_profile" "ec2_ssm_test_profile" {
    name = "ec2-ssm-test-profile"
    role = aws_iam_role.ec2_ssm_test_role.name
}

2. 作成したinstance profileをアタッチしたec2インスタンスを作成

amazon linux2を利用*1。Terraformのコードはこんな感じ。VPCとかSubnet、SecurityGroupは適宜設定。

data "aws_ssm_parameter" "amzn2_ami" {
    name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
}

resource "aws_instance" "ssm_test_server" {
    ami                  = data.aws_ssm_parameter.amzn2_ami.value
    instance_type        = "t2.micro"
    subnet_id            = aws_subnet.ec2_ssm_test_private_subnet.id
    iam_instance_profile = aws_iam_instance_profile.ec2_ssm_test_profile.name

    security_groups = [
        aws_security_group.allow_http.id,
    ]

    tags = {
        Name = "ssm-test-server"
    }

    user_data = <<EOF
#!/bin/bash
yum update -y
amazon-linux-extras install docker
usermod -aG docker ec2-user
systemctl enable docker
systemctl start docker
EOF
}

3. SSM SessionManager設定用Documentの作成

SSM SessionManagerの設定はSSM Documentで作成するようなので、terraformで作成。今回はテストなので暗号化はしない設定。ログ保存用のS3 bucketとCloudWatchLogsのLogGroupも一緒に作成。因みにDocumentの名前は "SSM-SessionManagerRunShell"にしておくとよい。session mamager使う時にデフォルトで読み込んでくれる。勿論名前変えて、CLIの引数で渡して指定することも可能*2

resource "aws_s3_bucket" "ec2_ssm_test_log_bucket" {
    bucket        = "ec2-ssm-test-log-bucket-xxx"
    force_destroy = true

    lifecycle_rule {
        enabled = true

        expiration {
            days = 3
        }
    }
}

resource "aws_cloudwatch_log_group" "ec2_ssm_test_log" {
    name              = "/ec2-ssm-test-log"
    retention_in_days = 3
}

resource "aws_ssm_document" "session_manager_run_shell" {
    name            = "SSM-SessionManagerRunShell"
    document_type   = "Session"
    document_format = "JSON"

    content = <<EOF
{
    "schemaVersion": "1.0",
    "description": "Document to hold regional settings for Session Manager",
    "sessionType": "Standard_Stream",
    "inputs": {
        "s3BucketName": "${aws_s3_bucket.ec2_ssm_test_log_bucket.id}",
        "s3EncryptionEnabled": false,
        "cloudWatchLogGroupName": "${aws_cloudwatch_log_group.ec2_ssm_test_log.name}",
        "cloudWatchEncryptionEnabled": false
    }
}
EOF
}

いざapplyと思って実行するとエラー。SSM-SessionManagerRunShell が存在するとのこと。どうもSSMを使ったことがあると自動で作成されている模様。消す*3かimportすればよさそう。今回はimportしちゃった。

$ terraform import --target aws_ssm_document.session_manager_run_shell SSH-SessionManagerRunShell
aws_ssm_document.session_manager_run_shell: Importing from ID "SSM-SessionManagerRunShell"...
aws_ssm_document.session_manager_run_shell: Import prepared!
  Prepared aws_ssm_document for import
aws_ssm_document.session_manager_run_shell: Refreshing state... [id=SSM-SessionManagerRunShell]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

無事importできた。その後applyすると無事適用できました。

$ terraform apply
...

4. 確認

  • ログはSessionを終了すると保存される模様
  • CloudWatch、S3ともに保存できてました。CloudWatchLogsだとこんな感じ。文字化けしてるけど、コンソールで実行したコマンド、その結果ちゃんと保存できてる。
Script started on 2020-10-18 13:32:11+0000
[?1034hsh-4.2$ 
[Ksh-4.2$ bash
]0;@ip-10-0-1-xxx:/usr/bin[?1034h[ssm-user@ip-10-0-1-xxx bin]$ sudo su - ec2-use
]0;ec2-user@ip-10-0-1-xxx:~[ec2-user@ip-10-0-1-xxx ~]$ uname -a
Linux ip-10-0-1-xxx.ap-northeast-1.compute.internal 4.14.193-149.317.amzn2.x86_64 #1 SMP Thu Sep 3 19:04:44 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
]0;ec2-user@ip-10-0-1-xxx:~[ec2-user@ip-10-0-1-xxx ~]$ hostname
ip-10-0-1-xxx.ap-northeast-1.compute.internal
]0;ec2-user@ip-10-0-1-xxx:~[ec2-user@ip-10-0-1-xxx ~]$ uptime
 13:26:27 up 5 min,  0 users,  load average: 0.02, 0.12, 0.08
]0;ec2-user@ip-10-0-1-xxx:~[ec2-user@ip-10-0-1-xxx ~]$ whoami
ec2-user
]0;ec2-user@ip-10-0-1-xxx:~[ec2-user@ip-10-0-1-xxx ~]$ exit
logout
]0;@ip-10-0-1-xxx:/usr/bin[ssm-user@ip-10-0-1-xxx bin]$ exit
exit
sh-4.2$ exit
exit

Script done on 2020-10-18 13:32:11+0000

*1:ami idはSSM parameter storeから取得。参考

*2:試してないけど

*3:AWS consoleからの削除方法不明。CLI使えば消せそう

CodePipelineでCodeCommitとCodeBuildを利用してはまる

CodePipeline、CodeCommit、CodeBuildで遊んでみたのですが、CodePipeline利用するときにハマったのでメモ。

前提

  • 最初CodePipelineを利用せずに、CodeCommitとCodeBuildでBuildできるか等々試していた
  • CodeBuildで利用してたbuildspec.ymlはこんな感じ
    • やっていることはdocker buildしてECRに突っ込んでいるだけ
    • イメージのタグとして、commit idを利用している
    • CodePipelineを使わない状況では上手く動いていた
version: 0.2

phases:
  pre_build:
    commands:
      - echo "Login to Amazon ECR"
      - aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com
      - IMAGE_TAG=$(git rev-parse --short HEAD)
      - echo "IMAGE_TAG=${IMAGE_TAG}"
  build:
    commands:
      - echo "Build started in $(date)"
      - echo "Building a docker image..."
      - docker build -t ${IMAGE_REPO_NAME}:${IMAGE_TAG} .
      - docker tag ${IMAGE_REPO_NAME}:${IMAGE_TAG} ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${IMAGE_REPO_NAME}:${IMAGE_TAG}
      - docker tag ${IMAGE_REPO_NAME}:${IMAGE_TAG} ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${IMAGE_REPO_NAME}:latest
  post_build:
    commands:
      - echo "Build completed in $(date)"
      - echo "Pushing the docker image..."
      - docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${IMAGE_REPO_NAME}:${IMAGE_TAG}
      - docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${IMAGE_REPO_NAME}:latest

問題

  • CodePipelineを利用して、Pipelineを作成

    • Source Stage --> CodeCommit
    • Build Stage --> CodeBuild
  • CodeBuildでエラー

[Container] Running command aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

[Container] Running command IMAGE_TAG=$(git rev-parse --short HEAD)
fatal: not a git repository (or any parent up to mount point /codebuild)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).

結果

  • いろいろ調べた結果、どうも source stageのartifactはgitリポジトリでなくなるらしい
  • .gitがなかった
  • じゃあcommit idどうやって取るの?と思って調べてみたら CODEBUILD_RESOLVED_SOURCE_VERSION が求めているものっぽいので、そちらを利用するように変更
version: 0.2

phases:
  pre_build:
    commands:
      - echo "Login to Amazon ECR"
      - aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com
      - COMMIT_HASH=$(echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH}
  build:
    commands:
      - echo "Build started in $(date)"
      - echo "Building a docker image..."
      - docker build -t ${IMAGE_REPO_NAME}:${IMAGE_TAG} .
      - docker tag ${IMAGE_REPO_NAME}:${IMAGE_TAG} ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${IMAGE_REPO_NAME}:${IMAGE_TAG}
      - docker tag ${IMAGE_REPO_NAME}:${IMAGE_TAG} ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${IMAGE_REPO_NAME}:latest
  post_build:
    commands:
      - echo "Build completed in $(date)"
      - echo "Pushing the docker image..."
      - docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${IMAGE_REPO_NAME}:${IMAGE_TAG}
      - docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${IMAGE_REPO_NAME}:latest

エラー出ずに動きましたとさ。

AWS managed policyを作成したIAMroleに紐づける

自分で作成したIAMpoliciesであれば、datasource利用してPolicy document作成し、aws_iam_role_policy_attachmentで紐づければ良いけど、AWS managedの場合どうするのがよいか調べたのでメモ。

方法1 - 直接ARNを書く

例えば AmazonSSMManagedInstanceCore (arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore) ポリシをIAMrole (my-test-role)に付与したい場合は下記な感じ。

resource "aws_iam_role" "my_test_role" {
    name = "my-test-role"
    ....
}

resource "aws_iam_role_policy_attachment" "my_test_role_pol_attachment" {
    role            = aws_iam_role.my_test_role.name
    policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

方法2 - data source を利用する

方法1同様、 AmazonSSMManagedInstanceCore (arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore) ポリシをIAMroleに付与したい場合は下記のようになる。

resource "aws_iam_role" "my_test_role" {
    name = "my-test-role"
    ....
}

data "aws_iam_policy" "AmazonSSMManagedInstanceCore" {
    arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

resource "aws_iam_role_policy_attachment" "my_test_role_pol_attachment" {
    role            = aws_iam_role.my_test_role.name
    policy_arn = data.aws_iam_policy.AmazonSSMManagedInstanceCore.arn
}

方法2の方がTerraform的な書き方ですかね。

参考

stackoverflow.com

Jiraの設定周りを整理

Jiraの設定をたまにするのだけど、ドキュメントさらっと読みつつ何となく設定して誤魔化してて、理解してやってないと良くないよねと思いまして、ドキュメントを読んでみました。絶対忘れるのでメモしておく。

今回下記内容をざっと理解。

issue type

  • Jiraは基本issueをチケット化して管理するんだけど、そのissueの種類
  • 例えば... Epic、Story、Task、Subtask、Bugとか
  • 階層化することが可能(デフォルトでは3階層)
    • Parent/Epic issues
    • (Standard) issues
    • Child issues (subtask)

issue type scheme

  • issueのセットみたいなもの
  • プロジェクトに割り当てて使う
    • 当該プロジェクトはissue type schemeに紐づけられているissue typeのみ使える
  • 用途:テンプレートとなるような共通のissueセットを作成しておいて、関連するプロジェクトでは同じものを使うとか?
    • スクラム用のissue type schemeとかデフォルトで用意されていたりする

workflow

  • issueが処理されていく流れ = 業務フローという感じ
    • 例:ToDo --> In progress --> Review --> Done
  • StatusとTransitionを定義
    • Status = issueの状態(上の例だとToDoとかIn progressとか)
    • Transition = あるStatusから別のStatusに移動できるようにするリンク
      • Status間の移動を定義できる
      • 例えば、Review StatusのチケットのみDoneに移動できるとか
  • GUI使って設定できる
  • Transitionに関連して色々できる
    • Triggers: リンクされてる外部ツールのイベントをトリガにしてStatusを変更したりすることが可能
    • Conditions: issueを移動できるユーザに条件つける
    • Validators: issue移動時に入力された内容が有効かチェック
    • post-functions: issue移動後に追加の処理を実施(Fieldのアップデートとか)

workflow scheme

  • workflowとissue typeを紐づける = 特定のissue typeのworkflowを設定する
  • issue type毎にworkflowを設定することが可能
  • workflow schemeをプロジェクトに割り当てる
    • issue typeでプロジェクトで利用するissueの種類を決める
    • workflow schemeでissue type毎のworkflowを決める

screen

  • (Issueの操作で表示する)画面を定義*1
    • こんな画面と定義する
  • fieldの集合

screen sheme

  • 画面の定義とissueの操作(issueの作成、編集、表示)と画面を紐づけ
    • issueのこの操作ではこの画面を表示すると定義
  • issueの操作毎に画面を変えることが可能

issue type screen scheme

  • screen shemeとissue typeを紐づける
    • このissue typeでは、このscreen schmeを利用する(この操作でこの画面を表示する)と定義
  • issue type screen schemeをプロジェクトに割り当てる
    • issue typeでプロジェクトで利用するissueの種類を決める
    • workflow schemeでissue type毎のworkflowを決める
    • issue type screen schemeでissue type毎に各種操作で表示する画面を決める

field configuration

  • screenと重複している感じ
  • field視点で、表示する/しないを設定することが可能
  • fieldとscreenの紐づけをここで変更することが可能だけど、これはfield configurationローカルの変更ではなくて、Globalな変更となる。要はここで割り当てを変更すると、他のfield configurationの部分にも影響がでる
    • とても分かり難い挙動。なんでこれだけglobal?
  • 正直screen主体で考えた方が分かり易い気がした

field configuration scheme

  • fieldとissue typeを紐づけ
    • このissue typeではこのfieldを表示するみたいな感じ
  • field configuration schemeをプロジェクトに割り当て
    • issue typeでプロジェクトで利用するissueの種類を決める
    • workflow schemeでissue type毎のworkflowを決める
    • issue type screen schemeでissue type毎に各種操作で表示する画面を決める
    • field configuration schemeでissue type毎に表示するfieldを決める
  • 結局画面で表示されるのは、screenの設定とfieldの設定のAND

プロジェクトとscreen/workflow/field schemeの関係

たぶんこのドキュメントが一番分かり易いからこれを読めばOK

*1:issueの操作との紐づけはここではしない

pipでのパッケージのインストール先

pip使ってpythonのパッケージをインストールするときに、dnfやapt-getのようにsudoを付けて実行すると警告が表示されますね。

$ sudo pip3 install <package>
WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead.

--userオプション使えということですが、--user使うとどこにインストールされるのか気になったので調べてみました。

まず、helpから調べてみる。

$ pip3 install --help
...
  --user                      Install to the Python user install directory for
                              your platform. Typically ~/.local/, or
                              %APPDATA%\Python on Windows. (See the Python
                              documentation for site.USER_BASE for full
                              details.)

通常、~/.local/ との事だけど、Pythonドキュメントsite.USER_BASEを読めとあるので探してみる。

site.USER_BASE
Path to the base directory for the user site-packages. Can be None if getuserbase() hasn’t been called yet. Default value is ~/.local for UNIX and Mac OS X non-framework builds, ~/Library/Python/X.Y for Mac framework builds, and %APPDATA%\Python for Windows. This value is used by Distutils to compute the installation directories for scripts, data files, Python modules, etc. for the user installation scheme. See also PYTHONUSERBASE.

site-packagesのベースディレクトリへのパスで、distutilsがパッケージとかをインストールするときに利用すると。通常は~/.localとやはり書いてある。ついでにPYTHONUSERBASEPythonのドキュメントで調べてみる。

PYTHONUSERBASE
Defines the user base directory, which is used to compute the path of the user site-packages directory and Distutils installation paths for python setup.py install --user.

site-packagesのディレクトリを設定できる環境変数のようだ。インストール先を変えたい場合、これを変更すればよさそう。

ちなみにsite-packagesとかuser site-packagesのお話は、PEP370に書いてある。

user site directory

A site directory inside the users' home directory. A user site directory is specific to a Python version. The path contains the version number (major and minor only).

AWS Certified Solution Architect Associate受験

年末にCloud Practitionerに合格したので、お正月休みに少し勉強してSolution Architect Associateを受験してみた。

やったこと

  • 黒本ざっと読んで章末問題を2回ほど解く。

book.impress.co.jp

  • A Cloud GuruのCertified Solution Architect AssociateコースのQuizを3回くらい。

結果

合格しました

所感

  • Cloud Practitionerでざっと覚えたばかりなので、特に苦労なく
  • 当然と言えば当然なんだけどアーキテクチャ的な内容を問う問題が多く、そういうのは問題文が長いので読むの面倒だった
    • 特にCloudGuruで英語読むの。。。

AWS Certified Cloud Practitioner 受験

AWS Certified DevOps Engineer Professionalを取るよう言われてるけど、まずはということでCloud Practitionerの試験を受験してみた。 お勉強は基本こちらの本。知識としては業務でAWS使い始めて4、5ヵ月というレベルです。

Amazon CAPTCHA

やったことは本読んで、問題解いて、問題だけもう一回やって、A Cloud GuruでQuizを解いて練習という感じでした。 いかんせん本買ってからモチベーションが維持できずだらだらと3、4ヵ月たってしまったのが反省点。

無事合格。