この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
今回はAMG(Amazon Managed Grafana)がGrafana APIトークンを作成するAPIをサポートしました、というアップデートをご紹介します。
何が嬉しいの?
一言でいうと、Grafanaワークスペースコンソール内のGrafanaリソース(データソース、ダッシュボード、フォルダーなど)のIaC化がやりやすくなりました。
例えばTerraformで上記のIaC化を行なうとします。Grafana Providerが必要です。
プロビジョニング先のGrafana Server(AMGだとworkspaceと呼ぶ)を指定するためにurl
attributeを使い、また認証のためにauth
attributeにAPIトークンを設定します。
provider "grafana" {
url = "http://grafana.example.com/"
auth = var.grafana_auth
}
アップデート以前
上記url
については、AMGの場合、aws_grafana_workspace
を使ってworkspaceを作成し、そのreference attributeのendpoint
を参照すればOKです。
一方、auth
に設定すべきAPIトークンは、一度Grafanaワークスペースコンソールにログインして、その中で作成する必要があります。 なのでコードだけで完結できないんですよね。ここが問題です。
※ 以下は、AMGのワークスペース作成時に認証アクセスでAWS SSOを選んだ場合のAPIトークン作成のフローです。SAMLを選んでいた場合については私が把握できてないので今回は割愛させていただきますが、大体同じなのではと思います。
- ワークスペースの詳細ページに行き、AMG アプリケーションにログインできるユーザー(グループ)を設定します。
- 設定したユーザー(グループ)のタイプを「管理者(Admin)」に変更します。
- ログインできるようにしたユーザーでSSOコンソールに行くと、AMGアプリケーションのパネルが表示されるので、クリックしてログインします。
- 以下画像のようにConfiguration→API Keysに遷移します。
- API Keyを作成します。RoleはAdminに設定します。
- API Keyが作成されます。これをコピペして
auth
attributeに設定します。VCSにKey値が残るのが嫌なので、Parameter Store等を経由するのがいいでしょう。
resource "aws_grafana_workspace" "sample" {
account_access_type = "CURRENT_ACCOUNT"
authentication_providers = ["AWS_SSO"]
permission_type = "SERVICE_MANAGED"
name = "sample"
description = "sample"
role_arn = aws_iam_role.workspace.arn
data_sources = ["PROMETHEUS"]
}
data "aws_ssm_parameter" "grafana_api_key" {
name = "sample-amg-api-key"
}
provider "grafana" {
url = "https://${aws_grafana_workspace.sample.endpoint}"
auth = data.aws_ssm_parameter.grafana_api_key.value
}
これでGrafanaリソースのプロビジョニングができるようになります。以下はAMP(Amazon Managed Service for Prometheus)をデータソースとして設定している例です。
resource "aws_prometheus_workspace" "sample" {
alias = "sample"
}
data "aws_region" "current" {}
resource "grafana_data_source" "prometheus" {
type = "prometheus"
name = "amp"
is_default = true
url = aws_prometheus_workspace.sample.prometheus_endpoint
json_data {
http_method = "POST"
sigv4_auth = true
sigv4_auth_type = "workspace-iam-role"
sigv4_region = data.aws_region.current.name
}
}
先程問題点として、「Grafana providerの設定に必要なAPIトークンを取得するためには、一度Grafanaワークスペースコンソールにログインして、その中で作成する必要があること」を挙げました。「まあ、最初一回ブラウザポチポチ作業が必要なだけでしょ?そんなに問題じゃなくないか?」と思われるかもしれません。そうじゃないんです。このAPIトークン、最大でも30日で期限切れするんです。 期限切れした場合、terraform planやapplyで以下のようなエラーになります。
Error: status: 401, body: {"message":"Expired API key"}
というわけで、30日ごとにコンソールにログインして、API Keyを作成し直す という作業が発生します。これが地味に辛い。
アップデート後
今回のアップデートで、このトークンをコンソール内で作成する以外に、AWSのAPIでも作成することができるようになりました。なのでもうコンソールログインが不要になります🙌
現在のところ、Terraform AWS providerではこのアップデートのサポートは無いようです。(issueはあったのでそのうちされそう)
AWS CLIは対応済なので、これをうまくTerraformの世界に取り込みましょう。
やってみた
AWS CLI アップデート
まずは、追加された前述のコマンドaws grafana create-workspace-api-key
が使えるように、AWS CLIをアップデートします。
% aws --version
aws-cli/2.4.22 Python/3.8.8 Darwin/20.6.0 exe/x86_64 prompt/off
% curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target /
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 27.6M 100 27.6M 0 0 30.3M 0 --:--:-- --:--:-- --:--:-- 30.2M
Password:
installer: Package name is AWS Command Line Interface
installer: Upgrading at base path /
installer: The upgrade was successful.
% aws --version
aws-cli/2.7.6 Python/3.9.11 Darwin/20.6.0 exe/x86_64 prompt/off
providerをインストール
External Data Sourceを使って、aws grafana create-workspace-api-key
の結果をTerraformの世界に取り込もうと思います。ですので、まずはexternal providerを追加します。
terraform {
required_version = "~> 1.2.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.9"
}
grafana = {
source = "grafana/grafana"
version = "1.23.0"
}
+ external = {
+ source = "hashicorp/external"
+ version = "2.2.2"
+ }
}
この後忘れず terraform init
しましょう。
失敗
以下のようにExternal Data Sourceを使いました。が、エラーになりました。残念。
data "external" "create_amg_workspace_api_key" {
program = [
"aws",
"grafana",
"create-workspace-api-key",
"--key-name", "for-terraform-${formatdate("YYMMDDhhmmss",timestamp())}",
"--key-role", "ADMIN",
"--seconds-to-live", "600",
"--workspace-id", split(".",aws_grafana_workspace.sample.endpoint)[0],
]
}
provider "grafana" {
url = "https://${aws_grafana_workspace.sample.endpoint}"
auth = data.external.create_amg_workspace_api_key.result.key
}
apply(plan)の結果
╷
│ Error: status: 401, body: {"message":"Unauthorized"}
│
│
│ with grafana_data_source.prometheus,
│ on grafana.tf line 25, in resource "grafana_data_source" "prometheus":
│ 25: resource "grafana_data_source" "prometheus" {
│
╵
ポイントはkey-nameの部分をtimestamp関数を使って動的にしている点です。このaws grafana create-workspace-api-key
コマンドを使うとその都度新しいAPI Keyが作成されます。すでに存在しているkey名を指定してしまうと以下のエラーになります。
│ An error occurred (ConflictException) when calling the CreateWorkspaceApiKey operation: Api key with
│ name hoge already exists.
というわけでtimestamp関数を使って、key-nameが既存のものと重複しないようにしています。
なのですが、どうやらこのtimestamp関数をExternal Data Source内で使うと先程の401エラーになってしまうようです… 解決方法がわからないのでこの方法は諦めました。
修正→成功
方針変更して、
- Terraform外部でkey-nameを定義
- 変数として1をTerraformに取り込む
というやり方をやってみました。
variable "api_key_suffix" {
type = string
description = "suffix of amg workspace api key name"
}
data "external" "create_amg_workspace_api_key" {
program = [
"aws",
"grafana",
"create-workspace-api-key",
"--key-name", "for-terraform-${var.api_key_suffix}",
"--key-role", "ADMIN",
"--seconds-to-live", "600",
"--workspace-id", split(".",aws_grafana_workspace.sample.endpoint)[0],
]
}
provider "grafana" {
url = "https://${aws_grafana_workspace.sample.endpoint}"
auth = data.external.create_amg_workspace_api_key.result.key
}
そしてplanやapplyの際に変数を指定します。
% terraform apply -var "api_key_suffix=$(date '+%y%m%d%k%M%S')"
毎回このコマンドを叩くのは面倒なのでスクリプト化するのがよいでしょう。
改良
前項の方法で動作するようにはなったのですが、ちょっとイマイチです。
なんとかならないものかなーと考えていたところ、現在お世話になっているお客様👨💼に教えていただきました。
- 👨💼「
delete-workspace-api-key
コマンド、ありますよ」 - 🤡「なんですと…」
実装済みのコードも共有いただきました。要はdelete-workspace-api-key
で既存のkeyを削除してから、同じ名前でkeyを作り直すやり方です。
./bash/create-workspace-api-key.sh
set -eu
eval "$(jq -r '@sh "WORKSPACE_ID=\(.workspace_id)"')"
aws grafana delete-workspace-api-key --key-name terraform_apikey --workspace-id ${WORKSPACE_ID} > /dev/null || true
API_KEY=$(aws grafana create-workspace-api-key --key-name terraform_apikey --key-role ADMIN --seconds-to-live 86400 --workspace-id ${WORKSPACE_ID} --query key --output text)
echo "{\"apikey\": \"${API_KEY}\"}"
exit 0
data "external" "grafana_apikey" {
program = ["bash", "${path.module}/bash/create-workspace-api-key.sh"]
query = {
workspace_id = split(".",aws_grafana_workspace.sample.endpoint)[0]
}
}
provider "grafana" {
url = "https://${aws_grafana_workspace.sample.endpoint}"
auth = data.external.grafana_apikey.result.apikey
}
この方法なら先程挙げたイマイチな点2つ両方とも解消できていますね!
まとめ
AMGが、Grafana APIトークンを作成するAPIをサポートしましたというアップデートのご紹介でした。AMGとTerraform両方併せて使われている方にはかなり嬉しいアップデートではないでしょうか。ぜひ導入をご検討ください。