Amazon Managed Grafana ワークスペースの作成と SAML の構成を AWS CDK で行ってみた

2023.09.01

こんにちは、CX 事業本部 Delivery 部の若槻です。

Amazon Managed Grafana は、オープンソース分析プラットフォームである Grafana のフルマネージドサービスです。

Amazon Managed Grafana を使用する場合には、ワークスペースの作成および認証(SAML または IAM Identity Center)の構成を行う必要があるのですが、せっかくなので IaC で管理したいですね。

そこで今回は、Amazon Managed Grafana ワークスペースの作成および SAML の構成を AWS CDK で行ってみました。

やってみた

今回はワークスペースの認証を SAML で構成しますが、その場合は CDK デプロイを 2 回に分けて行う必要があります。

IAM ロール、Grafana ワークスペースの作成

まず、1 回目のデプロイではワークスペースが AsusumeRole をするための IAM ロール、および Grafana ワークスペースの作成を行います。Grafana は現在は L1 Construct のみの提供となります。

lib/cdk-sample-stack.ts

import {
  aws_grafana,
  aws_iam,
  Stack,
  StackProps,
  CfnOutput,
} from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class CdkSampleStack extends Stack {
  public readonly myFileObjectKey: string;

  constructor(scope: Construct, id: string, props: StackProps) {
    super(scope, id, props);

    // Grafana Workspace 用の IAM Role を作成
    const principal = new aws_iam.ServicePrincipal(
      'grafana.amazonaws.com'
    ).withConditions({
      StringEquals: {
        'aws:SourceAccount': this.account,
      },
      StringLike: {
        'aws:SourceArn': `arn:aws:grafana:${this.region}:${this.account}:/workspaces/*`,
      },
    });
    const grafanaRole = new aws_iam.Role(this, 'GrafanaRole', {
      assumedBy: principal,
    });

    // Grafana Workspace を作成
    const cfnWorkspace = new aws_grafana.CfnWorkspace(this, 'Workspace', {
      accountAccessType: 'CURRENT_ACCOUNT',
      authenticationProviders: ['SAML'],
      permissionType: 'SERVICE_MANAGED',
      roleArn: grafanaRole.roleArn,
    });

    const workspaceId = cfnWorkspace.ref;

    // Service provider identifier (Entity ID) の出力
    new CfnOutput(this, 'WorkspaceServiceProviderIdentifier', {
      value: `https://${workspaceId}.grafana-workspace.${this.region}.amazonaws.com/saml/metadata`,
    });

    // Service provider reply URL (Assertion consumer service URL) の出力
    new CfnOutput(this, 'WorkspaceServiceProviderReplyUrl', {
      value: `https://${workspaceId}.grafana-workspace.${this.region}.amazonaws.com/saml/acs`,
    });
  }
}

ロールの作成部分については、マネジメントコンソールからのワークスペース作成時に自動作成されるロールと同等の Condition を設定したかったので、以下を参考に設定しています。

また、SAML の構成時に指定が必要な Service provider identifier および Service provider reply URL を出力しています。

SAML の構成(IdP 側)

下記を参考に Grafana ワークスペースと、IdP となる Auth0 との SAML 連携を構成します。

Auth0 で作成した Web App の SAML 設定で、Application Callback URL に Service provider reply URL (/acsで終わる方)を、また Settings に Service provider reply URL (/acsで終わる方) および Service provider identifier (/metadata で終わる方)を記載した下記の JSON を設定します。 [/js]

{
  "audience": "<Service provider identifier (/metadata で終わる方)>",
  "destination": "<Service provider reply URL (/acs で終わる方)>",
  "mappings": {
      "email": "email",
      "nickname": "nickname"
  },
  "createUpnClaim": false,
  "passthroughClaimsWithNoMapping": false,
  "mapUnknownClaimsAsIs": false
}

そして Identity Provider Metadata の URL を(ダウンロードではなく)コピーします。https://<TenantName>.jp.auth0.com/samlp/metadata/<ID>のような URL になります。

SAML の構成(Grafana ワークスペース側)

そして、2 回目の CDK デプロイでは samlConfiguration を構成しますが、ここで先ほどの Identity Provider Metadata の URL を Grafana ワークスペースに設定します。

lib/cdk-sample-stack.ts

import {
  aws_grafana,
  aws_iam,
  Stack,
  StackProps,
  CfnOutput,
} from 'aws-cdk-lib';
import { Construct } from 'constructs';

interface CdkSampleStackProps extends StackProps {
  readonly grafanaWorkspaceSamlAdminRoles: string[];
  readonly grafanaWorkspaceSamlEditorRoles: string[];
  readonly grafanaWorkspaceSamlIdpMetadataUrl: string;
}

export class CdkSampleStack extends Stack {
  public readonly myFileObjectKey: string;

  constructor(scope: Construct, id: string, props: CdkSampleStackProps) {
    super(scope, id, props);

    // Grafana Workspace 用の IAM Role を作成
    const principal = new aws_iam.ServicePrincipal(
      'grafana.amazonaws.com'
    ).withConditions({
      StringEquals: {
        'aws:SourceAccount': this.account,
      },
      StringLike: {
        'aws:SourceArn': `arn:aws:grafana:${this.region}:${this.account}:/workspaces/*`,
      },
    });
    const grafanaRole = new aws_iam.Role(this, 'GrafanaRole', {
      assumedBy: principal,
    });

    // Grafana Workspace を作成
    const cfnWorkspace = new aws_grafana.CfnWorkspace(this, 'Workspace', {
      accountAccessType: 'CURRENT_ACCOUNT',
      authenticationProviders: ['SAML'],
      permissionType: 'SERVICE_MANAGED',
      roleArn: grafanaRole.roleArn,
      samlConfiguration: {
        assertionAttributes: {
          name: 'nickname',
          login: 'email',
          email: 'email',
          role: 'email', // どの SAML 属性をロールのアサーションに使用するか(Assertion attribute role)を指定
        },
        loginValidityDuration: 1440,
        roleValues: {
          admin: props.grafanaWorkspaceSamlAdminRoles,
          editor: props.grafanaWorkspaceSamlEditorRoles,
        },
        idpMetadata: {
          url: props.grafanaWorkspaceSamlIdpMetadataUrl,
        },
      },
    });

    const workspaceId = cfnWorkspace.ref;

    // Grafana workspace URL の出力
    new CfnOutput(this, 'WorkspaceServiceProviderIdentifier', {
      value: `https://${workspaceId}.grafana-workspace.${this.region}.amazonaws.com`,
    });
  }
}

動作確認

出力された Grafana workspace URL からワークスペースにアクセスしてみます。

samlConfiguration.roleValues.adminで指定した管理者ユーザーでログインすると、管理者権限のメニューにアクセスできるようになっています。

ユーザー一覧を見ると、samlConfigurationroleValuesで指定した管理者権限と編集者権限のユーザーが確認できます。

またプロファイルを見ると、samlConfiguration.assertionAttributesで指定したアサーション属性がマッピングされていることが確認できます。

おわりに

Amazon Managed Grafana ワークスペースの作成と SAML の構成を AWS CDK で行ってみました。

どなたかの参考になれば幸いです。

参考

以上