RDS DBインスタンスを暗号化する – Step Functionsバージョン

RDS DBインスタンスを暗号化する - Step Functionsバージョン

RDS DBインスタンスを暗号化する – Step Functionsバージョン

暗号化していないRDS DBインスタンスを暗号化する方法について考えます。

AWS公式では、以下のように説明されています。

Amazon RDS DB インスタンスは、DB インスタンスの作成時にのみ暗号化できます。作成後には暗号化できません。

ただし、暗号化されていないスナップショットのコピーは暗号化できるので、暗号化されていない DB インスタンスに効果的に暗号化を追加できます。つまり、DB インスタンスのスナップショットを作成し、そのスナップショットの暗号化済みコピーを作成します。この暗号化されたスナップショットから DB インスタンスを復元することで、元の DB インスタンスの暗号化されたコピーを作成できます。

Amazon RDS の暗号化された DB インスタンスの制限事項

今回はStep Functionsを使用して、上記の手続きを実現します。

構築する環境

Diagram of encrypting an RDS DB instance - Step Functions version.

以下を順番に実施するStep Functionsステートマシンを作成します。

  1. 暗号化していないDBインスタンスのスナップショットを作成する。
  2. 作成したスナップショットのステータスを確認し、「available」であることを確認する。
  3. スナップショットのコピーを作成する。コピー作成時に、暗号化オプションを有効化する。
  4. 作成したスナップショットのステータスを確認し、「available」であることを確認する。
  5. 暗号化済みのスナップショットから新しいDBインスタンスを作成する。

手順の中で、2回スナップショットのステータスを確認します。
これは作成直後のスナップショットのステータスは「creating」であるためです。
このステータスでは、スナップショットのコピーや、DBインスタンスのリストアは不可です。

上記に対応するために、ChoiceステートおよびWaitステートを使用します。
ステータスが「available」の場合は、次のステートに進めます。
ステータスが「available」ではない場合は、Waitステートに移動し、待機します。
待機後は、再びステータスをチェックします。

ステートマシンを構成するLambda関数はPython3.8を使用します。

EC2インスタンスを作成します。
元々のDBインスタンスや、暗号化済みのDBインスタンスにアクセスするために使用します。
このインスタンスのOSは、最新のAmazon Linux 2とします。

CloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。

https://github.com/awstut-an-r/awstut-soa/tree/main/04/005

テンプレートファイルのポイント解説

Step Functionsステートマシン

Resources:
  StateMachine:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      Definition:
        Comment: !Sub "${Prefix}-StateMachine"
        StartAt: CreateSnapshotState
        States:
          CreateSnapshotState:
            Type: Task
            Resource: !Ref FunctionArn1
            Next: DescribeSnapshotState1
          DescribeSnapshotState1:
            Type: Task
            Resource: !Ref FunctionArn2
            Parameters:
              snapshot_id.$: $.no_encrypted_snapshot_id
            ResultPath: $.no_encrypted_snapshot_status
            Next: ChoiceState1
          ChoiceState1:
            Type: Choice
            Choices:
              - Not:
                  Variable: $.no_encrypted_snapshot_status
                  StringEquals: !Ref SnapshotAvailableStatus
                Next: WaitState1
              - Variable: $.no_encrypted_snapshot_status
                StringEquals: !Ref SnapshotAvailableStatus
                Next: CopySnapshotState
          WaitState1:
            Type: Wait
            Seconds: !Ref WaitSeconds
            Next: DescribeSnapshotState1
          CopySnapshotState:
            Type: Task
            Resource: !Ref FunctionArn3
            Parameters:
              instance_id.$: $.instance_id
              snapshot_id.$: $.no_encrypted_snapshot_id
            ResultPath: $.encrypted_snapshot_id
            Next: DescribeSnapshotState2
          DescribeSnapshotState2:
            Type: Task
            Resource: !Ref FunctionArn2
            Parameters:
              snapshot_id.$: $.encrypted_snapshot_id
            ResultPath: $.encrypted_snapshot_status
            Next: ChoiceState2
          ChoiceState2:
            Type: Choice
            Choices:
              - Not:
                  Variable: $.encrypted_snapshot_status
                  StringEquals: !Ref SnapshotAvailableStatus
                Next: WaitState2
              - Variable: $.encrypted_snapshot_status
                StringEquals: !Ref SnapshotAvailableStatus
                Next: RestoreDbInstanceState
          WaitState2:
            Type: Wait
            Seconds: !Ref WaitSeconds
            Next: DescribeSnapshotState2
          RestoreDbInstanceState:
            Type: Task
            Resource: !Ref FunctionArn4
            Parameters:
              availability_zone.$: $.availability_zone
              db_subnet_group_name.$: $.db_subnet_group_name
              instance_id.$: $.instance_id
              security_group_id.$: $.security_group_id
              snapshot_id.$: $.encrypted_snapshot_id
            ResultPath: $.encrypted_instance_id
            End: true
      LoggingConfiguration:
        Destinations:
          - CloudWatchLogsLogGroup:
              LogGroupArn: !GetAtt LogGroup.Arn
        IncludeExecutionData: true
        Level: ALL
      RoleArn: !GetAtt StateMachineRole.Arn
      StateMachineName: !Ref Prefix
      StateMachineType: STANDARD

  LogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "${Prefix}-StateMachineLogGroup"

  StateMachineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - states.amazonaws.com
      Policies:
        - PolicyName: !Sub "${Prefix}-InvokeTaskFunctions"
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - lambda:InvokeFunction
                Resource:
                  - !Ref FunctionArn1
                  - !Ref FunctionArn2
                  - !Ref FunctionArn3
                  - !Ref FunctionArn4
        - PolicyName: !Sub "${Prefix}-DeliverToCloudWatchLogPolicy"
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogDelivery
                  - logs:GetLogDelivery
                  - logs:UpdateLogDelivery
                  - logs:DeleteLogDelivery
                  - logs:ListLogDeliveries
                  - logs:PutLogEvents
                  - logs:PutResourcePolicy
                  - logs:DescribeResourcePolicies
                  - logs:DescribeLogGroups
                Resource: "*"
Code language: YAML (yaml)

Step Functionsステートマシンの基本的な事項については、以下のページをご確認ください。

あわせて読みたい
CFNでStep Functions入門 【CloudFormationでStep Functions入門】 AWS DVAの出題範囲であるリファクタリングに関する内容です。 Step Functionsはサーバーレスオーケストレーションサービスです...

1つ目のステート(CreateSnapshotState)

このステートはDBインスタンスのスナップショットを作成します。

Resourcesプロパティで実行するLambda関数を設定します。
本ステートでは、以下のLambda関数1を指定します。

Resources:
  Function1:
    Type: AWS::Lambda::Function
    Properties:
      Environment:
        Variables:
          REGION: !Ref AWS::Region
      Code:
        ZipFile: |
          import boto3
          import os

          region = os.environ['REGION']

          client = boto3.client('rds', region_name=region)

          def lambda_handler(event, context):
            instance_id = event['instance_id']

            response1 = client.describe_db_instances(
              DBInstanceIdentifier=instance_id
            )

            response2 = client.create_db_snapshot(
              DBSnapshotIdentifier='{instance}-no-encrypted'.format(instance=instance_id),
              DBInstanceIdentifier=instance_id
            )

            return {
              'instance_id': instance_id,
              'availability_zone': response1['DBInstances'][0]['AvailabilityZone'],
              'db_subnet_group_name': response1['DBInstances'][0]['DBSubnetGroup']['DBSubnetGroupName'],
              'security_group_id': response1['DBInstances'][0]['VpcSecurityGroups'][0]['VpcSecurityGroupId'],
              'no_encrypted_snapshot_id': response2['DBSnapshot']['DBSnapshotIdentifier'],
            }
      FunctionName: !Sub "${Prefix}-function1"
      Handler: !Ref Handler
      Runtime: !Ref Runtime
      Role: !GetAtt FunctionRole.Arn
      Timeout: !Ref Timeout
Code language: YAML (yaml)

本関数は引数として、instance_idを取ります。
これは暗号化されていないDBインスタンスのIDです。

describe_db_instancesおよびcreate_db_snapshotメソッドを実行します。

前者は既存DBインスタンスに関する情報を取得します。
具体的には、DBインスタンスに設定されているセキュリティグループ、DBサブネットグループ、インスタンスが配置されているAZです。

後者はDBインスタンスのスナップショットを作成します。
冒頭の引用の通り、まだこのスナップショットは暗号化されていません。

DBインスタンスの情報と作成したスナップショットのIDを、辞書にまとめて戻り値とします。
これらの値をステートマシンは保持します。
例えば以下のようなデータを保持することになります。

{
  "availability_zone": "ap-northeast-1d",
  "instance_id": "soa-04-005",
  "no_encrypted_snapshot_id": "soa-04-005-no-encrypted",
  "db_subnet_group_name": "dbsubnetgroup",
  "security_group_id": "sg-09dfb82d21e4846c4"
}
Code language: JSON / JSON with Comments (json)

2つ目のステート(DescribeSnapshotState1)

このステートは前ステートで作成したスナップショットのステータスを確認します。
Lambda関数2を実行します。

Parameterプロパティで、関数に渡すパラメータを指定できます。
例えば以下のようなデータを渡します。

{
  "snapshot_id": "soa-04-005-no-encrypted"
}
Code language: JSON / JSON with Comments (json)

渡すデータの値は、前ステートで保持したものを参照しています。

以下の関数を実行します。

Resources:
  Function2:
    Type: AWS::Lambda::Function
    Properties:
      Environment:
        Variables:
          REGION: !Ref AWS::Region
      Code:
        ZipFile: |
          import boto3
          import os

          region = os.environ['REGION']

          client = boto3.client('rds', region_name=region)

          def lambda_handler(event, context):
            snapshot_id = event['snapshot_id']

            response = client.describe_db_snapshots(
              DBSnapshotIdentifier=snapshot_id
            )

            return response['DBSnapshots'][0]['Status']
      FunctionName: !Sub "${Prefix}-function2"
      Handler: !Ref Handler
      Runtime: !Ref Runtime
      Role: !GetAtt FunctionRole.Arn
      Timeout: !Ref Timeout
Code language: YAML (yaml)

describe_db_snapshotsメソッドを実行して、スナップショットのステータスを確認します。
先ほど確認したように、引数としてスナップショットIDを渡して、同メソッドを呼び出します。
取得したステータス情報を返します。

ステートマシンのResultPathプロパティで、関数から渡されたデータの受け取り方を設定できます。
今回はno_encrypted_snapshot_statusというキーに、ステータス情報を格納します。

これを受けて、ステートマシンが保持するデータが更新されます。
以下がその具体例です。

{
  "availability_zone": "ap-northeast-1d",
  "instance_id": "soa-04-005",
  "no_encrypted_snapshot_id": "soa-04-005-no-encrypted",
  "db_subnet_group_name": "dbsubnetgroup",
  "security_group_id": "sg-09dfb82d21e4846c4",
  "no_encrypted_snapshot_status": "creating"
}
Code language: JSON / JSON with Comments (json)

3つ目・4つ目のステート(ChoiceState1, WaitState1)

これらのステートは、前ステートと連携して動作します。
現在のスナップショットのステータスが「creating」等の状態であれば、しばらく待機します。
改めてステータスをチェックし、「available」であれば、次のステートに進みます。

上記の挙動を、ChoiceステートのStringEqualsおよびNotプロパティ、Waitステートで実現します。
Choice・Waitステートを組み合わせて、特定の条件を満たすまでループ処理する方法については、以下のページをご確認ください。

あわせて読みたい
Step Functionsで条件を満たすまでループする 【Step Functionsで条件を満たすまでループする】 以下のページで、Step Functionsの条件分岐(Choice)を取り上げました。 https://awstut.com/2022/11/26/step-function...

今回は作成したスナップショットのステータスに応じて条件分岐します。
ですから先ほどステートマシンに保持させたno_encrypted_snapshot_statusを参照します。

5つ目のステート(CopySnapshotState)

このステートは作成したスナップショットを暗号化を有効化した上でコピーします。
Lambda関数3を実行します。

Parameterプロパティで、例えば以下のようなデータを渡します。

{
  "instance_id": "soa-04-005",
  "snapshot_id": "soa-04-005-no-encrypted"
}
Code language: JSON / JSON with Comments (json)

先述のインスタンスのIDと、作成が完了したスナップショットのIDです。

以下の関数を実行します。

Resources:
  Function3:
    Type: AWS::Lambda::Function
    Properties:
      Environment:
        Variables:
          REGION: !Ref AWS::Region
          KMS_KEY_ID: !Sub "arn:aws:kms:${AWS::Region}:${AWS::AccountId}:alias/aws/rds"
      Code:
        ZipFile: |
          import boto3
          import os

          region = os.environ['REGION']
          kms_key_id = os.environ['KMS_KEY_ID']

          client = boto3.client('rds', region_name=region)

          def lambda_handler(event, context):
            instance_id = event['instance_id']
            snapshot_id = event['snapshot_id']

            response = client.copy_db_snapshot(
              SourceDBSnapshotIdentifier=snapshot_id,
              TargetDBSnapshotIdentifier='{instance}-encrypted'.format(instance=instance_id),
              KmsKeyId=kms_key_id,
            )

            return response['DBSnapshot']['DBSnapshotIdentifier']
      FunctionName: !Sub "${Prefix}-function3"
      Handler: !Ref Handler
      Runtime: !Ref Runtime
      Role: !GetAtt FunctionRole.Arn
      Timeout: !Ref Timeout
Code language: YAML (yaml)

copy_db_snapshotメソッドを使用して、スナップショットをコピーします。
ポイントはこのメソッドを呼び出す際の引数KmsKeyIdです。
これを指定することで、暗号化が有効化になります。
このパラメータには、暗号化に使用するKMSキーを指定します。
今回はRDS用のAWSマネージドキーを使用します。

コピーして新たに作成されるスナップショットのIDを戻り値にします。

ステートマシンにおいて、encrypted_snapshot_idというキーに、スナップショットIDを格納します。
これを受けて、ステートマシンが保持するデータが更新されます。
以下がその具体例です。

{
  "availability_zone": "ap-northeast-1d",
  "instance_id": "soa-04-005",
  "no_encrypted_snapshot_id": "soa-04-005-no-encrypted",
  "db_subnet_group_name": "dbsubnetgroup",
  "security_group_id": "sg-09dfb82d21e4846c4",
  "no_encrypted_snapshot_status": "creating",
  "encrypted_snapshot_id": "soa-04-005-encrypted"
}
Code language: JSON / JSON with Comments (json)

6〜8つ目のステート(DescribeSnapshotState2, ChoiceState2, WaitState2)

先ほどと同じ流れです。
暗号化を有効化したスナップショットに対してステータスチェックを行い、「creating」の場合は、一時停止します。
停止後、改めてステータスチェックを実施し、「available」の場合は次のステートに進みます。

9つ目のステート(RestoreDbInstanceState)

このステートは暗号化済みのスナップショットから、DBインスタンスを作成します。
Lambda関数4を実行します。

Parameterプロパティで、例えば以下のようなデータを渡します。

{
  "availability_zone": "ap-northeast-1d",
  "instance_id": "soa-04-005",
  "db_subnet_group_name": "dbsubnetgroup",
  "security_group_id": "sg-09dfb82d21e4846c4",
  "snapshot_id": "soa-04-005-encrypted"
}
Code language: JSON / JSON with Comments (json)

ポイントはsnapsho_idの値です。
こちらに暗号化済みのスナップショットIDを指定することで、これからDBインスタンスが作成されます。

以下の関数を実行します。

Resources:
  Function4:
    Type: AWS::Lambda::Function
    Properties:
      Environment:
        Variables:
          REGION: !Ref AWS::Region
      Code:
        ZipFile: |
          import boto3
          import os

          region = os.environ['REGION']

          client = boto3.client('rds', region_name=region)

          def lambda_handler(event, context):
            instance_id = event['instance_id']
            snapshot_id = event['snapshot_id']
            availability_zone = event['availability_zone']
            db_subnet_group_name = event['db_subnet_group_name']
            security_group_id = event['security_group_id']

            response = client.restore_db_instance_from_db_snapshot(
              DBInstanceIdentifier='{instance}-encrypted'.format(instance=instance_id),
              DBSnapshotIdentifier=snapshot_id,
              AvailabilityZone=availability_zone,
              DBSubnetGroupName=db_subnet_group_name,
              VpcSecurityGroupIds=[
                security_group_id,
              ]
            )
            return response['DBInstance']['DBInstanceIdentifier']
      FunctionName: !Sub "${Prefix}-function4"
      Handler: !Ref Handler
      Runtime: !Ref Runtime
      Role: !GetAtt FunctionRole.Arn
      Timeout: !Ref Timeout
Code language: YAML (yaml)

restore_db_instance_from_db_snapshotメソッドを実行して、スナップショットからDBインスタンスを作成します。

戻り値には、作成されたDBインスタンスのIDを指定します。

ステートマシンにおいて、encrypted_instance_idというキーに、DBインスタンスのIDを格納します。
これを受けて、ステートマシンが保持するデータが更新されます。
以下がその具体例です。

{
  "availability_zone": "ap-northeast-1d",
  "instance_id": "soa-04-005",
  "no_encrypted_snapshot_id": "soa-04-005-no-encrypted",
  "db_subnet_group_name": "dbsubnetgroup",
  "security_group_id": "sg-09dfb82d21e4846c4",
  "no_encrypted_snapshot_status": "creating",
  "encrypted_snapshot_id": "soa-04-005-encrypted",
  "encrypted_instance_id": "soa-04-005-encrypted"
}
Code language: JSON / JSON with Comments (json)

(参考) RDS

Resources:
  DBInstance:
    Type: AWS::RDS::DBInstance
    DeletionPolicy: Delete
    Properties:
      AllocatedStorage: !Ref DBAllocatedStorage
      AvailabilityZone: !Sub "${AWS::Region}${AvailabilityZone}"
      DBInstanceClass: !Ref DBInstanceClass
      DBInstanceIdentifier: !Ref Prefix
      DBSubnetGroupName: !Ref DBSubnetGroup
      Engine: !Ref DBEngine
      EngineVersion: !Ref DBEngineVersion
      MasterUsername: !Ref DBMasterUsername
      MasterUserPassword: !Ref DBMasterUserPassword
      StorageEncrypted: false
      VPCSecurityGroups:
        - !Ref DBSecurityGroup

  DBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupName: dbsubnetgroup
      DBSubnetGroupDescription: testgroup.
      SubnetIds:
        - !Ref DBSubnet1
        - !Ref DBSubnet2
Code language: YAML (yaml)

特別な設定は行いません。
DBインスタンスを作成します。

(参考) EC2

Resources:
  Instance:
    Type: AWS::EC2::Instance
    Properties:
      IamInstanceProfile: !Ref InstanceProfile
      ImageId: !Ref ImageId
      InstanceType: !Ref InstanceType
      NetworkInterfaces:
        - DeviceIndex: 0
          SubnetId: !Ref InstanceSubnet
          GroupSet:
            - !Ref InstanceSecurityGroup
      UserData: !Base64 |
        #!/bin/bash -xe
        yum update -y
        yum install -y mariadb
Code language: YAML (yaml)

ユーザデータで、初期化処理を定義します。
ユーザデータに関する詳細については、以下のページをご確認ください。

あわせて読みたい
Linuxインスタンスの初期化方法4選 【Linuxインスタンスを初期化する4つの方法】 EC2インスタンスの起動時に初期化処理を実行する方法を考えます。 EC2インスタンスを構築時に初期化する以下の4つの手法を...

MariaDBをインストールすることで、DB接続用のクライアントを準備します。
詳細につきましては、以下のページをご確認ください。

あわせて読みたい
AL2で全RDSに接続する方法まとめ 【Amazon Linux 2からRDSの全DBエンジンに接続する方法】 2022年現在、RDSは以下の7種類のDBエンジンを提供しています。 Aurora(PostgreSQL) Aurora(MySQL) PostgreSQL ...

環境構築

CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。

CloudFormationスタックを作成し、スタック内のリソースを確認する

CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。

あわせて読みたい
CloudFormationのネストされたスタックで環境を構築する 【CloudFormationのネストされたスタックで環境を構築する方法】 CloudFormationにおけるネストされたスタックを検証します。 CloudFormationでは、スタックをネストす...

各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。

  • EC2インスタンス:i-0c948c4d3fa569462
  • DBインスタンス:soa-04-005
  • DBインスタンスのエンドポイント:soa-04-005.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com
  • Lambda関数1:soa-04-005-function1
  • Lambda関数2:soa-04-005-function2
  • Lambda関数3:soa-04-005-function3
  • Lambda関数4:soa-04-005-function4
  • Step Functionsステートマシン:soa-04-005

AWS Management ConsoleからDBインスタンスを確認します。

Detail of RDS 1.

Storageを見ると、Encryptionが「Not enabled」となります。
つまり暗号化されていません。

Step Functionsステートマシンを確認します。

Detail of Step Functions 1.

確かにステートマシンが作成されています。
ステートマシンの流れがグラフとして表現されています。

動作確認

暗号化していないDBインスタンスに接続

準備が整いましたので、EC2インスタンスにアクセスします。

EC2インスタンスへのアクセスはSSM Session Managerを使用します。

% aws ssm start-session --target i-0c948c4d3fa569462
...
sh-4.2$
Code language: Bash (bash)

SSM Session Managerの詳細につきましては、以下のページをご確認ください。

あわせて読みたい
LinuxインスタンスにSSM Session Manager経由でアクセスする 【LinuxインスタンスにSSM Session Manager経由でアクセスする】 EC2インスタンスにSSM Session Manager経由でアクセスする構成を確認します。 Session Manager は完全...

MySQLクライアントのインストール状況を確認します。

sh-4.2$ mysql -V
mysql  Ver 15.1 Distrib 5.5.68-MariaDB, for Linux (aarch64) using readline 5.1
Code language: Bash (bash)

確かにユーザデータによって、MySQLクライアントがインストールされています。

このクライアントパッケージを使用して、DBインスタンスに接続します。

sh-4.2$ mysql -h soa-04-005.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com -P 3306 -u testuser -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 18
Server version: 8.0.28 Source distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> 
Code language: Bash (bash)

接続できました。

試しにテスト用データベースおよびテーブルを作成し、テストデータを保存します。

MySQL [(none)]> CREATE database test;
Query OK, 1 row affected (0.01 sec)

MySQL [(none)]> use test;
Database changed

MySQL [test]> CREATE TABLE planet (id INT UNSIGNED AUTO_INCREMENT, name VARCHAR(30), PRIMARY KEY(id));
Query OK, 0 rows affected (0.02 sec)

MySQL [test]> INSERT INTO planet (name) VALUES ("Mercury");
Query OK, 1 row affected (0.00 sec)

MySQL [test]> select * from planet;
+----+---------+
| id | name    |
+----+---------+
|  1 | Mercury |
+----+---------+
1 row in set (0.00 sec)
Code language: Bash (bash)

テストデータを書き込むことができました。

Step Functionsステートマシン実行

ステートマシンを実行します。

Inputに引数を設定します。
引数には暗号化されていないDBインスタンスのIDを指定します。

Detail of Step Functions 2.

Start executionを押下します。

Detail of Step Functions 3.

ステートマシンの実行が開始されました。
これから各ステートが順番に実行されます。

Detail of Step Functions 4.

WaitState1で一時停止しました。
Inputを見ると、no_encypted_snapshot_statusの値が「creating」とあります。
つまりDBインスタンスから作成されたスナップショットのステータスが「available」に変更することを待っている状態です。

このスナップショットの詳細を確認します。

Detail of RDS 2.

Statusを見ると、確かに「creating」とあります。
なおKMS key IDが設定されていないため、暗号化されていません。

しばらく待機すると、処理が次のステートに移行します。

Detail of Step Functions 5.

WaitState2で一時停止しました。
Inputを見ると、encypted_snapshot_statusの値が「creating」とあります。
このスナップショットは暗号化オプションを有効化した上で、先ほどのスナップショットをコピーして作成したものです。
これのステータスが「available」に変更することを待っている状態です。

このスナップショットの詳細を確認します。

Detail of RDS 3.

Statusを見ると、確かに「creating」とあります。
なおKMS key IDが設定されているため、暗号化されています。

しばらく待機すると、全てのステートの処理が完了します。

Detail of Step Functions 6.

暗号化済みのスナップショットから新しいDBインスタンスが作成されました。
DBインスタンスのIDは「soa-04-005-encrypted」です。

暗号化済みDBインスタンス

新たに作成されたDBインスタンスを確認します。

Detail of RDS 4.

Encryptionを見ると「enabled」とあります。
このことから確かに暗号化されていることがわかります。

最後にこのDBインスタンスにアクセスします。

sh-4.2$ mysql -h soa-04-005-encrypted.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com -P 3306 -u testuser -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 8.0.28 Source distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> 
Code language: Bash (bash)

正常にログインできました。

保存されているデータを確認します。

MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| test               |
+--------------------+
5 rows in set (0.00 sec)

MySQL [(none)]> use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MySQL [test]>

MySQL [test]> show tables;
+----------------+
| Tables_in_test |
+----------------+
| planet         |
+----------------+
1 row in set (0.01 sec)

MySQL [test]> select * from planet;
+----+---------+
| id | name    |
+----+---------+
|  1 | Mercury |
+----+---------+
1 row in set (0.00 sec)
Code language: Bash (bash)

データが保存されていました。
これは暗号化前に保存していたものです。

以上より、暗号化していないDBインスタンスと同じ内容を持つ、暗号化されたDBインスタンスを作成できました。

まとめ

Step Functionsを使用して、暗号化していないRDS DBインスタンスを暗号化する方法をご紹介しました。