一年前のGoCon Kyotoの発表資料をどこにも載せていなかったので、書いておきます。
AWS Secrets Managerから秘匿値を取得して環境変数を設定しコマンドを実行するツールを作った
envchainのバックエンドにAWS Secrets Managerを使ったようなツールを作った。
使い方
Secrets Managerに秘匿値を設定した上で
$ aws secretsmanager get-secret-value --secret-id foo/bar { ... "SecretString": "BAZ", ... $ aws secretsmanager get-secret-value --secret-id foo/zoo # JSON secret { ... "SecretString": "{\"TOKEN\":\"AAA\",\"SECRET\":\"BBB\"}", ...
sevの設定ファル(~/.sev.toml
)を作成し
[hello] BAR = "secretsmanager://foo/bar" ZOO = "secretsmanager://foo/zoo:TOKEN" BAZ = "BAZBAZBAZ" [world] HOGE = "secretsmanager://foo/zoo:SECRET" FOGA = "secretsmanager://foo/bar" PIYO = "PIYOPIYOPIYO"
sevでラップしてプロファイル名を指定してコマンドを実行すると、秘匿値が環境変数経由でコマンドに渡される。
$ sev hello -- env FOO=BAZ ZOO=AAA BAZ=BAZBAZBAZ $ sev world -- env HOGE=BBB FUGA=BAZ PIYO=PIYOPIYOPIYO
設定ファイルにAWS_PROFILEを指定しておくと、デフォルトではそのAWS_PROFILEを使ってSecrets Manager APIを呼び出す。
[hello] AWS_PROFILE = "my-profile" BAR = "secretsmanager://foo/bar" # foo/barの値の取得にmy-profileが使われる
aws_iam_policy_documentを使うとplanの差分が大きくなるパターン
aws_iam_policy_documentを使っていると、terraform planを実行したときに差分が大きくて変更がわかりにくくなることがあったので検証してみた。
具体的には以下のようなパターンで差分が大きくなった。
まず以下のようなtfがあったとして
resource "aws_cloudwatch_log_group" "my-group" { name = "my-group" } resource "aws_iam_role" "log-writer" { name = "log-writer" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "lambda.amazonaws.com" } }, ] }) } data "aws_iam_policy_document" "put-log" { statement { actions = [ "logs:CreateLogStream", "logs:DescribeLogStreams", "logs:PutLogEvents", "logs:GetLogEvents", ] resources = [ # 普通、aws_cloudwatch_log_group.my-group.arn を使うが、説明のために便宜上 "arn:aws:logs:ap-northeast-1:XXX:log-group:${aws_cloudwatch_log_group.my-group.name}:*", ] } } resource "aws_iam_role_policy" "put-log" { role = aws_iam_role.log-writer.name name = "put-log" policy = data.aws_iam_policy_document.put-log.json }
aws_cloudwatch_log_group.my-groupのname属性を変更する。
diff --git a/terraform.tf b/terraform.tf index cea67e4..d806f57 100644 --- a/terraform.tf +++ b/terraform.tf @@ -14,7 +14,7 @@ provider "aws" { } resource "aws_cloudwatch_log_group" "my-group" { - name = "my-group" + name = "my-group2" } resource "aws_iam_role" "log-writer" {
terraform planの差分は以下のようになる。
# aws_iam_role_policy.put-log will be updated in-place ~ resource "aws_iam_role_policy" "put-log" { id = "log-writer:put-log" name = "put-log" ~ policy = jsonencode( { - Statement = [ - { - Action = [ - "logs:CreateLogStream", - "logs:DescribeLogStreams", - "logs:PutLogEvents", - "logs:GetLogEvents", ] - Effect = "Allow" - Resource = "arn:aws:logs:ap-northeast-1:XXX:log-group:my-group:*" }, ] - Version = "2012-10-17" } ) -> (known after apply) # (2 unchanged attributes hidden) }
これをjsonencodeで直接ポリシーを指定するとポリシーの差分だけ表示される。
resource "aws_iam_role_policy" "put-log" { role = aws_iam_role.log-writer.name name = "put-log" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = [ "logs:CreateLogStream", "logs:DescribeLogStreams", "logs:PutLogEvents", "logs:GetLogEvents", ] Effect = "Allow" Resource = "arn:aws:logs:ap-northeast-1:XXX:log-group:${aws_cloudwatch_log_group.my-group.name}:*" }, ] }) }
# aws_iam_role_policy.put-log will be updated in-place ~ resource "aws_iam_role_policy" "put-log" { id = "log-writer:put-log" name = "put-log" ~ policy = jsonencode( ~ { ~ Statement = [ ~ { ~ Resource = "arn:aws:logs:ap-northeast-1:XXX:log-group:my-group:*" -> "arn:aws:logs:ap-northeast-1:XXX:log-group:my-group2:*" # (2 unchanged attributes hidden) }, ] # (1 unchanged attribute hidden) } ) # (2 unchanged attributes hidden) }
name属性のようにリソースの変更前に値がわかっているようなものだと、同様の現象が発生しそう。
追記
id:MIZZY さんにコメントをいただいたので、aws_cloudwatch_log_group.my-group.name をlocal変数に格納してみた。
resource "aws_cloudwatch_log_group" "my-group" { name = "my-group2" } locals { my_group_name = aws_cloudwatch_log_group.my-group.name } data "aws_iam_policy_document" "put-log" { statement { actions = [ "logs:CreateLogStream", "logs:DescribeLogStreams", "logs:PutLogEvents", "logs:GetLogEvents", ] resources = [ "arn:aws:logs:ap-northeast-1:822997939312:log-group:${local.my_group_name}:*", ] } }
いったんlocal変数に格納すると、差分が最小限になる。
メモ
レビューアの立場で問題のスコープを広げすぎないように注意する
ファイルの先頭と末尾の空行を削除するだけのCLIを作った
もうsedのコマンドの検索はしないぞ github.com
プルリクエストが承認+テストをパスしたら通知するmacOSアプリを作った
プルリクエストがマージ可能になったらいち早く知りたいので、承認+テストをパスしたら通知を送るmacOSのアプリを作った。
approve不要なPRについてはテストが完了した時点で通知がくる(はず)。 テストがこけても通知が来る。rejectされても通知が来る。要はステータスが確定したら通知が来る。
Notificationsをみてもいいんだけど、approveやテスト以外の通知もくるので専用に作ってみた。 Notifications全般の通知を受け取りたいならGitifyを使った方がいいと思う。
通知はこんな感じ。
メニューバーのアイコンをクリックするとステータスが確定したPRの一覧が出る。
あとステータスが確定したPRがないときは、メニューバーのアイコンが黒に変わる。
設定画面で検索条件を変えられるので、特定のorgだけにするとか、一部orgを除外するとかできる。
実装
以下のようなGraphQLを実行して、PR一覧とテストのステータスをまとめて取得している。
https://github.com/winebarrel/Succ/blob/main/Succ/Github/SearchPullRequests.graphql
query SearchPullRequests($query: String!) { search(type: ISSUE, last: 100, query: $query) { nodes { ... on PullRequest { repository { name owner { login } } title url reviewDecision commits(last: 1) { nodes { commit { url statusCheckRollup { state } } } } } } } }
取得したPRのステータスを見て通知するかどうかを判断。
https://github.com/winebarrel/Succ/blob/main/Succ/PullRequest.swift
private func updateNodes(_ value: GraphQLResult<Github.SearchPullRequestsQuery.Data>) { var fetchedNodes: Nodes = [] value.data?.search.nodes?.forEach { body in if let pull = body?.asPullRequest { let reviewDecision = pull.reviewDecision if reviewDecision != nil && reviewDecision != .approved && reviewDecision != .changesRequested { return } guard let commit = pull.commits.nodes?.first??.commit else { return } guard let state = commit.statusCheckRollup?.state else { return } if state != .success && state != .failure && state != .error { return } let node = Node( owner: pull.repository.owner.login, repo: pull.repository.name, title: pull.title, url: pull.url, reviewDecision: reviewDecision?.rawValue ?? "", state: state.rawValue, commitUrl: commit.url, success: (reviewDecision == nil || reviewDecision == .approved) && state == .success ) fetchedNodes.append(node) } }