Amazon Redshift Serverlessでクロスアカウントのデータ共有を試してみました

Amazon Redshiftのデータ共有(Data sharing)を使えば、アカウント間(クロスアカウント)でも簡単な設定でデータが共有できるのでめちゃくちゃいいですね。
2023.06.19

データアナリティクス事業本部の鈴木です。

最近はAmazon Redshiftのデータ共有に凝っています。前回はRedshift Serverlessで同一アカウント内のデータ共有設定を確認してみましたが、今回はクロスアカウントでのデータ共有設定について確認してみました。

確認したかったこと

異なるアカウントに一つずつRedshift Serverlessインスタンスを起動し、データ共有の設定方法を確認しました。

検証したい構成

特に以下を確認しました。

  • アカウント間のデータ共有設定を実行してみて、コンシューマーアカウントのRedshift Serverlessインスタンスからプロデューサーアカウントのインスタンスのデータをクエリできることを確認する。
  • コンシューマーアカウントのRedshift Serverlessインスタンスに一般ユーザーを作成し、データ共有から作ったデータベースがクエリできるか確認する。
  • プロデューサーアカウントでデータ共有を無効化した時にどうなるのか確認する。

Redshift Serverlessインスタンスの準備

以下のブログで紹介されているCloudFormationテンプレートを少し修正してデプロイし、リソースを準備しました。

修正内容は以下になります。

  • AWS::RedshiftServerless::NamespaceKmsKeyIdは指定せず、Redshift Serverlessの暗号化鍵にAWS_OWNED_KMS_KEYを使いました。
  • 最小ベースキャパシティは8RPUとしました。

テンプレートを、プロデューサーアカウントとコンシューマーアカウントのそれぞれのCloudFormationからデプロイしました。

テンプレートは以下としましたが、長いのでトグルメニューにして隠しておきます。

CloudFormationテンプレート
AWSTemplateFormatVersion: "2010-09-09"
Description: "Redshift Serverless and VPC"
Parameters:
  Env:
    Type: "String"
    Default: "test"
  ProjectName:
    Type: "String"
  CidrBlock:
    Description: Please type the CidrBlock.
    Type: String
    Default: 192.168.0.0/22
  BaseCapacity: 
    Type: Number
    Default: 8
  EnhancedVpcRouting: 
    Type: String
    AllowedValues:
      - true
      - false
    Default: false 
  PubliclyAccessible: 
    Type: String
    AllowedValues:
      - true
      - false
    Default: true
  AdminUsername:
    Type: String
    Default: awsuser
  AdminUserPassword:
    Type: String
    Description: Must be 8-64 characters long. Must contain at least one uppercase letter, one lowercase letter and one number. Can be any printable ASCII character except “/”, ““”, or “@”.
    NoEcho: true
    MinLength: 8
    MaxLength: 64

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Sub ${CidrBlock}
      EnableDnsSupport: True
      EnableDnsHostnames: True
      InstanceTenancy: default
      Tags:
      - Key: Name
        Value: !Sub ${ProjectName}-redshiftserverless-${Env}-VPC
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: Application
        Value:
          Ref: AWS::StackId
      - Key: Network
        Value: Public
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId:
        Ref: VPC
      InternetGatewayId:
        Ref: InternetGateway
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    DependsOn: AttachGateway
    Properties:
      VpcId:
        Ref: VPC
      Tags:
      - Key: Name
        Value: !Sub |
          ${ProjectName}-redshiftserverless-${Env}-public-rtb
      - Key: Application
        Value:
          Ref: AWS::StackId
  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId:
        Ref: PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId:
        Ref: InternetGateway

  Subnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: VPC
      AvailabilityZone: !Select [ 0, !GetAZs ]
      CidrBlock: !Select [ 0, !Cidr [ !GetAtt VPC.CidrBlock, 4, 8 ]]
  Subnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: VPC
      AvailabilityZone: !Select [ 1, !GetAZs ]
      CidrBlock: !Select [ 1, !Cidr [ !GetAtt VPC.CidrBlock, 4, 8 ]]
  Subnet3:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: VPC
      AvailabilityZone: !Select [ 2, !GetAZs ]
      CidrBlock: !Select [ 2, !Cidr [ !GetAtt VPC.CidrBlock, 4, 8 ]]

  Subnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId:
        Ref: Subnet1
      RouteTableId:
        Ref: PublicRouteTable
  Subnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId:
        Ref: Subnet2
      RouteTableId:
        Ref: PublicRouteTable
  Subnet3RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId:
        Ref: Subnet3
      RouteTableId:
        Ref: PublicRouteTable
  RedshiftServerlessSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId:
        Ref: VPC
      GroupDescription: Marker security group for Application server.
      Tags:
      - Key: Name
        Value: !Sub |
          ${ProjectName}-redshiftserverless-${Env}-sg
  RedshiftServerlessRole:
    Type: "AWS::IAM::Role"
    Properties:
      Path: "/"
      RoleName: !Sub "${ProjectName}-${Env}-redshift-role"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - redshift.amazonaws.com
            Action: sts:AssumeRole
      MaxSessionDuration: 3600
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/AmazonAthenaFullAccess"
        - "arn:aws:iam::aws:policy/AmazonS3FullAccess"
        - "arn:aws:iam::aws:policy/AWSGlueConsoleFullAccess"
        - "arn:aws:iam::aws:policy/AmazonRedshiftAllCommandsFullAccess"
      Description: "Allows Redshift clusters to call AWS services on your behalf."
      Tags:
        - Key: "Name"
          Value: !Sub "${ProjectName}-redshiftserverless-${Env}-redshift-role"
  RedshiftServerlessWorkGroup:
    Type: AWS::RedshiftServerless::Workgroup
    Properties: 
      WorkgroupName: !Sub "${ProjectName}-${Env}-redshift-wg"
      BaseCapacity: !Ref BaseCapacity
      EnhancedVpcRouting: !Ref EnhancedVpcRouting
      NamespaceName: !Ref RedshiftServerlessNamespace
      PubliclyAccessible: !Ref PubliclyAccessible
      SecurityGroupIds: 
        - !Ref RedshiftServerlessSecurityGroup
      SubnetIds: 
        - !Ref Subnet1
        - !Ref Subnet2
        - !Ref Subnet3
  RedshiftServerlessNamespace:
    Type: AWS::RedshiftServerless::Namespace
    Properties: 
      NamespaceName: !Sub "${ProjectName}-${Env}-redshift-ns"
      AdminUsername: !Ref AdminUsername
      AdminUserPassword: !Ref AdminUserPassword
      DbName: !Sub "${ProjectName}-db"
      IamRoles: 
        - !GetAtt RedshiftServerlessRole.Arn

データ共有の設定

ドキュメントとしては、以下の『Amazon Redshift データベース開発者ガイド』のAWSアカウント間でのデータ共有のページを参考にしました。Redshiftのデータ共有をそのまま使って設定する方法になります。

記載の手順に従って、クロスアカウントでのデータ共有ができることを確認しました。ただし、実際やってみると、ドキュメントを読んだだけよりも、必要な操作が多い印象だった(とはいえあくまで確認手順が増えたくらいの話です)ので、後の検証内容の紹介部分で補足します。

やってみる

今回は二つのアカウントを使います。マネジメントコンソールがライトモードである方がプロデューサーアカウント、ダークモードである方がプロデューサーアカウントです。Redshift query editor v2のコンソールは両方ダークモードで作業してしまったので、どちらなのかは随時記載します。

1. CloudFormationテンプレートのデプロイ

先に紹介したCloudFormationテンプレートを、プロデューサーアカウントとコンシューマーアカウントのそれぞれでデプロイしました。

2. プロデューサー側の設定(データ共有作成)

データの準備

まず、プロデューサーのRedshift Serverlessインスタンスに、Redshift query editor v2で、awsuserスーパーユーザーで接続し、tickitサンプルデータベースを作成しました。

サンプルデータベースの作成

データ共有の作成

以下のSQLを実行し、tickit_datashareデータ共有を作成しました。

CREATE DATASHARE tickit_datashare;
ALTER DATASHARE tickit_datashare SET publicaccessible = TRUE;

次に、以下のSQLを実行して、tickit.usersサンプルテーブルをデータ共有に追加しました。

-- Add schema to datashare
ALTER DATASHARE tickit_datashare ADD SCHEMA PUBLIC;

-- Add table under schema to datashare
ALTER DATASHARE tickit_datashare ADD SCHEMA tickit;


ALTER DATASHARE tickit_datashare ADD TABLE tickit.users;

最後に、以下のSQLを実行して、コンシューマーアカウントにデータ共有を行うよう設定しました。

-- <コンシューマーアカウントのアカウントID>は適したものに変えてください
GRANT USAGE ON DATASHARE tickit_datashare TO ACCOUNT '<コンシューマーアカウントのアカウントID>';

ここまでで、アカウントで作成されたデータ共有画面に、作業で作成したデータ共有ができていることを確認できました。

作成されたデータ共有

データ共有ステータス必要なアクションになっていました。これは次で説明します。

3. プロデューサー側の設定(データコンシューマーの承認)

アカウント内のデータ共有では不要でしたが、クロスアカウントではデータコンシューマーの承認をする必要がありました。

個別のデータ共有を開きました。コンシューマーのステータスPending authorizationになっています。

個別のデータ共有画面

データコンシューマーを選び、データコンシューマーIDが合っていることを確認して、承認を押しました。

データコンシューマーの承認

これでコンシューマーのステータス承認済みになりました。

承認済み

4. コンシューマー側の設定(データ共有の関連付け)

次に、コンシューマーアカウントの設定をしていきました。

データ共有で他のアカウントからタブを開くと、tickit_datashareがあることを確認できました。ここでもデータ共有ステータスが 必要なアクションになっているので作業をしました。

他のアカウントからのデータ共有

tickit_datashareを選択し、Associtateを押しました。

関連付けの設定1

データ共有を関連づける対象を選択しました。今回は、コンシューマーアカウントにある、今回作成したRedshift Serverlessインスタンスだけを指定しました。

関連付けの設定2

データ共有ステータスアクティブになりました。

関連付けの設定の完了

5. コンシューマー側の設定(データ共有からのデータベース作成)

まず、コンシューマー側のRedshift Serverlessインスタンスからデータ共有が確認できるか、Redshift query editor v2で確認しました。

コンシューマー側のRedshift Serverlessインスタンスの適当なデータベースにawsuserで接続し、以下のSQLを実行しました。

SHOW DATASHARES LIKE 'tickit_%';

関連付けをしているため、データ共有が見えました。

データ共有の確認

次に、以下のSQLを実行して、データ共有からデータベースを作成しました。

-- <コンシューマーアカウントのアカウントID>は適したものに変えてください
-- <プロデューサーRedshift ServerlessのNAMESPACE>は適したものに変えてください
CREATE DATABASE consumer_tickit FROM DATASHARE tickit_datashare OF ACCOUNT '<プロデューサーアカウントのアカウントID>' NAMESPACE '<プロデューサーRedshift ServerlessのNAMESPACE>';

作成されたデータベース

検索すると、確かにデータを取得できました。

データの検索結果

そのほかに確認したこと

プロデューサーに追加したデータが反映されることの確認

改めてではありますが、プロデューサーのRedshift Serverless側で追加したデータが、コンシューマー側で取得できるのかについて確認しました。

プロデューサーアカウントで、Redshift query editor v2にてインスタンスに接続し、以下のSQLを実行してusersテーブルにデータを追加しました。

insert into tickit.users values
('000','Samaple','Samaple','Samaple','Samaple','OK','Sample.nulla@magnisdis.ca','(295) Samaple',null,false,false,false,null,true,null,null,null,null)

コンシューマーアカウントで、Redshift query editor v2にてインスタンスに接続し、consumer_tickit.tickit.usersテーブルにデータが反映されているか確認できました。

反映されたデータ

コンシューマーアカウントの一般ユーザーで共有されたデータを確認する

コンシューマーアカウントにて、Redshift query editor v2でawsuserスーパーユーザーを使って適当なユーザーを作成しました。このユーザーから共有されたconsumer_tickit.tickit.usersテーブルを確認できるか試しました。

コンシューマーアカウントにて、以下のSQLを実行しました。

CREATE USER testuser1 WITH PASSWORD 'testPASS111';

Redshift query editor v2で、testuser1で接続しました。

この時点では、consumer_tickitデータベースが見られませんでした。

consumer_tickitデータベースが見られない

再びawsuserでの接続に切り替え、consumer_tickitデータベースへの権限を付与しました。

GRANT USAGE ON DATABASE consumer_tickit TO testuser1;

再度testuser1で接続すると、利用できるようになりました。

consumer_tickitデータベースが見える

プロデューサー側からデータ共有を無効化した場合の挙動

最後に、プロデューサー側からデータ共有を無効化した場合の挙動を確認しました。これはドキュメントにオプションとして記載されていたもので、データ共有が不要になった場合の操作になります。

まず、プロデューサー側からawsuserで以下のSQLを実行し、tickit_datashareデータ共有を無効化しました。

-- <コンシューマーアカウントのアカウントID>は適したものに変えてください
REVOKE USAGE ON DATASHARE tickit_datashare FROM ACCOUNT '<コンシューマーアカウントのアカウントID>';

これでRedshiftのコンソールからはデータ共有が削除されました。

プロデューサー側の削除結果

コンシューマー側でもRedshiftのコンソールからはデータ共有が削除されました。

コンシューマー側の削除結果

ただし、コンシューマー側のインスタンスに接続すると、データベースは残っていたので、これは手動で削除する必要がありそうでした。

コンシューマー側に残ったデータベース

例えば以下のようにSQLを実行して削除できました。

DROP DATABASE consumer_tickit;

最後に

今回はAmazon Redshift Serverlessで、クロスアカウントでのデータ共有設定について確認してみました。同一アカウントのときよりも少し操作が増えていましたが、事故防止の一手間という程度で、とても簡単に設定できました。

アカウントを跨いでも、Redshift間であればETLなしでデータを連携できるのは非常に強力です。Redshiftをデータウェアハウスの選択肢に入れておられる方は、ぜひご確認頂ければと思います。

そのた参考資料