この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
好物はインフラとフロントエンドのかじわらゆたかです。 Redshift Serverless熱いですね。
GAされて以後、様々なブログがDevelopers.IOでも出ているのもあり、 気軽に試せるように環境構築用のCloudFormationのテンプレートを書いてみました。
書いてみた
Redshift_Serverless.yml
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: 32
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
KmsKeyId:
Type: String
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
KmsKeyId: !Ref KmsKeyId
DbName: !Sub "${ProjectName}-db"
IamRoles:
- !GetAtt RedshiftServerlessRole.Arn
create_Redshift_Serverless.sh
#!/bin/bash
set -eu
export AWS_PAGER=""
CHANGESET_OPTION="--no-execute-changeset"
# 実行時に指定された引数の数、つまり変数 $# の値が 4 でなければエラー終了。
if [ $# -ne 4 ]; then
echo "指定された引数は$#個です。" 1>&2
echo "実行するには3個の引数が必要です。" 1>&2
echo "$0 [deploy or check] [ProjectName] [AdminUserPassword Parameter Store Key ][AWS CLI Profile]"
exit 1
fi
# 第1引数がdeployの場合、Change setの確認は行わず、Deployを行います。
if [ $1 = "deploy" ]; then
echo "deploy mode"
CHANGESET_OPTION=""
else
echo "check mode"
fi
# 第2引数にはプロジェクト名を指定します。
ProjectName=$2
# 第3引数にはRedshift Serverlessのパスワードが格納されているパラメータストアのキーを指定します。
AdminUserPasswordKey=$3
# 第4引数にはAWS CLIのProfileを指定します。
profile=$4
# パラメータストアから格納されているパスワードの値を取得します。
AdminUserPassword=`aws ssm get-parameter --name ${AdminUserPasswordKey} --profile $profile --with-decryption --query Parameter.Value --output text`
# RedshiftのAWS マネージドキーのIDを取得します。別の鍵を使う場合は個々の値を書き換えてください。
KmsKeyID=$(aws kms list-aliases --output text --profile $profile --query 'Aliases[?AliasName == `alias/aws/redshift`].TargetKeyId')
CFN_TEMPLATE=./Redshift_Serverless.yml
CFN_STACK_NAME=${ProjectName}-redshiftserverless
# テンプレートの実行
aws cloudformation deploy --stack-name ${CFN_STACK_NAME} --template-file ${CFN_TEMPLATE} ${CHANGESET_OPTION} \
--profile $profile \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
ProjectName=$ProjectName \
AdminUserPassword=$AdminUserPassword \
KmsKeyId=$KmsKeyID
CloudFormationとシェルで分割している理由
CloudFormationをAWS CLIから動かすのが便利だということをハマコーの記事から学んだので、それを使いたかったのと、 それ以外にも以下の理由があります。
KMSのIDはアカウントごとに固有だがCloudFormationから取得する方法がない
Redshift ServerlessはKMSによる暗号化が必須で、構築時には用いるKMSのIDを入力する必要があります。
この値は動かすAWSアカウントごとに固有になるためCloudFormationのテンプレートに記載するのではなく、CloudFormation単体で取得したいのですが取得することができません。
そのため、シェルの中でAWS マネージドキーのID取得してCloudFormationのパラメーターとして渡しています。
Redshift ServerlessのAdminUserPasswordはSecureString未対応
Cloudformationではパスワード等の文字列をパラメータストアのSecureStringから取得すると言った方法があります。 ServerlessではないRedshiftの場合SecureStringで格納した値をMasterUserPasswordに格納すると言った事が可能なのですが、 RedshiftServerlessは対応しておらずパラメータストアから取得しようとするためにはSecureStringではなく、普通のStringとしてパラメータストアに格納しそれを参照すると言った方法を取る必要があります。(2022/09現在)
動的な参照を使用してテンプレート値を指定する - AWS CloudFormation
パスワードという機密データをテンプレートに埋め込むのはもちろん論外で、パラメータストアに只の文字列として格納するのも微妙です。
そのためSecureStringとしてパラメータストアに格納し、その値の取得をシェルから行いCloudFormationにわたすと言った方法をとっています。
もちろん値はNoEcho: true
と設定しているので、Cloudformationのマネージメントコンソール上からの確認はできません。
VPC構築を少しスマートに書いてみました。
Redshift Serverlessを構築する場合、Availability Zoneは3つにまたいだSubnetが必要です。 また、Redshift Serverlessに割り当てるBaseUnitCapacityの数によっては十分なIPも必要です。
VPCを新しく作成した際にSubnetに割り当てるCIDRの計算やAZの文字列を組み合わせたりしたくないなって思っていたのですが、そこらへんはもうしなくてよさそうです。
RegionごとのAvailability Zoneを動的に取得する
AZの取得は以下のようになります。
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 ]]
強調表記したところになるのですが、CloudFormationの組み込み関数である !GetAZs
はリージョンのAZのリストを返す関数になります。
それを、!Select
で指定して取得することで、AZの情報を取得することが可能です。
Subnetごとに割り当てるCidrを計算させる。
これも組み込み関数の!Cidr
を用いることでVPCに割り当てたCIDRから計算させる事が可能です。
これについては以下のブログで非常にわかり易く解説されております。
構築してみた
以下のような形で構築が可能です。
$ sh ./create_Redshift_Serverless.sh check deviosample "/cm-kajiwara/redshift-serverless-admin-user-password" YourAWSCLIProfile
Waiting for changeset to be created..
Changeset created successfully. Run the following command to review changes:
aws cloudformation describe-change-set --change-set-name arn:aws:cloudformation:ap-northeast-1:123456789012:changeSet/awscli-cloudformation-package-deploy-1663432179/4f875331-1cd9-4a9b-86db-f98871abfca8
まとめ
Redshift Serverlessを構築するためのCloudFormationのテンプレートとシェルを公開してみました。 単一のCloudformationになっていますので、不要になったときの片付けも容易かと思います。 検証時には是非お使いください。