├── .gitignore ├── rds ├── CDK │ ├── .gitignore │ ├── cdk.json │ ├── cdk.context.json │ ├── requirements.txt │ ├── README.md │ ├── app.py │ ├── setup.py │ ├── unencrypted_to_encrypted_rds │ │ ├── test_rds_stack.py │ │ └── unencrypted_to_encrypted_rds_stack.py │ ├── aws-encrypt-rds-instance.json │ ├── aws-encrypt-rds.json │ └── aws-encrypt-rds-cluster.json ├── SCP │ ├── rds_encrypted.json │ └── README.md ├── README.md └── CloudFormation │ ├── README.md │ └── unencrypted-to-encrypted-rds.template.json ├── ebs-architecture.png ├── rds-architecture.png ├── CODE_OF_CONDUCT.md ├── ebs ├── SCP │ └── DenyUnencryptedEC2.json ├── Bash │ └── enable-ebs-encryption-for-account.sh ├── README.md └── CloudFormation │ ├── Stack1.yaml │ └── Stack2.yaml ├── CHANGELOG.md ├── LICENSE ├── README.md └── CONTRIBUTING.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /rds/CDK/.gitignore: -------------------------------------------------------------------------------- 1 | cdk.out/ 2 | -------------------------------------------------------------------------------- /rds/CDK/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "python3 app.py" 3 | } 4 | -------------------------------------------------------------------------------- /rds/CDK/cdk.context.json: -------------------------------------------------------------------------------- 1 | { 2 | "@aws-cdk/core:enableStackNameDuplicates": "true", 3 | "aws-cdk:enableDiffNoFail": "true" 4 | } 5 | -------------------------------------------------------------------------------- /ebs-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-system-manager-automation-unencrypted-to-encrypted-resources/HEAD/ebs-architecture.png -------------------------------------------------------------------------------- /rds-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-system-manager-automation-unencrypted-to-encrypted-resources/HEAD/rds-architecture.png -------------------------------------------------------------------------------- /rds/CDK/requirements.txt: -------------------------------------------------------------------------------- 1 | aws-cdk.aws-iam==1.121.0 2 | aws-cdk.aws-ssm==1.121.0 3 | aws-cdk.aws-config==1.121.0 4 | aws-cdk.aws-kms==1.121.0 5 | aws-cdk.aws-rds==1.121.0 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /ebs/SCP/DenyUnencryptedEC2.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [{ 4 | "Effect": "Deny", 5 | "Action": ["ec2:CreateVolume", "ec2:RunInstances"], 6 | "Resource": "*", 7 | "Condition": { 8 | "Bool": { 9 | "ec2:Encrypted": "false" 10 | } 11 | } 12 | }] 13 | } -------------------------------------------------------------------------------- /ebs/Bash/enable-ebs-encryption-for-account.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Eeuox pipefail 3 | 4 | aws ec2 enable-ebs-encryption-by-default 5 | 6 | aws ec2 get-ebs-default-kms-key-id 7 | 8 | EBS_CMK_ARN=$(aws kms describe-key --key-id 'alias/EC2EncryptionAtRestCMKAlias' |jq --raw-output '.KeyMetadata.Arn') 9 | 10 | aws ec2 modify-ebs-default-kms-key-id --kms-key-id "$EBS_CMK_ARN" 11 | 12 | aws ec2 get-ebs-default-kms-key-id 13 | -------------------------------------------------------------------------------- /rds/CDK/README.md: -------------------------------------------------------------------------------- 1 | Create a virtual environment 2 | ``` 3 | python3 -m venv venv 4 | source venv/bin/activate 5 | ``` 6 | 7 | Install the tool locally 8 | ``` 9 | pip3 install -r requirements.txt 10 | ``` 11 | 12 | Bootstrap the environment for CDK 13 | ``` 14 | cdk bootstrap 15 | ``` 16 | 17 | Deploy the automation stack 18 | ``` 19 | cdk deploy unencrypted-to-encrypted-rds 20 | ``` 21 | 22 | For testing, deploy the test stack, test, and then destroy 23 | 1. Deploy the test resources ```cdk deploy test-rds-stack``` 24 | 2. Test by going to config and clicking remediate on the test resource. 25 | 3. Destroy the test resources ```cdk destroy test-rds-stack``` 26 | -------------------------------------------------------------------------------- /rds/CDK/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from aws_cdk import core 4 | 5 | from unencrypted_to_encrypted_rds.unencrypted_to_encrypted_rds_stack import UnencryptedToEncryptedRdsStack 6 | from unencrypted_to_encrypted_rds.test_rds_stack import RDSStack 7 | 8 | app = core.App() 9 | description="This sample, non-production-ready template create a config remediation rule to create encrypted RDS instances and clusters using the specific KMS key." 10 | UnencryptedToEncryptedRdsStack(app, "unencrypted-to-encrypted-rds", description=description) 11 | RDSStack(app,"test-rds-stack", description="This sample, non-production-ready template creating a test RDS instance and cluster in order to test remediation.") 12 | 13 | app.synth() 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## What's Changed 2 | * Updated to use KMS key instead of CMK in https://github.com/aws-samples/aws-system-manager-automation-unencrypted-to-encrypted-resources/pull/2 3 | * Removed references to “customer master key” (or any reference to “mas… in https://github.com/aws-samples/aws-system-manager-automation-unencrypted-to-encrypted-resources/pull/4 4 | * remove references to “customer master key” (or any reference to “mast… in https://github.com/aws-samples/aws-system-manager-automation-unencrypted-to-encrypted-resources/pull/6 5 | * Added EBS in https://github.com/aws-samples/aws-system-manager-automation-unencrypted-to-encrypted-resources/pull/7 6 | 7 | **Full Changelog**: https://github.com/aws-samples/aws-system-manager-automation-unencrypted-to-encrypted-resources/commits/v1.0.0 -------------------------------------------------------------------------------- /rds/SCP/rds_encrypted.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "StatementForNonAuroraRDS", 6 | "Effect": "Deny", 7 | "Action": [ 8 | "rds:CreateDBInstance" 9 | ], 10 | "Resource": [ 11 | "*" 12 | ], 13 | "Condition": { 14 | "StringNotLike": { 15 | "rds:DatabaseEngine": "aurora*" 16 | }, 17 | "Bool": { 18 | "rds:StorageEncrypted": "false" 19 | } 20 | } 21 | }, 22 | { 23 | "Sid": "StatementForAurora", 24 | "Effect": "Deny", 25 | "Action": [ 26 | "rds:CreateDBCluster" 27 | ], 28 | "Resource": [ 29 | "*" 30 | ], 31 | "Condition": { 32 | "Bool": { 33 | "rds:StorageEncrypted": "false" 34 | } 35 | } 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /rds/CDK/setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | 4 | with open("README.md") as fp: 5 | long_description = fp.read() 6 | 7 | 8 | setuptools.setup( 9 | name="unencrypted_to_encrypted_rds", 10 | version="0.0.1", 11 | 12 | description="An empty CDK Python app", 13 | long_description=long_description, 14 | long_description_content_type="text/markdown", 15 | 16 | author="author", 17 | 18 | package_dir={"": "unencrypted_to_encrypted_rds"}, 19 | packages=setuptools.find_packages(where="unencrypted_to_encrypted_rds"), 20 | 21 | install_requires=[ 22 | "aws-cdk.core", 23 | ], 24 | 25 | python_requires=">=3.6", 26 | 27 | classifiers=[ 28 | "Development Status :: 4 - Beta", 29 | 30 | "Intended Audience :: Developers", 31 | 32 | "License :: OSI Approved :: Apache Software License", 33 | 34 | "Programming Language :: JavaScript", 35 | "Programming Language :: Python :: 3 :: Only", 36 | "Programming Language :: Python :: 3.6", 37 | "Programming Language :: Python :: 3.7", 38 | "Programming Language :: Python :: 3.8", 39 | 40 | "Topic :: Software Development :: Code Generators", 41 | "Topic :: Utilities", 42 | 43 | "Typing :: Typed", 44 | ], 45 | ) 46 | -------------------------------------------------------------------------------- /rds/README.md: -------------------------------------------------------------------------------- 1 | # Automatically remediate unencrypted RDS Instances and Clusters using customer KMS keys 2 | 3 | This sample describes how to automatically remediate unencrypted RDS Instances and Clusters. Amazon RDS encrypted DB instances provide an additional layer of data protection by securing your data from unauthorized access to the underlying storage. You can use Amazon RDS encryption to increase data protection of your applications deployed in the cloud, and to fulfill compliance requirements for encryption at rest. 4 | 5 | ![RDS architecture diagram](../rds-architecture.png) 6 | 7 | ## Deployment Options 8 | 9 | The sample solution can be deployed using the AWS CloudFormation stack or AWS Cloud Development Kit (AWS CDK), which create remediation using Systems Manger Automation Document (SSM) that will unencrypted RDS instances and clusters by using the specific Customer KMS keys if not initially encrypted when created. 10 | 11 | * Option # 1: Deploy the [CloudFormation](CloudFormation) which will create the Remediation Rule that will encrypt the unencrypted RDS Instances and Clusters using your KMS key. 12 | 13 | * Option # 2: Deploy the [AWS Cloud Development Kit AWS CDK](CDK) which will create the Remediation Rule that will encrypt the unencrypted RDS Instances and Clusters using your KMS key. The CDK option also allows you to deploy a test stack, if you desire to evaluate running the remediation on an example RDS scenario. 14 | 15 | ## Enforce Service Control Policies 16 | 17 | Once the resources are remediated, enforce Service control policies(rds_encrypted.json) to deny DB instances and cluster creation in future without encryption. 18 | 19 | [SCPs](SCP) 20 | 21 | 22 | 23 | ## Security 24 | 25 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 26 | 27 | ## License 28 | 29 | This library is licensed under the MIT-0 License. See the LICENSE file. 30 | 31 | 32 | -------------------------------------------------------------------------------- /rds/CDK/unencrypted_to_encrypted_rds/test_rds_stack.py: -------------------------------------------------------------------------------- 1 | from aws_cdk import ( 2 | aws_ec2 as ec2, 3 | aws_rds as rds, 4 | core, 5 | ) 6 | 7 | 8 | class RDSStack(core.Stack): 9 | def __init__(self, app: core.App, id: str, **kwargs) -> None: 10 | super().__init__(app, id, **kwargs) 11 | 12 | vpc = ec2.Vpc(self, "VPC", max_azs=99) 13 | 14 | rds.DatabaseInstance( 15 | self, 16 | "RDS-instance", 17 | database_name="dbinstance1", 18 | engine=rds.DatabaseInstanceEngine.mysql( 19 | version=rds.MysqlEngineVersion.VER_8_0_16 20 | ), 21 | vpc=vpc, 22 | port=3306, 23 | instance_type=ec2.InstanceType.of( 24 | ec2.InstanceClass.MEMORY4, 25 | ec2.InstanceSize.LARGE, 26 | ), 27 | removal_policy=core.RemovalPolicy.DESTROY, 28 | deletion_protection=False, 29 | publicly_accessible=False, 30 | vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PRIVATE), 31 | allocated_storage=10, 32 | ), 33 | 34 | rds.DatabaseCluster( 35 | self, 36 | "RDS-cluster", 37 | cluster_identifier="dbcluster1", 38 | engine=rds.DatabaseClusterEngine.aurora_mysql( 39 | version=rds.AuroraMysqlEngineVersion.VER_2_08_1 40 | ), 41 | port=3306, 42 | instance_props=rds.InstanceProps( 43 | vpc=vpc, 44 | instance_type=ec2.InstanceType.of( 45 | ec2.InstanceClass.MEMORY4, 46 | ec2.InstanceSize.LARGE, 47 | ), 48 | publicly_accessible=False, 49 | vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PRIVATE), 50 | ), 51 | removal_policy=core.RemovalPolicy.DESTROY, 52 | deletion_protection=False, 53 | ) 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Automatically remediate unencrypted resources 2 | 3 | These samples describe how to automatically remediate uenencrypted resources such as EBS and RDS. 4 | 5 | ## Automatically remediate unencrypted EBS Volumes using customer KMS keys 6 | 7 | This [sample](ebs) describes how to automatically remediate unencrypted EBS Volumes. Amazon EBS encrypted volumes provide an additional layer of data protection by securing your data from unauthorized access to the underlying storage. You can use Amazon EBS encryption to increase data protection of your applications deployed in the cloud, and to fulfill compliance requirements for encryption at rest. 8 | 9 | ![EBS architecture diagram](ebs-architecture.png) 10 | 11 | The sample solution can be deployed using the AWS CloudFormation template, which creates a Config remediation using a Systems Manger (SSM) Automation Document that will encrypt EBS Volumes using a specific customer-managed KMS key if not initially encrypted when created. 12 | 13 | Once the resources are remediated, ensure preventive enforcement via Service Control Policies to deny EC2 instance and volume creation in the future without encryption. 14 | 15 | ## Automatically remediate unencrypted RDS Instances and Clusters using customer KMS keys 16 | 17 | This [sample](rds) describes how to automatically remediate unencrypted RDS Instances and Clusters. Amazon RDS encrypted DB instances provide an additional layer of data protection by securing your data from unauthorized access to the underlying storage. You can use Amazon RDS encryption to increase data protection of your applications deployed in the cloud, and to fulfill compliance requirements for encryption at rest. 18 | 19 | ![RDS architecture diagram](rds-architecture.png) 20 | 21 | The sample solution can be deployed using the AWS CloudFormation stack or AWS Cloud Development Kit (AWS CDK), which create remediation using Systems Manger Automation Document (SSM) that will unencrypted RDS instances and clusters by using the specific Customer KMS key if not initially encrypted when created. 22 | 23 | Once the resources are remediated, ensure preventive enforcement via Service control policies to deny DB instances and cluster creation in future without encryption. 24 | 25 | ## Security 26 | 27 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 28 | 29 | ## License 30 | 31 | This library is licensed under the MIT-0 License. See the LICENSE file. 32 | 33 | -------------------------------------------------------------------------------- /rds/SCP/README.md: -------------------------------------------------------------------------------- 1 | Prerequisites: 2 | 3 | 1. Access to AWS services, including: 4 | 1. AWS Config 5 | 2. AWS RDS 6 | 3. AWS System Manager Automation Document 7 | 4. AWS CloudFormation (Option #1) 8 | 5. AWS CDK (Option #2) 9 | 6. AWS Key Management Service (KMS) 10 | 7. AWS Identity and Access Management (IAM) 11 | 8. You must have AWS Config enabled in your AWS account. For more information, see [Getting Started with AWS Config](https://docs.aws.amazon.com/config/latest/developerguide/getting-started.html) 12 | 13 | Limitations: 14 | 15 | 1. You can enable encryption for an Amazon RDS DB instance only when you create it, not after the DB instance is created. 16 | 17 | 2. You can't have an encrypted read replica of an unencrypted DB instance or an unencrypted read replica of an encrypted DB instance. 18 | 19 | 3. You can't restore an unencrypted backup or snapshot to an encrypted DB instance. 20 | Amazon RDS encryption is available for most DB instance classes. The following table lists DB instance classes that do not support [Amazon RDS encryption](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.Encryption.html) 21 | 22 | 4. To copy an encrypted snapshot from one AWS Region to another, you must specify the KMS key in the destination AWS Region. This is because KMS keys are specific to the AWS Region that they are created in. 23 | 24 | 5. The source snapshot remains encrypted throughout the copy process. Amazon RDS uses envelope encryption to protect data during the copy process. For more information about envelope encryption, see Envelope encryption in the AWS Key Management Service Developer Guide. 25 | 26 | 6. You can't unencrypt an encrypted DB instance. However, you can export data from an encrypted DB instance and import the data into an unencrypted DB instance. 27 | 28 | 7. You should delete a KMS key only when you are sure that you don't need to use it anymore. If you are not sure, consider disabling the KMS key instead of deleting it. You can reenable a disabled KMS key if you need to use it again later, but you cannot recover a deleted KMS key. 29 | 30 | 8. If you don't choose to retain automated backups, your automated backups in the same AWS Region as the DB instance are deleted. They can't be recovered after you delete the DB instance. 31 | 32 | 9. Your automated backups are retained for the retention period that is set on the DB instance at the time when you delete it. This set retention period occurs whether or not you choose to create a final DB snapshot. 33 | -------------------------------------------------------------------------------- /rds/CloudFormation/README.md: -------------------------------------------------------------------------------- 1 | Prerequisites: 2 | 3 | 1. Access to AWS services, including: 4 | 1. AWS Config 5 | 2. AWS RDS 6 | 3. AWS System Manager Automation Document 7 | 4. AWS CloudFormation (Option #1) 8 | 5. AWS CDK (Option #2) 9 | 6. AWS Key Management Service (KMS) 10 | 7. AWS Identity and Access Management (IAM) 11 | 8. You must have AWS Config enabled in your AWS account. For more information, see [Getting Started with AWS Config](https://docs.aws.amazon.com/config/latest/developerguide/getting-started.html) 12 | 13 | Limitations: 14 | 15 | 1. You can enable encryption for an Amazon RDS DB instance only when you create it, not after the DB instance is created. 16 | 17 | 2. You can't have an encrypted read replica of an unencrypted DB instance or an unencrypted read replica of an encrypted DB instance. 18 | 19 | 3. You can't restore an unencrypted backup or snapshot to an encrypted DB instance. Amazon RDS encryption is available for most DB instance classes. The following table lists DB instance classes that do not support [Amazon RDS encryption](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.Encryption.html) 20 | 21 | 4. To copy an encrypted snapshot from one AWS Region to another, you must specify the KMS key in the destination AWS Region. This is because KMS keys are specific to the AWS Region that they are created in. 22 | 23 | 5. The source snapshot remains encrypted throughout the copy process. Amazon RDS uses envelope encryption to protect data during the copy process. For more information about envelope encryption, see Envelope encryption in the AWS Key Management Service Developer Guide. 24 | 25 | 6. You can't unencrypt an encrypted DB instance. However, you can export data from an encrypted DB instance and import the data into an unencrypted DB instance. 26 | 27 | 7. You should delete a KMS key only when you are sure that you don't need to use it anymore. If you are not sure, consider disabling the KMS key instead of deleting it. You can reenable a disabled KMS key if you need to use it again later, but you cannot recover a deleted KMS key. 28 | 29 | 8. If you don't choose to retain automated backups, your automated backups in the same AWS Region as the DB instance are deleted. They can't be recovered after you delete the DB instance. 30 | 31 | 9. Your automated backups are retained for the retention period that is set on the DB instance at the time when you delete it. This set retention period occurs whether or not you choose to create a final DB snapshot. 32 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /ebs/README.md: -------------------------------------------------------------------------------- 1 | # Automatically remediate unencrypted EBS Volumes using customer KMS keys 2 | 3 | This sample describes how to automatically remediate unencrypted EBS Volumes. Amazon EBS encrypted volumes provide an additional layer of data protection by securing your data from unauthorized access to the underlying storage. You can use Amazon EBS encryption to increase data protection of your applications deployed in the cloud, and to fulfill compliance requirements for encryption at rest. 4 | 5 | ![EBS architecture diagram](../ebs-architecture.png) 6 | 7 | ## Prerequisites 8 | A workstation with both AWS CLI and jq installed. Instructions on installation can be found in the Tools section. 9 | IAM permissions provisioned to have read and write access to CloudFormation, EC2, Systems Manager, Config, & KMS. 10 | AWS Organizations configured with all features enabled, a requirement for Service Control Policies. 11 | Config enabled in the target accounts ensuring there are no Config rules in your AWS accounts with the name "encrypted-volumes". This solution deploys a rule with this name. Existing Config rules with this name may result in failure on deployment. This can also result in unnecessary charges related to processing the same rule more than once. 12 | 13 | 14 | ## Limitations 15 | Although there is no direct way to encrypt an existing unencrypted volume or snapshot, you can encrypt them by creating either a volume or a snapshot. This pattern provides automation to remediate your unencrypted EBS volumes. 16 | This solution encrypts all EBS volumes with the same AWS KMS customer master key (CMK). 17 | If you choose to enable the account attribute for EBS encryption, be aware that encryption by default is a Region-specific setting. If you enable it for a Region, you cannot disable it for individual volumes or snapshots in that Region. 18 | 19 | 20 | ## Deployment 21 | 22 | * A new KMS CMK will be created as part of this pattern. Identify an IAM User or Role which will be the Key Admin for the new CMK. If a new user or role needs to be created for this purpose, create it now. 23 | * Once identified, copy the user or role's ARN and keep it handy as it will be used during the deployment of Stack1 in the next step. 24 | * Deploy _Stack1.yaml_ in CloudFormation. Give the stack a clear and descriptive name. Take note of the name as you will need it during the deployment of Stack2 in the next step. 25 | * Paste the ARN of your Key Admin into the only parameter field in Stack1. This IAM entity will be added as the Key Admin for the new CMK which the stack will create. 26 | * Deploy the attached _Stack2.yaml_ in CloudFormation. Give the stack a clear and descriptive name. 27 | * For the only parameter of Stack2, enter in the Stack Name entered during the previous step for deploying Stack1. This will allow Stack2 to reference the new CMK and Role that Stack1 deployed. 28 | * Create an EC2 instance with an unencrypted EBS volume. The instance type does not matter and access to the instance is not needed. As such, you can choose a t2.micro to stay in the free tier and there is no need to create a key pair. 29 | * In the AWS Console go to Services -> Config > Rules. Select the Rule ‘encrypted-volumes’. A list of non-compliant resources will be listed, including your newly-created volume. If the volume does not appear immediately, give it a few minutes. The Config rule is set to detect on resource changes, and will trigger shortly after the instance and volume are created. 30 | * Select the resource then click “Remediate”. 31 | * You can view the progress and status in Systems Manager. In the console go to “Systems Manager” -> “Automation”. Select the ‘Execution ID’ of the corresponding automation to view further details. 32 | 33 | 34 | ## Enforce Service Control Policies 35 | 36 | Navigate to AWS Organizations and create a new Service Control Policy. Add the contents of _DenyUnencryptedEC2.json_ to the policy and save it. Apply this policy to the organization Root or any necessary OUs. 37 | 38 | [SCP](SCP) 39 | 40 | ## Enable Default EBS Encryption 41 | 42 | In each account and region in use, run _enable-ebs-encryption-for-account.sh_ script from your workstation. 43 | 44 | [Bash](Bash) 45 | 46 | ## Security 47 | 48 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 49 | 50 | ## License 51 | 52 | This library is licensed under the MIT-0 License. See the LICENSE file. 53 | 54 | 55 | -------------------------------------------------------------------------------- /ebs/CloudFormation/Stack1.yaml: -------------------------------------------------------------------------------- 1 | Description: This sample, non-production-ready template creates the IAM Role and KMS Key for the EBS Encryption Remediation Process 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Parameters: 4 | KeyAdminParameter: 5 | Description: ARN of Key Admin IAM User or Role 6 | Type: String 7 | Resources: 8 | EncryptionRemediationPolicy: 9 | Type: AWS::IAM::ManagedPolicy 10 | Properties: 11 | ManagedPolicyName: EncryptEBSAutomationRole-policy 12 | Roles: 13 | - !Ref EncryptionRemediationRole 14 | PolicyDocument: 15 | Version: "2012-10-17" 16 | Statement: 17 | - Effect: Allow 18 | Action: 19 | - cloudformation:CreateStack 20 | - cloudformation:DescribeStacks 21 | - cloudformation:DeleteStack 22 | Resource: 23 | - !Sub 'arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/DetachEBSVolumeStack*' 24 | - !Sub 'arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/AttachEBSVolumeStack*' 25 | - Effect: Allow 26 | Action: 27 | - ec2:AttachVolume 28 | - ec2:CopySnapshot 29 | - ec2:CreateSnapshot 30 | - ec2:CreateVolume 31 | - ec2:CreateTags 32 | - ec2:DeleteSnapshot 33 | - ec2:DeleteVolume 34 | - ec2:DescribeInstances 35 | - ec2:DescribeInstanceStatus 36 | - ec2:DescribeNetworkInterfaces 37 | - ec2:DescribeSnapshots 38 | - ec2:DescribeVolumes 39 | - ec2:DescribeVolumes 40 | - ec2:ModifyInstanceAttribute 41 | - ec2:StartInstances 42 | - ec2:StopInstances 43 | - tag:TagResources 44 | - ssm:GetAutomationExecution 45 | - ssm:StartAutomationExecution 46 | Resource: '*' 47 | - Effect: Allow 48 | Action: 49 | - lambda:DeleteFunction 50 | - lambda:CreateFunction 51 | - lambda:GetFunction* 52 | - lambda:InvokeFunction 53 | Resource: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:DetachVolumeLambda*' 54 | - Effect: Allow 55 | Action: 56 | - kms:Encrypt 57 | - kms:Decrypt 58 | - kms:ReEncrypt* 59 | - kms:GenerateDataKey* 60 | - kms:CreateGrant 61 | - kms:ListGrants 62 | - kms:DescribeKey 63 | Resource: !Sub 'arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/${EBSEncryptionKey.KeyId}' 64 | - Effect: Allow 65 | Action: 66 | - iam:PassRole 67 | - iam:DeleteRole 68 | - iam:PutRolePolicy* 69 | - iam:CreateRole 70 | - iam:GetRole* 71 | - iam:DeleteRolePolicy 72 | Resource: !Sub 'arn:aws:iam::${AWS::AccountId}:role/DetachEBSVolumeStack*LambdaRole*' 73 | EncryptionRemediationRole: 74 | Type: AWS::IAM::Role 75 | Properties: 76 | AssumeRolePolicyDocument: 77 | Statement: 78 | - Action: sts:AssumeRole 79 | Effect: Allow 80 | Principal: 81 | Service: 82 | - ssm.amazonaws.com 83 | - ec2.amazonaws.com 84 | Version: "2012-10-17" 85 | EBSEncryptionKey: 86 | Type: AWS::KMS::Key 87 | Properties: 88 | Description: Key used for encryption EBS volumes 89 | Enabled: True 90 | EnableKeyRotation: True 91 | KeyPolicy: 92 | Version: '2012-10-17' 93 | Id: key-default-1 94 | Statement: 95 | - Sid: Enable IAM User Permissions 96 | Effect: Allow 97 | Principal: 98 | AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root' 99 | Action: kms:* 100 | Resource: '*' 101 | - Sid: Allow administration of the key 102 | Effect: Allow 103 | Principal: 104 | AWS: !Ref KeyAdminParameter 105 | Action: 106 | - kms:Create* 107 | - kms:Describe* 108 | - kms:Enable* 109 | - kms:List* 110 | - kms:Put* 111 | - kms:Update* 112 | - kms:Revoke* 113 | - kms:Disable* 114 | - kms:Get* 115 | - kms:Delete* 116 | - kms:ScheduleKeyDeletion 117 | - kms:CancelKeyDeletion 118 | Resource: '*' 119 | - Sid: Allow use of the key 120 | Effect: Allow 121 | Principal: 122 | AWS: !GetAtt EncryptionRemediationRole.Arn 123 | Action: 124 | - kms:DescribeKey 125 | - kms:Encrypt 126 | - kms:Decrypt 127 | - kms:ReEncrypt* 128 | - kms:GenerateDataKey 129 | - kms:GenerateDataKeyWithoutPlaintext 130 | Resource: '*' 131 | EBSEncryptionKeyAlias: 132 | Type: AWS::KMS::Alias 133 | Properties: 134 | AliasName: alias/EC2EncryptionAtRestKeyAlias 135 | TargetKeyId: !Ref EBSEncryptionKey 136 | Outputs: 137 | RoleARN: 138 | Description: ARN of the EncryptionRemediationRole 139 | Value: !GetAtt EncryptionRemediationRole.Arn 140 | Export: 141 | Name: !Sub "${AWS::StackName}-RoleARN" 142 | KeyID: 143 | Description: Key ID of EBSEncryptionKey 144 | Value: !Ref EBSEncryptionKey 145 | Export: 146 | Name: !Sub "${AWS::StackName}-KeyID" -------------------------------------------------------------------------------- /rds/CDK/aws-encrypt-rds-instance.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "0.3", 3 | "description": "Encrypt RDS Instance Automation Document", 4 | "assumeRole": "{{automationAssumeRole}}", 5 | "parameters": { 6 | "DBInstanceIdentifier": { 7 | "description": "(Required) The DBInstanceId ID of the RDS Instance to create Snapshot from.", 8 | "type": "String" 9 | }, 10 | "EncryptedDBInstanceIdentifier": { 11 | "description": "(Optional, default provided) The name of the id of the target encrypted DB Instance to create", 12 | "default": "encrypted-{{DBInstanceIdentifier}}", 13 | "type": "String" 14 | }, 15 | "EncryptedDBSnapshotIdentifier": { 16 | "description": "(Optional, default provided) The DBSnapshotIdentifier ID of the Encrypted RDS snapshot to create.", 17 | "default": "encrypted-db-instance-snapshot-{{DBInstanceIdentifier}}", 18 | "type": "String" 19 | }, 20 | "kmsKeyId": { 21 | "description": "(Required) Customer KMS key to use during the encryption", 22 | "type": "String" 23 | }, 24 | "automationAssumeRole": { 25 | "type": "String", 26 | "description": "(Required) The ARN of the role that allows Automation to perform the actions on your behalf." 27 | } 28 | }, 29 | "mainSteps": [ 30 | { 31 | "name": "createAndVerifySnapshot", 32 | "action": "aws:executeAutomation", 33 | "timeoutSeconds": 1800, 34 | "onFailure": "step:deleteSnapshot", 35 | "nextStep": "copyToEncryptedSnapshot", 36 | "maxAttempts": 3, 37 | "inputs": { 38 | "DocumentName": "AWS-CreateRdsSnapshot", 39 | "RuntimeParameters": { 40 | "DBInstanceIdentifier": "{{DBInstanceIdentifier}}", 41 | "DBSnapshotIdentifier": "unencrypted-{{EncryptedDBSnapshotIdentifier}}", 42 | "AutomationAssumeRole": "{{automationAssumeRole}}" 43 | } 44 | } 45 | }, 46 | { 47 | "name": "copyToEncryptedSnapshot", 48 | "action": "aws:executeAwsApi", 49 | "timeoutSeconds": 300, 50 | "onFailure": "step:deleteEncryptedSnapshot", 51 | "nextStep": "waitForEncryptedSnapshot", 52 | "maxAttempts": 1, 53 | "inputs": { 54 | "Service": "rds", 55 | "Api": "CopyDBSnapshot", 56 | "KmsKeyId": "{{kmsKeyId}}", 57 | "TargetDBSnapshotIdentifier": "{{EncryptedDBSnapshotIdentifier}}", 58 | "SourceDBSnapshotIdentifier": "unencrypted-{{EncryptedDBSnapshotIdentifier}}" 59 | } 60 | }, 61 | { 62 | "name": "waitForEncryptedSnapshot", 63 | "action": "aws:waitForAwsResourceProperty", 64 | "timeoutSeconds": 1800, 65 | "onFailure": "step:deleteEncryptedSnapshot", 66 | "nextStep": "createEncryptedRDSInstanceFromSnapshot", 67 | "maxAttempts": 1, 68 | "inputs": { 69 | "Service": "rds", 70 | "Api": "DescribeDBSnapshots", 71 | "DBSnapshotIdentifier": "{{EncryptedDBSnapshotIdentifier}}", 72 | "PropertySelector": "$.DBSnapshots[0].Status", 73 | "DesiredValues": [ 74 | "available" 75 | ] 76 | } 77 | }, 78 | { 79 | "name": "createEncryptedRDSInstanceFromSnapshot", 80 | "action": "aws:executeAwsApi", 81 | "timeoutSeconds": 300, 82 | "onFailure": "step:deleteEncryptedSnapshot", 83 | "nextStep": "waitForEncryptedRDSInstance", 84 | "maxAttempts": 1, 85 | "inputs": { 86 | "Service": "rds", 87 | "Api": "RestoreDBInstanceFromDBSnapshot", 88 | "DBInstanceIdentifier": "{{EncryptedDBInstanceIdentifier}}", 89 | "DBSnapshotIdentifier": "{{EncryptedDBSnapshotIdentifier}}", 90 | "MultiAZ": true 91 | } 92 | }, 93 | { 94 | "name": "waitForEncryptedRDSInstance", 95 | "action": "aws:waitForAwsResourceProperty", 96 | "timeoutSeconds": 1200, 97 | "onFailure": "step:deleteEncryptedSnapshot", 98 | "nextStep": "deleteEncryptedSnapshot", 99 | "maxAttempts": 1, 100 | "inputs": { 101 | "Service": "rds", 102 | "Api": "DescribeDBInstances", 103 | "DBInstanceIdentifier": "{{EncryptedDBInstanceIdentifier}}", 104 | "PropertySelector": "$.DBInstances[0].DBInstanceStatus", 105 | "DesiredValues": [ 106 | "available" 107 | ] 108 | } 109 | }, 110 | { 111 | "name": "deleteEncryptedSnapshot", 112 | "action": "aws:executeAwsApi", 113 | "timeoutSeconds": 300, 114 | "onFailure": "Continue", 115 | "nextStep": "deleteSnapshot", 116 | "maxAttempts": 1, 117 | "inputs": { 118 | "Service": "rds", 119 | "Api": "DeleteDBSnapshot", 120 | "DBSnapshotIdentifier": "{{EncryptedDBSnapshotIdentifier}}" 121 | } 122 | }, 123 | { 124 | "name": "deleteSnapshot", 125 | "action": "aws:executeAwsApi", 126 | "timeoutSeconds": 300, 127 | "onFailure": "Continue", 128 | "isEnd": true, 129 | "maxAttempts": 1, 130 | "inputs": { 131 | "Service": "rds", 132 | "Api": "DeleteDBSnapshot", 133 | "DBSnapshotIdentifier": "unencrypted-{{EncryptedDBSnapshotIdentifier}}" 134 | } 135 | } 136 | ], 137 | "outputs": [ 138 | "createAndVerifySnapshot.Output" 139 | ] 140 | } -------------------------------------------------------------------------------- /rds/CDK/aws-encrypt-rds.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "0.3", 3 | "description": "Encrypt RDS Automation Document", 4 | "assumeRole": "{{automationAssumeRole}}", 5 | "parameters": { 6 | "instanceAutomationDocument": { 7 | "description": "(Required) Document Id of the RDS instance encryption automation", 8 | "type": "String" 9 | }, 10 | "clusterAutomationDocument": { 11 | "description": "(Required) Document Id of the RDS cluster encryption automation", 12 | "type": "String" 13 | }, 14 | "resourceId": { 15 | "description": "(Required) RDS Instance Id of the resource to be remediated", 16 | "type": "String" 17 | }, 18 | "kmsKeyId": { 19 | "description": "(Required) Customer KMS key to use during the encryption", 20 | "type": "String" 21 | }, 22 | "automationAssumeRole": { 23 | "type": "String", 24 | "description": "(Required) The ARN of the role that allows Automation to perform the actions on your behalf." 25 | } 26 | }, 27 | "mainSteps": [ 28 | { 29 | "name": "determineInstanceOrCluster", 30 | "action": "aws:executeScript", 31 | "description": "Determines whether the instance is a standalone or part of a cluster", 32 | "timeoutSeconds": 300, 33 | "onFailure": "Abort", 34 | "nextStep": "chooseInstanceOrCluster", 35 | "maxAttempts": 1, 36 | "outputs": [ 37 | { 38 | "Name": "InstanceClass", 39 | "Selector": "$.Payload.instance_class", 40 | "Type": "String" 41 | }, 42 | { 43 | "Name": "IsCluster", 44 | "Selector": "$.Payload.is_cluster", 45 | "Type": "Boolean" 46 | }, 47 | { 48 | "Name": "ClusterId", 49 | "Selector": "$.Payload.cluster_id", 50 | "Type": "String" 51 | }, 52 | { 53 | "Name": "DBInstanceIdentifier", 54 | "Selector": "$.Payload.db_instance_id", 55 | "Type": "String" 56 | } 57 | ], 58 | "inputs": { 59 | "Runtime": "python3.6", 60 | "Handler": "handler", 61 | "InputPayload": { 62 | "DBResourceIdentifier": "{{resourceId}}" 63 | }, 64 | "Script": "import boto3\n\nDB_RESOURCE_ID = \"DBResourceIdentifier\"\n\ndef handler(event, context):\n rds = boto3.client('rds')\n\n resource_id = event[DB_RESOURCE_ID]\n result=rds.describe_db_instances(\n Filters=[\n {\n 'Name': 'dbi-resource-id',\n 'Values': [\n resource_id\n ]\n },\n ],\n )\n\n info=result[\"DBInstances\"][0]\n instance_class=info[\"DBInstanceClass\"]\n db_instance_id=info[\"DBInstanceIdentifier\"]\n is_cluster=False\n cluster_id=\"\"\n if \"DBClusterIdentifier\" in info:\n is_cluster=True\n cluster_id=info[\"DBClusterIdentifier\"]\n\n found= {\n 'db_instance_id': db_instance_id,\n 'instance_class' : instance_class,\n 'cluster_id': cluster_id,\n 'is_cluster': is_cluster\n }\n return(found)" 65 | } 66 | }, 67 | { 68 | "name": "chooseInstanceOrCluster", 69 | "action": "aws:branch", 70 | "description": "Branch to Instance or Cluster automation", 71 | "timeoutSeconds": 300, 72 | "onFailure": "Abort", 73 | "isEnd": true, 74 | "maxAttempts": 1, 75 | "inputs": { 76 | "Choices": [ 77 | { 78 | "NextStep": "rdsInstanceEncryptionAutomation", 79 | "Variable": "{{determineInstanceOrCluster.IsCluster}}", 80 | "BooleanEquals": false 81 | }, 82 | { 83 | "NextStep": "rdsClusterEncryptionAutomation", 84 | "Variable": "{{determineInstanceOrCluster.IsCluster}}", 85 | "BooleanEquals": true 86 | } 87 | ] 88 | } 89 | }, 90 | { 91 | "name": "rdsClusterEncryptionAutomation", 92 | "action": "aws:executeAutomation", 93 | "timeoutSeconds": 1800, 94 | "onFailure": "Abort", 95 | "isEnd": true, 96 | "maxAttempts": 1, 97 | "inputs": { 98 | "DocumentName": "{{clusterAutomationDocument}}", 99 | "RuntimeParameters": { 100 | "DBInstanceClass": [ 101 | "{{determineInstanceOrCluster.InstanceClass}}" 102 | ], 103 | "SourceDBClusterIdentifier": [ 104 | "{{determineInstanceOrCluster.ClusterId}}" 105 | ], 106 | "kmsKeyId": [ 107 | "{{kmsKeyId}}" 108 | ], 109 | "automationAssumeRole": [ 110 | "{{automationAssumeRole}}" 111 | ] 112 | } 113 | } 114 | }, 115 | { 116 | "name": "rdsInstanceEncryptionAutomation", 117 | "action": "aws:executeAutomation", 118 | "timeoutSeconds": 1800, 119 | "onFailure": "Abort", 120 | "isEnd": true, 121 | "maxAttempts": 1, 122 | "inputs": { 123 | "DocumentName": "{{instanceAutomationDocument}}", 124 | "RuntimeParameters": { 125 | "DBInstanceIdentifier": [ 126 | "{{determineInstanceOrCluster.DBInstanceIdentifier}}" 127 | ], 128 | "kmsKeyId": [ 129 | "{{kmsKeyId}}" 130 | ], 131 | "automationAssumeRole": [ 132 | "{{automationAssumeRole}}" 133 | ] 134 | } 135 | } 136 | } 137 | ] 138 | } -------------------------------------------------------------------------------- /rds/CDK/aws-encrypt-rds-cluster.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "0.3", 3 | "description": "Encrypt RDS Cluster Automation Document", 4 | "assumeRole": "{{automationAssumeRole}}", 5 | "parameters": { 6 | "DBInstanceClass": { 7 | "description": "(Required) The compute and memory capacity of the DB instance, for example, db.m4.large.", 8 | "type": "String" 9 | }, 10 | "SourceDBClusterIdentifier": { 11 | "description": "(Required) The DB Cluster Identifier of the RDS Cluster to create Snapshot from.", 12 | "type": "String" 13 | }, 14 | "EncryptedDBClusterIdentifier": { 15 | "description": "(Optional, default provided) The name of the id of the target encrypted DB Instance to create", 16 | "default": "encrypted-{{SourceDBClusterIdentifier}}", 17 | "type": "String" 18 | }, 19 | "DBClusterSnapshotIdentifier": { 20 | "description": "(Optional, default provided) The Encrypted DBClusterSnapshotIdentifier ID of the RDS snapshot to create.", 21 | "default": "unencrypted-db-cluster-snapshot-{{SourceDBClusterIdentifier}}", 22 | "type": "String" 23 | }, 24 | "kmsKeyId": { 25 | "description": "(Required) Customer KMS key to use during the encryption", 26 | "type": "String" 27 | }, 28 | "automationAssumeRole": { 29 | "type": "String", 30 | "description": "(Required) The ARN of the role that allows Automation to perform the actions on your behalf." 31 | } 32 | }, 33 | "mainSteps": [ 34 | { 35 | "name": "createRDSClusterSnapshot", 36 | "action": "aws:executeAwsApi", 37 | "timeoutSeconds": 300, 38 | "onFailure": "step:deleteSnapshotCluster", 39 | "nextStep": "waitForSnapshot", 40 | "maxAttempts": 1, 41 | "inputs": { 42 | "Service": "rds", 43 | "Api": "CreateDBClusterSnapshot", 44 | "DBClusterIdentifier": "{{SourceDBClusterIdentifier}}", 45 | "DBClusterSnapshotIdentifier": "{{DBClusterSnapshotIdentifier}}" 46 | }, 47 | "outputs": [ 48 | { 49 | "Name": "engine", 50 | "Selector": "$.DBClusterSnapshot.Engine", 51 | "Type": "String" 52 | } 53 | ] 54 | }, 55 | { 56 | "name": "waitForSnapshot", 57 | "action": "aws:waitForAwsResourceProperty", 58 | "timeoutSeconds": 1800, 59 | "onFailure": "Abort", 60 | "nextStep": "createEncryptedRDSClusterFromSnapshot", 61 | "maxAttempts": 1, 62 | "inputs": { 63 | "Service": "rds", 64 | "Api": "DescribeDBClusterSnapshots", 65 | "DBClusterSnapshotIdentifier": "{{DBClusterSnapshotIdentifier}}", 66 | "PropertySelector": "$.DBClusterSnapshots[0].Status", 67 | "DesiredValues": [ 68 | "available" 69 | ] 70 | } 71 | }, 72 | { 73 | "name": "createEncryptedRDSClusterFromSnapshot", 74 | "action": "aws:executeAwsApi", 75 | "timeoutSeconds": 300, 76 | "onFailure": "step:deleteSnapshotCluster", 77 | "nextStep": "waitForEncryptedRDSCluster", 78 | "maxAttempts": 1, 79 | "inputs": { 80 | "Service": "rds", 81 | "Api": "RestoreDBClusterFromSnapshot", 82 | "DBClusterIdentifier": "{{EncryptedDBClusterIdentifier}}", 83 | "SnapshotIdentifier": "{{DBClusterSnapshotIdentifier}}", 84 | "Engine": "{{createRDSClusterSnapshot.engine}}", 85 | "KmsKeyId": "{{kmsKeyId}}" 86 | } 87 | }, 88 | { 89 | "name": "waitForEncryptedRDSCluster", 90 | "action": "aws:waitForAwsResourceProperty", 91 | "timeoutSeconds": 1200, 92 | "onFailure": "step:deleteSnapshotCluster", 93 | "nextStep": "createEncryptedRDSInstanceInCluster", 94 | "maxAttempts": 1, 95 | "inputs": { 96 | "Service": "rds", 97 | "Api": "DescribeDBClusters", 98 | "DBClusterIdentifier": "{{EncryptedDBClusterIdentifier}}", 99 | "PropertySelector": "$.DBClusters[0].Status", 100 | "DesiredValues": [ 101 | "available" 102 | ] 103 | } 104 | }, 105 | { 106 | "name": "createEncryptedRDSInstanceInCluster", 107 | "action": "aws:executeAwsApi", 108 | "timeoutSeconds": 300, 109 | "onFailure": "step:deleteSnapshotCluster", 110 | "nextStep": "waitForEncryptedRDSInstance", 111 | "maxAttempts": 1, 112 | "inputs": { 113 | "Service": "rds", 114 | "Api": "CreateDBInstance", 115 | "DBClusterIdentifier": "{{EncryptedDBClusterIdentifier}}", 116 | "DBInstanceIdentifier": "instance-1-{{EncryptedDBClusterIdentifier}}", 117 | "DBInstanceClass": "{{DBInstanceClass}}", 118 | "Engine": "{{createRDSClusterSnapshot.engine}}" 119 | } 120 | }, 121 | { 122 | "name": "waitForEncryptedRDSInstance", 123 | "action": "aws:waitForAwsResourceProperty", 124 | "timeoutSeconds": 1200, 125 | "onFailure": "step:deleteSnapshotCluster", 126 | "nextStep": "deleteSnapshotCluster", 127 | "maxAttempts": 1, 128 | "inputs": { 129 | "Service": "rds", 130 | "Api": "DescribeDBInstances", 131 | "DBInstanceIdentifier": "instance-1-{{EncryptedDBClusterIdentifier}}", 132 | "PropertySelector": "$.DBInstances[0].DBInstanceStatus", 133 | "DesiredValues": [ 134 | "available" 135 | ] 136 | } 137 | }, 138 | { 139 | "name": "deleteSnapshotCluster", 140 | "action": "aws:executeAwsApi", 141 | "timeoutSeconds": 300, 142 | "onFailure": "Abort", 143 | "isEnd": true, 144 | "maxAttempts": 1, 145 | "inputs": { 146 | "Service": "rds", 147 | "Api": "DeleteDBClusterSnapshot", 148 | "DBClusterSnapshotIdentifier": "{{DBClusterSnapshotIdentifier}}" 149 | } 150 | } 151 | ] 152 | } -------------------------------------------------------------------------------- /rds/CDK/unencrypted_to_encrypted_rds/unencrypted_to_encrypted_rds_stack.py: -------------------------------------------------------------------------------- 1 | # © 2021 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. 2 | # This AWS Content is provided subject to the terms of the AWS Customer Agreement 3 | # available at http://aws.amazon.com/agreement or other written agreement between 4 | # Customer and either Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. 5 | 6 | # This is sample, non-production-ready template create a config remediation rule to create encrypted RDS instances and clusters using the specific KMS key. 7 | 8 | # Prerequisites 9 | # An active AWS account 10 | # The KMS key key should already exist to encrypt RDS Instances and Clusters 11 | # Plesae ensure you have access to update KMS KMS key Resource policy 12 | # An unencrypted Amazon RDS DB instance or Clusters 13 | # Access to AWS services, including: 14 | # AWS Config 15 | # AWS RDS 16 | # AWS System Manager Automation Document 17 | # AWS CloudFormation (Option #1) 18 | # AWS CDK (Option #2) 19 | # AWS Key Management Service (KMS) 20 | # AWS Identity and Access Management (IAM) 21 | # You must have AWS Config enabled in your AWS account. For more information, see Getting Started with AWS Config. 22 | 23 | # You can enable encryption for an Amazon RDS DB instance only when you create it, not after the DB instance is created. 24 | # You can't have an encrypted read replica of an unencrypted DB instance or an unencrypted read replica of an encrypted DB instance. 25 | # You can't restore an unencrypted backup or snapshot to an encrypted DB instance. 26 | # Amazon RDS encryption is available for most DB instance classes. The following table lists DB instance classes that do not support Amazon RDS encryption: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.Encryption.html 27 | # To copy an encrypted snapshot from one AWS Region to another, you must specify the KMS key in the destination AWS Region. This is because KMS keys are specific to the AWS Region that they are created in. 28 | # The source snapshot remains encrypted throughout the copy process. Amazon RDS uses envelope encryption to protect data during the copy process. For more information about envelope encryption, see Envelope encryption in the AWS Key Management Service Developer Guide. 29 | # You can't unencrypt an encrypted DB instance. However, you can export data from an encrypted DB instance and import the data into an unencrypted DB instance. 30 | # You should delete a KMS key only when you are sure that you don't need to use it anymore. If you are not sure, consider disabling the KMS key instead of deleting it. You can reenable a disabled KMS key if you need to use it again later, but you cannot recover a deleted KMS key. 31 | # If you don't choose to retain automated backups, your automated backups in the same AWS Region as the DB instance are deleted. They can't be recovered after you delete the DB instance. 32 | # Your automated backups are retained for the retention period that is set on the DB instance at the time when you delete it. This set retention period occurs whether or not you choose to create a final DB snapshot. 33 | 34 | import json 35 | 36 | from aws_cdk import core, aws_iam as iam, aws_ssm, aws_config, aws_kms 37 | 38 | 39 | class UnencryptedToEncryptedRdsStack(core.Stack): 40 | def __init__(self, scope: core.Construct, id: str, **kwargs) -> None: 41 | super().__init__(scope, id, **kwargs) 42 | 43 | """ 44 | kms_key_id_param = core.CfnParameter( 45 | self, 46 | "kmskeyid", 47 | type="String", 48 | description="The KMS key id used to encrypt", 49 | ) 50 | 51 | kms_key_id_value = kms_key_id_param.value_as_string 52 | """ 53 | 54 | # user guide example with assumerolepolicydocument 55 | # https://docs.aws.amazon.com/systems-manager/latest/userguide/automation-cf.html 56 | # cloudformation example https://docs.aws.amazon.com/systems-manager/latest/userguide/samples/AWS-SystemsManager-AutomationServiceRole.zip 57 | automation_role = iam.Role( 58 | self, 59 | "EncryptRDSAutomationRole", 60 | # role_name="EncryptRDSAutomationRole", 61 | assumed_by=iam.CompositePrincipal( 62 | iam.ServicePrincipal("ssm.amazonaws.com"), 63 | iam.ServicePrincipal("rds.amazonaws.com"), 64 | ), 65 | ) 66 | 67 | automation_role.grant_pass_role(automation_role) 68 | 69 | rds_key = aws_kms.Key( 70 | self, 71 | "rds-encryption-key", 72 | alias="alias/RDSEncryptionAtRestKMSAlias", 73 | enable_key_rotation=True, 74 | ) 75 | 76 | # Setup Role Permissions 77 | automation_role_policy = iam.ManagedPolicy( 78 | self, 79 | f"EncryptRDSAutomationRole-policy", 80 | description="Permissions for the RDS encrypt unencrypted", 81 | path="/", 82 | statements=[ 83 | iam.PolicyStatement( 84 | effect=iam.Effect.ALLOW, 85 | actions=[ 86 | "ec2:DescribeAvailabilityZones", 87 | "ec2:DescribeInternetGateways", 88 | "ec2:DescribeSecurityGroups", 89 | "ec2:DescribeSubnets", 90 | "ec2:DescribeVpcAttribute", 91 | "ec2:DescribeVpcs", 92 | "rds:AddTagsToResource", 93 | "ssm:GetAutomationExecution", 94 | ], 95 | resources=["*"], 96 | ), 97 | iam.PolicyStatement( 98 | effect=iam.Effect.ALLOW, 99 | actions=["kms:CreateGrant", "kms:DescribeKey"], 100 | resources=[ 101 | f"arn:aws:kms:{core.Aws.REGION}:{core.Aws.ACCOUNT_ID}:key/{rds_key.key_id}" 102 | ], 103 | ), 104 | iam.PolicyStatement( 105 | effect=iam.Effect.ALLOW, 106 | actions=[ 107 | "rds:CreateDBClusterSnapshot", 108 | "rds:DeleteDBClusterSnapshot", 109 | "rds:DescribeDBClusterSnapshots", 110 | "rds:RestoreDBClusterFromSnapshot", 111 | ], 112 | resources=[ 113 | f"arn:aws:rds:{core.Aws.REGION}:{core.Aws.ACCOUNT_ID}:cluster-snapshot:*" 114 | ], 115 | ), 116 | iam.PolicyStatement( 117 | effect=iam.Effect.ALLOW, 118 | actions=[ 119 | "rds:CreateDBClusterSnapshot", 120 | "rds:DescribeDBClusters", 121 | "rds:RestoreDBClusterFromSnapshot", 122 | ], 123 | resources=[ 124 | f"arn:aws:rds:{core.Aws.REGION}:{core.Aws.ACCOUNT_ID}:cluster:*" 125 | ], 126 | ), 127 | iam.PolicyStatement( 128 | effect=iam.Effect.ALLOW, 129 | actions=[ 130 | "rds:CreateDBInstance", 131 | "rds:CreateDBSnapshot", 132 | "rds:DescribeDBInstances", 133 | "rds:RestoreDBInstanceFromDBSnapshot", 134 | ], 135 | resources=[ 136 | f"arn:aws:rds:{core.Aws.REGION}:{core.Aws.ACCOUNT_ID}:db:*", 137 | f"arn:aws:rds:{core.Aws.REGION}:{core.Aws.ACCOUNT_ID}:cluster:*", 138 | ], 139 | ), 140 | iam.PolicyStatement( 141 | effect=iam.Effect.ALLOW, 142 | actions=[ 143 | "rds:CreateDBInstance", 144 | "rds:RestoreDBClusterFromSnapshot", 145 | "rds:RestoreDBInstanceFromDBSnapshot", 146 | ], 147 | resources=[ 148 | f"arn:aws:rds:{core.Aws.REGION}:{core.Aws.ACCOUNT_ID}:og:*" 149 | ], 150 | ), 151 | iam.PolicyStatement( 152 | effect=iam.Effect.ALLOW, 153 | actions=["rds:CreateDBInstance"], 154 | resources=[ 155 | f"arn:aws:rds:{core.Aws.REGION}:{core.Aws.ACCOUNT_ID}:secgrp:*", 156 | f"arn:aws:rds:{core.Aws.REGION}:{core.Aws.ACCOUNT_ID}:pg:*", 157 | ], 158 | ), 159 | iam.PolicyStatement( 160 | effect=iam.Effect.ALLOW, 161 | actions=[ 162 | "rds:CopyDBSnapshot", 163 | "rds:CreateDBSnapshot", 164 | "rds:DeleteDBSnapshot", 165 | "rds:DescribeDBSnapshots", 166 | "rds:RestoreDBInstanceFromDBSnapshot", 167 | ], 168 | resources=[ 169 | f"arn:aws:rds:{core.Aws.REGION}:{core.Aws.ACCOUNT_ID}:snapshot:*" 170 | ], 171 | ), 172 | iam.PolicyStatement( 173 | effect=iam.Effect.ALLOW, 174 | actions=[ 175 | "rds:CreateDBInstance", 176 | "rds:RestoreDBInstanceFromDBSnapshot", 177 | ], 178 | resources=[ 179 | f"arn:aws:rds:{core.Aws.REGION}:{core.Aws.ACCOUNT_ID}:subgrp:*" 180 | ], 181 | ), 182 | iam.PolicyStatement( 183 | effect=iam.Effect.ALLOW, 184 | actions=["ssm:StartAutomationExecution"], 185 | resources=[ 186 | # f"arn:aws:ssm:{core.Aws.REGION}:{core.Aws.ACCOUNT_ID}:automation-definition/*:*", 187 | "*", 188 | ], 189 | ), 190 | ], 191 | ) 192 | 193 | automation_role.add_managed_policy(automation_role_policy) 194 | 195 | encrypt_rds_json = "aws-encrypt-rds.json" 196 | encrypt_rds_cluster_json = "aws-encrypt-rds-cluster.json" 197 | encrypt_rds_instance_json = "aws-encrypt-rds-instance.json" 198 | with open(encrypt_rds_json, "r") as file_encrypt_rds_json, open( 199 | encrypt_rds_instance_json, "r" 200 | ) as file_encrypt_rds_instance_json, open( 201 | encrypt_rds_cluster_json, "r" 202 | ) as file_encrypt_rds_cluster_json: 203 | 204 | encrypt_rds_content = json.load(file_encrypt_rds_json) 205 | encrypt_rds_instance_content = json.load(file_encrypt_rds_instance_json) 206 | encrypt_rds_cluster_content = json.load(file_encrypt_rds_cluster_json) 207 | 208 | encrypt_rds_cluster_ssmdoc = aws_ssm.CfnDocument( 209 | self, 210 | "ENCRYPT-unencryptedrdscluster", 211 | content=encrypt_rds_cluster_content, 212 | document_type="Automation", 213 | ) 214 | 215 | encrypt_rds_instance_ssmdoc = aws_ssm.CfnDocument( 216 | self, 217 | "ENCRYPT-unencryptedrdsinstance", 218 | content=encrypt_rds_instance_content, 219 | document_type="Automation", 220 | ) 221 | 222 | encrypt_rds_ssmdoc = aws_ssm.CfnDocument( 223 | self, 224 | "ENCRYPT-unencryptedrds", 225 | content=encrypt_rds_content, 226 | document_type="Automation", 227 | ) 228 | 229 | rds_storage_encrypted_config_rule_name = ( 230 | "rds-storage-encrypted-with-remediation" 231 | ) 232 | 233 | aws_config.ManagedRule( 234 | self, 235 | rds_storage_encrypted_config_rule_name, 236 | config_rule_name=rds_storage_encrypted_config_rule_name, 237 | identifier="RDS_STORAGE_ENCRYPTED", 238 | description="Checks whether storage encryption is enabled for your RDS DB instances.", 239 | ) 240 | configremediation = aws_config.CfnRemediationConfiguration( 241 | self, 242 | "EncryptRDSConfigRemediation", 243 | config_rule_name=rds_storage_encrypted_config_rule_name, 244 | target_id=encrypt_rds_ssmdoc.ref, 245 | target_type="SSM_DOCUMENT", 246 | automatic=False, 247 | parameters={ 248 | "instanceAutomationDocument": { 249 | "StaticValue": {"Values": [encrypt_rds_instance_ssmdoc.ref]}, 250 | }, 251 | "clusterAutomationDocument": { 252 | "StaticValue": {"Values": [encrypt_rds_cluster_ssmdoc.ref]}, 253 | }, 254 | "automationAssumeRole": { 255 | "StaticValue": {"Values": [automation_role.role_arn]} 256 | }, 257 | "kmsKeyId": {"StaticValue": {"Values": [rds_key.key_id]}}, 258 | "resourceId": {"ResourceValue": {"Value": "RESOURCE_ID"}}, 259 | }, 260 | resource_type="AWS::RDS::DBInstance", 261 | target_version="1", 262 | ) 263 | -------------------------------------------------------------------------------- /ebs/CloudFormation/Stack2.yaml: -------------------------------------------------------------------------------- 1 | Description: This sample, non-production-ready template create a config remediation rule to create encrypted EBS volumes using the specific Key. 2 | Parameters: 3 | RoleKeyStackNameParameter: 4 | Description: Stack Name used when creating the IAM role and KMS Key 5 | Type: String 6 | Resources: 7 | EncryptedVolumesConfigRule: 8 | Type: AWS::Config::ConfigRule 9 | Properties: 10 | ConfigRuleName: "encrypted-volumes" 11 | Scope: 12 | ComplianceResourceTypes: 13 | - "AWS::EC2::Volume" 14 | Description: "A Config rule that checks whether the EBS volumes that are in an attached state are encrypted." 15 | Source: 16 | Owner: "AWS" 17 | SourceIdentifier: "ENCRYPTED_VOLUMES" 18 | ENCRYPTunencryptedebsvolume: 19 | Type: AWS::SSM::Document 20 | Properties: 21 | Content: 22 | schemaVersion: "0.3" 23 | description: Encrypt EBS Volume Automation Document 24 | assumeRole: "{{automationAssumeRole}}" 25 | parameters: 26 | volumeId: 27 | description: (Required) Volume ID of the EBS volume attached to an ec2 instance whose volume needs to be encrypted 28 | type: String 29 | kmsKeyId: 30 | description: (Required) Customer KMS key to use during the encryption 31 | type: String 32 | default: 33 | Fn::ImportValue: 34 | !Sub "${RoleKeyStackNameParameter}-KeyID" 35 | automationAssumeRole: 36 | type: String 37 | description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf. 38 | default: 39 | Fn::ImportValue: 40 | !Sub "${RoleKeyStackNameParameter}-RoleARN" 41 | mainSteps: 42 | - name: describeVolume 43 | action: aws:executeAwsApi 44 | timeoutSeconds: 30 45 | onFailure: Abort 46 | nextStep: describeInstance 47 | maxAttempts: 1 48 | inputs: 49 | Service: ec2 50 | Api: DescribeVolumes 51 | VolumeIds: 52 | - "{{volumeId}}" 53 | outputs: 54 | - Name: instanceId 55 | Selector: $.Volumes[0].Attachments[0].InstanceId 56 | Type: String 57 | - Name: availabilityZone 58 | Selector: $.Volumes[0].AvailabilityZone 59 | Type: String 60 | - Name: deviceVolumeType 61 | Selector: $.Volumes[0].VolumeType 62 | Type: String 63 | - Name: deleteOnTermination 64 | Selector: $.Volumes[0].Attachments[0].DeleteOnTermination 65 | Type: Boolean 66 | - Name: deviceMount 67 | Selector: $.Volumes[0].Attachments[0].Device 68 | Type: String 69 | - name: describeInstance 70 | action: aws:executeAwsApi 71 | timeoutSeconds: 30 72 | onFailure: Abort 73 | nextStep: createSnapshot 74 | maxAttempts: 1 75 | inputs: 76 | Service: ec2 77 | Api: DescribeInstances 78 | InstanceIds: 79 | - "{{describeVolume.instanceId}}" 80 | outputs: 81 | - Name: availabilityZone 82 | Selector: $.Reservations[0].Instances[0].Placement.AvailabilityZone 83 | Type: String 84 | - Name: instanceState 85 | Selector: $.Reservations[0].Instances[0].State.Name 86 | Type: String 87 | - name: createSnapshot 88 | action: aws:executeAutomation 89 | timeoutSeconds: 1800 90 | onFailure: Abort 91 | nextStep: extractSnapshotId 92 | maxAttempts: 3 93 | inputs: 94 | DocumentName: AWS-CreateSnapshot 95 | RuntimeParameters: 96 | VolumeId: "{{volumeId}}" 97 | - name: extractSnapshotId 98 | action: aws:executeAwsApi 99 | timeoutSeconds: 30 100 | onFailure: step:deleteRootVolumeSnapshot 101 | nextStep: copyAndEncryptSnapshot 102 | maxAttempts: 1 103 | inputs: 104 | Service: ec2 105 | Api: DescribeSnapshots 106 | SnapshotIds: "{{createSnapshot.Output}}" 107 | outputs: 108 | - Name: SnapshotId 109 | Selector: $.Snapshots[0].SnapshotId 110 | Type: String 111 | - name: copyAndEncryptSnapshot 112 | action: aws:executeAwsApi 113 | timeoutSeconds: 3600 114 | onFailure: step:deleteEncryptedRootVolumeSnapshot 115 | nextStep: waitForEncryptedSnapshot 116 | maxAttempts: 1 117 | inputs: 118 | Service: ec2 119 | Api: CopySnapshot 120 | SourceSnapshotId: "{{extractSnapshotId.SnapshotId}}" 121 | SourceRegion: "{{global:REGION}}" 122 | Encrypted: true 123 | KmsKeyId: "{{kmsKeyId}}" 124 | DestinationRegion: "{{global:REGION}}" 125 | outputs: 126 | - Name: encryptedSnapshotId 127 | Selector: $.SnapshotId 128 | Type: String 129 | - name: waitForEncryptedSnapshot 130 | action: aws:waitForAwsResourceProperty 131 | timeoutSeconds: 3600 132 | onFailure: step:deleteEncryptedRootVolumeSnapshot 133 | nextStep: createEncryptedVolumeFromEncryptedSnapshot 134 | inputs: 135 | Service: ec2 136 | Api: DescribeSnapshots 137 | SnapshotIds: 138 | - "{{copyAndEncryptSnapshot.encryptedSnapshotId}}" 139 | PropertySelector: $.Snapshots[0].State 140 | DesiredValues: 141 | - completed 142 | - name: createEncryptedVolumeFromEncryptedSnapshot 143 | action: aws:executeAwsApi 144 | timeoutSeconds: 30 145 | onFailure: step:deleteNewEncryptedVolume 146 | nextStep: stopInstance 147 | maxAttempts: 1 148 | inputs: 149 | Service: ec2 150 | Api: CreateVolume 151 | AvailabilityZone: "{{describeInstance.availabilityZone}}" 152 | Encrypted: true 153 | KmsKeyId: "{{kmsKeyId}}" 154 | SnapshotId: "{{copyAndEncryptSnapshot.encryptedSnapshotId}}" 155 | VolumeType: "{{describeVolume.deviceVolumeType}}" 156 | TagSpecifications: 157 | - ResourceType: volume 158 | Tags: 159 | - Key: encrypted-clone-of-volume 160 | Value: "{{volumeId}}" 161 | outputs: 162 | - Name: NewRootVolumeID 163 | Selector: $.VolumeId 164 | Type: String 165 | - name: stopInstance 166 | action: aws:executeAutomation 167 | timeoutSeconds: 300 168 | onFailure: step:deleteNewEncryptedVolume 169 | nextStep: detachEBSVolume 170 | maxAttempts: 1 171 | inputs: 172 | DocumentName: AWS-StopEC2Instance 173 | RuntimeParameters: 174 | InstanceId: "{{describeVolume.instanceId}}" 175 | - name: detachEBSVolume 176 | action: aws:executeAutomation 177 | timeoutSeconds: 600 178 | onFailure: step:attachOriginalVolume 179 | nextStep: attachNewEBSVolume 180 | maxAttempts: 1 181 | inputs: 182 | DocumentName: AWS-DetachEBSVolume 183 | RuntimeParameters: 184 | VolumeId: "{{volumeId}}" 185 | - name: attachNewEBSVolume 186 | action: aws:executeAutomation 187 | timeoutSeconds: 600 188 | onFailure: step:detachNewVolume 189 | nextStep: applyDeleteOnTerminationValue 190 | maxAttempts: 1 191 | inputs: 192 | DocumentName: AWS-AttachEBSVolume 193 | RuntimeParameters: 194 | Device: "{{describeVolume.deviceMount}}" 195 | InstanceId: "{{describeVolume.instanceId}}" 196 | VolumeId: "{{createEncryptedVolumeFromEncryptedSnapshot.NewRootVolumeID}}" 197 | - name: applyDeleteOnTerminationValue 198 | action: aws:executeAwsApi 199 | onFailure: step:detachNewVolume 200 | nextStep: restoreInstanceInitialState 201 | timeoutSeconds: 60 202 | maxAttempts: 10 203 | isCritical: true 204 | inputs: 205 | Service: ec2 206 | Api: ModifyInstanceAttribute 207 | InstanceId: "{{describeVolume.instanceId}}" 208 | BlockDeviceMappings: 209 | - DeviceName: "{{describeVolume.deviceMount}}" 210 | Ebs: 211 | DeleteOnTermination: "{{describeVolume.deleteOnTermination}}" 212 | - name: restoreInstanceInitialState 213 | action: aws:changeInstanceState 214 | onFailure: step:detachNewVolume 215 | isCritical: true 216 | nextStep: markUnencryptedVolumeRemediated 217 | inputs: 218 | InstanceIds: 219 | - "{{describeVolume.instanceId}}" 220 | DesiredState: "{{describeInstance.instanceState}}" 221 | - name: markUnencryptedVolumeRemediated 222 | action: aws:executeAwsApi 223 | timeoutSeconds: 300 224 | onFailure: Continue 225 | nextStep: deleteEncryptedRootVolumeSnapshot 226 | maxAttempts: 1 227 | inputs: 228 | Service: resourcegroupstaggingapi 229 | Api: TagResources 230 | ResourceARNList: 231 | - arn:aws:ec2:{{global:REGION}}:{{global:ACCOUNT_ID}}:volume/{{volumeId}} 232 | Tags: 233 | remediated: "{{createEncryptedVolumeFromEncryptedSnapshot.NewRootVolumeID}}" 234 | - name: detachNewVolume 235 | action: aws:executeAutomation 236 | timeoutSeconds: 300 237 | onFailure: Continue 238 | nextStep: attachOriginalVolume 239 | maxAttempts: 1 240 | inputs: 241 | DocumentName: AWS-DetachEBSVolume 242 | RuntimeParameters: 243 | VolumeId: "{{createEncryptedVolumeFromEncryptedSnapshot.NewRootVolumeID}}" 244 | - name: attachOriginalVolume 245 | action: aws:executeAutomation 246 | timeoutSeconds: 180 247 | onFailure: Continue 248 | nextStep: deleteNewEncryptedVolume 249 | maxAttempts: 1 250 | inputs: 251 | DocumentName: AWS-AttachEBSVolume 252 | RuntimeParameters: 253 | Device: "{{describeVolume.deviceMount}}" 254 | InstanceId: "{{describeVolume.instanceId}}" 255 | VolumeId: "{{volumeId}}" 256 | - name: deleteNewEncryptedVolume 257 | action: aws:executeAwsApi 258 | timeoutSeconds: 300 259 | onFailure: Continue 260 | nextStep: deleteEncryptedRootVolumeSnapshot 261 | maxAttempts: 1 262 | inputs: 263 | Service: ec2 264 | Api: DeleteVolume 265 | VolumeId: "{{createEncryptedVolumeFromEncryptedSnapshot.NewRootVolumeID}}" 266 | - name: deleteEncryptedRootVolumeSnapshot 267 | action: aws:executeAwsApi 268 | onFailure: Continue 269 | nextStep: deleteRootVolumeSnapshot 270 | timeoutSeconds: 300 271 | maxAttempts: 1 272 | inputs: 273 | Service: ec2 274 | Api: DeleteSnapshot 275 | SnapshotId: "{{copyAndEncryptSnapshot.encryptedSnapshotId}}" 276 | - name: deleteRootVolumeSnapshot 277 | action: aws:executeAwsApi 278 | onFailure: Continue 279 | isEnd: true 280 | timeoutSeconds: 300 281 | maxAttempts: 1 282 | inputs: 283 | Service: ec2 284 | Api: DeleteSnapshot 285 | SnapshotId: "{{extractSnapshotId.SnapshotId}}" 286 | outputs: 287 | - createEncryptedVolumeFromEncryptedSnapshot.NewRootVolumeID 288 | DocumentType: Automation 289 | Metadata: 290 | aws:cdk:path: unencrypted-to-encrypted-ebs/ENCRYPT-unencryptedebsvolume 291 | EncryptEBSVolumesConfigRemediation: 292 | Type: AWS::Config::RemediationConfiguration 293 | Properties: 294 | ConfigRuleName: encrypted-volumes 295 | TargetId: 296 | Ref: ENCRYPTunencryptedebsvolume 297 | TargetType: SSM_DOCUMENT 298 | Automatic: false 299 | Parameters: 300 | automationAssumeRole: 301 | StaticValue: 302 | Values: 303 | - Fn::ImportValue: 304 | !Sub "${RoleKeyStackNameParameter}-RoleARN" 305 | kmsKeyId: 306 | StaticValue: 307 | Values: 308 | - Fn::ImportValue: 309 | !Sub "${RoleKeyStackNameParameter}-KeyID" 310 | volumeId: 311 | ResourceValue: 312 | Value: RESOURCE_ID 313 | ResourceType: AWS::EC2::Volume 314 | TargetVersion: "1" 315 | DependsOn: 316 | - ENCRYPTunencryptedebsvolume 317 | Metadata: 318 | aws:cdk:path: unencrypted-to-encrypted-ebs/EncryptEBSVolumesConfigRemediation 319 | CDKMetadata: 320 | Type: AWS::CDK::Metadata 321 | Properties: 322 | Modules: aws-cdk=1.19.0,@aws-cdk/assets=1.19.0,@aws-cdk/aws-cloudwatch=1.19.0,@aws-cdk/aws-config=1.19.0,@aws-cdk/aws-ec2=1.19.0,@aws-cdk/aws-events=1.19.0,@aws-cdk/aws-iam=1.19.0,@aws-cdk/aws-kms=1.19.0,@aws-cdk/aws-lambda=1.19.0,@aws-cdk/aws-logs=1.19.0,@aws-cdk/aws-s3=1.19.0,@aws-cdk/aws-s3-assets=1.19.0,@aws-cdk/aws-sns=1.19.0,@aws-cdk/aws-sqs=1.19.0,@aws-cdk/aws-ssm=1.19.0,@aws-cdk/core=1.19.0,@aws-cdk/cx-api=1.19.0,@aws-cdk/region-info=1.19.0,jsii-runtime=Python/3.7.4 323 | Condition: CDKMetadataAvailable 324 | Conditions: 325 | CDKMetadataAvailable: 326 | Fn::Or: 327 | - Fn::Or: 328 | - Fn::Equals: 329 | - Ref: AWS::Region 330 | - ap-east-1 331 | - Fn::Equals: 332 | - Ref: AWS::Region 333 | - ap-northeast-1 334 | - Fn::Equals: 335 | - Ref: AWS::Region 336 | - ap-northeast-2 337 | - Fn::Equals: 338 | - Ref: AWS::Region 339 | - ap-south-1 340 | - Fn::Equals: 341 | - Ref: AWS::Region 342 | - ap-southeast-1 343 | - Fn::Equals: 344 | - Ref: AWS::Region 345 | - ap-southeast-2 346 | - Fn::Equals: 347 | - Ref: AWS::Region 348 | - ca-central-1 349 | - Fn::Equals: 350 | - Ref: AWS::Region 351 | - cn-north-1 352 | - Fn::Equals: 353 | - Ref: AWS::Region 354 | - cn-northwest-1 355 | - Fn::Equals: 356 | - Ref: AWS::Region 357 | - eu-central-1 358 | - Fn::Or: 359 | - Fn::Equals: 360 | - Ref: AWS::Region 361 | - eu-north-1 362 | - Fn::Equals: 363 | - Ref: AWS::Region 364 | - eu-west-1 365 | - Fn::Equals: 366 | - Ref: AWS::Region 367 | - eu-west-2 368 | - Fn::Equals: 369 | - Ref: AWS::Region 370 | - eu-west-3 371 | - Fn::Equals: 372 | - Ref: AWS::Region 373 | - me-south-1 374 | - Fn::Equals: 375 | - Ref: AWS::Region 376 | - sa-east-1 377 | - Fn::Equals: 378 | - Ref: AWS::Region 379 | - us-east-1 380 | - Fn::Equals: 381 | - Ref: AWS::Region 382 | - us-east-2 383 | - Fn::Equals: 384 | - Ref: AWS::Region 385 | - us-west-1 386 | - Fn::Equals: 387 | - Ref: AWS::Region 388 | - us-west-2 389 | 390 | -------------------------------------------------------------------------------- /rds/CloudFormation/unencrypted-to-encrypted-rds.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Description": "This sample, non-production-ready template create a config remediation rule to create encrypted RDS instances and clusters using the specific KMS key.", 3 | "Resources": { 4 | "EncryptRDSAutomationRoleB6031D59": { 5 | "Type": "AWS::IAM::Role", 6 | "Properties": { 7 | "AssumeRolePolicyDocument": { 8 | "Statement": [ 9 | { 10 | "Action": "sts:AssumeRole", 11 | "Effect": "Allow", 12 | "Principal": { 13 | "Service": [ 14 | "ssm.amazonaws.com", 15 | "rds.amazonaws.com" 16 | ] 17 | } 18 | } 19 | ], 20 | "Version": "2012-10-17" 21 | }, 22 | "ManagedPolicyArns": [ 23 | { 24 | "Ref": "EncryptRDSAutomationRolepolicy5A97FD3E" 25 | } 26 | ] 27 | }, 28 | "Metadata": { 29 | "aws:cdk:path": "unencrypted-to-encrypted-rds/EncryptRDSAutomationRole/Resource" 30 | } 31 | }, 32 | "EncryptRDSAutomationRoleDefaultPolicyC9DEFBD9": { 33 | "Type": "AWS::IAM::Policy", 34 | "Properties": { 35 | "PolicyDocument": { 36 | "Statement": [ 37 | { 38 | "Action": "iam:PassRole", 39 | "Effect": "Allow", 40 | "Resource": { 41 | "Fn::GetAtt": [ 42 | "EncryptRDSAutomationRoleB6031D59", 43 | "Arn" 44 | ] 45 | } 46 | } 47 | ], 48 | "Version": "2012-10-17" 49 | }, 50 | "PolicyName": "EncryptRDSAutomationRoleDefaultPolicyC9DEFBD9", 51 | "Roles": [ 52 | { 53 | "Ref": "EncryptRDSAutomationRoleB6031D59" 54 | } 55 | ] 56 | }, 57 | "Metadata": { 58 | "aws:cdk:path": "unencrypted-to-encrypted-rds/EncryptRDSAutomationRole/DefaultPolicy/Resource" 59 | } 60 | }, 61 | "rdsencryptionkeyA4CDBAAA": { 62 | "Type": "AWS::KMS::Key", 63 | "Properties": { 64 | "KeyPolicy": { 65 | "Statement": [ 66 | { 67 | "Action": [ 68 | "kms:Create*", 69 | "kms:Describe*", 70 | "kms:Enable*", 71 | "kms:List*", 72 | "kms:Put*", 73 | "kms:Update*", 74 | "kms:Revoke*", 75 | "kms:Disable*", 76 | "kms:Get*", 77 | "kms:Delete*", 78 | "kms:ScheduleKeyDeletion", 79 | "kms:CancelKeyDeletion", 80 | "kms:GenerateDataKey", 81 | "kms:TagResource", 82 | "kms:UntagResource" 83 | ], 84 | "Effect": "Allow", 85 | "Principal": { 86 | "AWS": { 87 | "Fn::Join": [ 88 | "", 89 | [ 90 | "arn:", 91 | { 92 | "Ref": "AWS::Partition" 93 | }, 94 | ":iam::", 95 | { 96 | "Ref": "AWS::AccountId" 97 | }, 98 | ":root" 99 | ] 100 | ] 101 | } 102 | }, 103 | "Resource": "*" 104 | } 105 | ], 106 | "Version": "2012-10-17" 107 | }, 108 | "EnableKeyRotation": true 109 | }, 110 | "UpdateReplacePolicy": "Retain", 111 | "DeletionPolicy": "Retain", 112 | "Metadata": { 113 | "aws:cdk:path": "unencrypted-to-encrypted-rds/rds-encryption-key/Resource" 114 | } 115 | }, 116 | "rdsencryptionkeyAliasF994D841": { 117 | "Type": "AWS::KMS::Alias", 118 | "Properties": { 119 | "AliasName": "alias/RDSEncryptionAtRestKMSAlias", 120 | "TargetKeyId": { 121 | "Fn::GetAtt": [ 122 | "rdsencryptionkeyA4CDBAAA", 123 | "Arn" 124 | ] 125 | } 126 | }, 127 | "Metadata": { 128 | "aws:cdk:path": "unencrypted-to-encrypted-rds/rds-encryption-key/Alias/Resource" 129 | } 130 | }, 131 | "EncryptRDSAutomationRolepolicy5A97FD3E": { 132 | "Type": "AWS::IAM::ManagedPolicy", 133 | "Properties": { 134 | "PolicyDocument": { 135 | "Statement": [ 136 | { 137 | "Action": [ 138 | "ec2:DescribeAvailabilityZones", 139 | "ec2:DescribeInternetGateways", 140 | "ec2:DescribeSecurityGroups", 141 | "ec2:DescribeSubnets", 142 | "ec2:DescribeVpcAttribute", 143 | "ec2:DescribeVpcs", 144 | "rds:AddTagsToResource", 145 | "ssm:GetAutomationExecution" 146 | ], 147 | "Effect": "Allow", 148 | "Resource": "*" 149 | }, 150 | { 151 | "Action": [ 152 | "kms:CreateGrant", 153 | "kms:DescribeKey" 154 | ], 155 | "Effect": "Allow", 156 | "Resource": { 157 | "Fn::Join": [ 158 | "", 159 | [ 160 | "arn:aws:kms:", 161 | { 162 | "Ref": "AWS::Region" 163 | }, 164 | ":", 165 | { 166 | "Ref": "AWS::AccountId" 167 | }, 168 | ":key/", 169 | { 170 | "Ref": "rdsencryptionkeyA4CDBAAA" 171 | } 172 | ] 173 | ] 174 | } 175 | }, 176 | { 177 | "Action": [ 178 | "rds:CreateDBClusterSnapshot", 179 | "rds:DeleteDBClusterSnapshot", 180 | "rds:DescribeDBClusterSnapshots", 181 | "rds:RestoreDBClusterFromSnapshot" 182 | ], 183 | "Effect": "Allow", 184 | "Resource": { 185 | "Fn::Join": [ 186 | "", 187 | [ 188 | "arn:aws:rds:", 189 | { 190 | "Ref": "AWS::Region" 191 | }, 192 | ":", 193 | { 194 | "Ref": "AWS::AccountId" 195 | }, 196 | ":cluster-snapshot:*" 197 | ] 198 | ] 199 | } 200 | }, 201 | { 202 | "Action": [ 203 | "rds:CreateDBClusterSnapshot", 204 | "rds:DescribeDBClusters", 205 | "rds:RestoreDBClusterFromSnapshot" 206 | ], 207 | "Effect": "Allow", 208 | "Resource": { 209 | "Fn::Join": [ 210 | "", 211 | [ 212 | "arn:aws:rds:", 213 | { 214 | "Ref": "AWS::Region" 215 | }, 216 | ":", 217 | { 218 | "Ref": "AWS::AccountId" 219 | }, 220 | ":cluster:*" 221 | ] 222 | ] 223 | } 224 | }, 225 | { 226 | "Action": [ 227 | "rds:CreateDBInstance", 228 | "rds:CreateDBSnapshot", 229 | "rds:DescribeDBInstances", 230 | "rds:RestoreDBInstanceFromDBSnapshot" 231 | ], 232 | "Effect": "Allow", 233 | "Resource": [ 234 | { 235 | "Fn::Join": [ 236 | "", 237 | [ 238 | "arn:aws:rds:", 239 | { 240 | "Ref": "AWS::Region" 241 | }, 242 | ":", 243 | { 244 | "Ref": "AWS::AccountId" 245 | }, 246 | ":db:*" 247 | ] 248 | ] 249 | }, 250 | { 251 | "Fn::Join": [ 252 | "", 253 | [ 254 | "arn:aws:rds:", 255 | { 256 | "Ref": "AWS::Region" 257 | }, 258 | ":", 259 | { 260 | "Ref": "AWS::AccountId" 261 | }, 262 | ":cluster:*" 263 | ] 264 | ] 265 | } 266 | ] 267 | }, 268 | { 269 | "Action": [ 270 | "rds:CreateDBInstance", 271 | "rds:RestoreDBClusterFromSnapshot", 272 | "rds:RestoreDBInstanceFromDBSnapshot" 273 | ], 274 | "Effect": "Allow", 275 | "Resource": { 276 | "Fn::Join": [ 277 | "", 278 | [ 279 | "arn:aws:rds:", 280 | { 281 | "Ref": "AWS::Region" 282 | }, 283 | ":", 284 | { 285 | "Ref": "AWS::AccountId" 286 | }, 287 | ":og:*" 288 | ] 289 | ] 290 | } 291 | }, 292 | { 293 | "Action": "rds:CreateDBInstance", 294 | "Effect": "Allow", 295 | "Resource": [ 296 | { 297 | "Fn::Join": [ 298 | "", 299 | [ 300 | "arn:aws:rds:", 301 | { 302 | "Ref": "AWS::Region" 303 | }, 304 | ":", 305 | { 306 | "Ref": "AWS::AccountId" 307 | }, 308 | ":secgrp:*" 309 | ] 310 | ] 311 | }, 312 | { 313 | "Fn::Join": [ 314 | "", 315 | [ 316 | "arn:aws:rds:", 317 | { 318 | "Ref": "AWS::Region" 319 | }, 320 | ":", 321 | { 322 | "Ref": "AWS::AccountId" 323 | }, 324 | ":pg:*" 325 | ] 326 | ] 327 | } 328 | ] 329 | }, 330 | { 331 | "Action": [ 332 | "rds:CopyDBSnapshot", 333 | "rds:CreateDBSnapshot", 334 | "rds:DeleteDBSnapshot", 335 | "rds:DescribeDBSnapshots", 336 | "rds:RestoreDBInstanceFromDBSnapshot" 337 | ], 338 | "Effect": "Allow", 339 | "Resource": { 340 | "Fn::Join": [ 341 | "", 342 | [ 343 | "arn:aws:rds:", 344 | { 345 | "Ref": "AWS::Region" 346 | }, 347 | ":", 348 | { 349 | "Ref": "AWS::AccountId" 350 | }, 351 | ":snapshot:*" 352 | ] 353 | ] 354 | } 355 | }, 356 | { 357 | "Action": [ 358 | "rds:CreateDBInstance", 359 | "rds:RestoreDBInstanceFromDBSnapshot" 360 | ], 361 | "Effect": "Allow", 362 | "Resource": { 363 | "Fn::Join": [ 364 | "", 365 | [ 366 | "arn:aws:rds:", 367 | { 368 | "Ref": "AWS::Region" 369 | }, 370 | ":", 371 | { 372 | "Ref": "AWS::AccountId" 373 | }, 374 | ":subgrp:*" 375 | ] 376 | ] 377 | } 378 | }, 379 | { 380 | "Action": "ssm:StartAutomationExecution", 381 | "Effect": "Allow", 382 | "Resource": "*" 383 | } 384 | ], 385 | "Version": "2012-10-17" 386 | }, 387 | "Description": "Permissions for the RDS encrypt unencrypted", 388 | "Path": "/" 389 | }, 390 | "Metadata": { 391 | "aws:cdk:path": "unencrypted-to-encrypted-rds/EncryptRDSAutomationRole-policy/Resource" 392 | } 393 | }, 394 | "ENCRYPTunencryptedrdscluster": { 395 | "Type": "AWS::SSM::Document", 396 | "Properties": { 397 | "Content": { 398 | "schemaVersion": "0.3", 399 | "description": "Encrypt RDS Cluster Automation Document", 400 | "assumeRole": "{{automationAssumeRole}}", 401 | "parameters": { 402 | "DBInstanceClass": { 403 | "description": "(Required) The compute and memory capacity of the DB instance, for example, db.m4.large.", 404 | "type": "String" 405 | }, 406 | "SourceDBClusterIdentifier": { 407 | "description": "(Required) The DB Cluster Identifier of the RDS Cluster to create Snapshot from.", 408 | "type": "String" 409 | }, 410 | "EncryptedDBClusterIdentifier": { 411 | "description": "(Optional, default provided) The name of the id of the target encrypted DB Instance to create", 412 | "default": "encrypted-{{SourceDBClusterIdentifier}}", 413 | "type": "String" 414 | }, 415 | "DBClusterSnapshotIdentifier": { 416 | "description": "(Optional, default provided) The Encrypted DBClusterSnapshotIdentifier ID of the RDS snapshot to create.", 417 | "default": "unencrypted-db-cluster-snapshot-{{SourceDBClusterIdentifier}}", 418 | "type": "String" 419 | }, 420 | "kmsKeyId": { 421 | "description": "(Required) Customer KMS key to use during the encryption", 422 | "type": "String" 423 | }, 424 | "automationAssumeRole": { 425 | "type": "String", 426 | "description": "(Required) The ARN of the role that allows Automation to perform the actions on your behalf." 427 | } 428 | }, 429 | "mainSteps": [ 430 | { 431 | "name": "createRDSClusterSnapshot", 432 | "action": "aws:executeAwsApi", 433 | "timeoutSeconds": 300, 434 | "onFailure": "step:deleteSnapshotCluster", 435 | "nextStep": "waitForSnapshot", 436 | "maxAttempts": 1, 437 | "inputs": { 438 | "Service": "rds", 439 | "Api": "CreateDBClusterSnapshot", 440 | "DBClusterIdentifier": "{{SourceDBClusterIdentifier}}", 441 | "DBClusterSnapshotIdentifier": "{{DBClusterSnapshotIdentifier}}" 442 | }, 443 | "outputs": [ 444 | { 445 | "Name": "engine", 446 | "Selector": "$.DBClusterSnapshot.Engine", 447 | "Type": "String" 448 | } 449 | ] 450 | }, 451 | { 452 | "name": "waitForSnapshot", 453 | "action": "aws:waitForAwsResourceProperty", 454 | "timeoutSeconds": 1800, 455 | "onFailure": "Abort", 456 | "nextStep": "createEncryptedRDSClusterFromSnapshot", 457 | "maxAttempts": 1, 458 | "inputs": { 459 | "Service": "rds", 460 | "Api": "DescribeDBClusterSnapshots", 461 | "DBClusterSnapshotIdentifier": "{{DBClusterSnapshotIdentifier}}", 462 | "PropertySelector": "$.DBClusterSnapshots[0].Status", 463 | "DesiredValues": [ 464 | "available" 465 | ] 466 | } 467 | }, 468 | { 469 | "name": "createEncryptedRDSClusterFromSnapshot", 470 | "action": "aws:executeAwsApi", 471 | "timeoutSeconds": 300, 472 | "onFailure": "step:deleteSnapshotCluster", 473 | "nextStep": "waitForEncryptedRDSCluster", 474 | "maxAttempts": 1, 475 | "inputs": { 476 | "Service": "rds", 477 | "Api": "RestoreDBClusterFromSnapshot", 478 | "DBClusterIdentifier": "{{EncryptedDBClusterIdentifier}}", 479 | "SnapshotIdentifier": "{{DBClusterSnapshotIdentifier}}", 480 | "Engine": "{{createRDSClusterSnapshot.engine}}", 481 | "KmsKeyId": "{{kmsKeyId}}" 482 | } 483 | }, 484 | { 485 | "name": "waitForEncryptedRDSCluster", 486 | "action": "aws:waitForAwsResourceProperty", 487 | "timeoutSeconds": 1200, 488 | "onFailure": "step:deleteSnapshotCluster", 489 | "nextStep": "createEncryptedRDSInstanceInCluster", 490 | "maxAttempts": 1, 491 | "inputs": { 492 | "Service": "rds", 493 | "Api": "DescribeDBClusters", 494 | "DBClusterIdentifier": "{{EncryptedDBClusterIdentifier}}", 495 | "PropertySelector": "$.DBClusters[0].Status", 496 | "DesiredValues": [ 497 | "available" 498 | ] 499 | } 500 | }, 501 | { 502 | "name": "createEncryptedRDSInstanceInCluster", 503 | "action": "aws:executeAwsApi", 504 | "timeoutSeconds": 300, 505 | "onFailure": "step:deleteSnapshotCluster", 506 | "nextStep": "waitForEncryptedRDSInstance", 507 | "maxAttempts": 1, 508 | "inputs": { 509 | "Service": "rds", 510 | "Api": "CreateDBInstance", 511 | "DBClusterIdentifier": "{{EncryptedDBClusterIdentifier}}", 512 | "DBInstanceIdentifier": "instance-1-{{EncryptedDBClusterIdentifier}}", 513 | "DBInstanceClass": "{{DBInstanceClass}}", 514 | "Engine": "{{createRDSClusterSnapshot.engine}}" 515 | } 516 | }, 517 | { 518 | "name": "waitForEncryptedRDSInstance", 519 | "action": "aws:waitForAwsResourceProperty", 520 | "timeoutSeconds": 1200, 521 | "onFailure": "step:deleteSnapshotCluster", 522 | "nextStep": "deleteSnapshotCluster", 523 | "maxAttempts": 1, 524 | "inputs": { 525 | "Service": "rds", 526 | "Api": "DescribeDBInstances", 527 | "DBInstanceIdentifier": "instance-1-{{EncryptedDBClusterIdentifier}}", 528 | "PropertySelector": "$.DBInstances[0].DBInstanceStatus", 529 | "DesiredValues": [ 530 | "available" 531 | ] 532 | } 533 | }, 534 | { 535 | "name": "deleteSnapshotCluster", 536 | "action": "aws:executeAwsApi", 537 | "timeoutSeconds": 300, 538 | "onFailure": "Abort", 539 | "isEnd": true, 540 | "maxAttempts": 1, 541 | "inputs": { 542 | "Service": "rds", 543 | "Api": "DeleteDBClusterSnapshot", 544 | "DBClusterSnapshotIdentifier": "{{DBClusterSnapshotIdentifier}}" 545 | } 546 | } 547 | ] 548 | }, 549 | "DocumentType": "Automation" 550 | }, 551 | "Metadata": { 552 | "aws:cdk:path": "unencrypted-to-encrypted-rds/ENCRYPT-unencryptedrdscluster" 553 | } 554 | }, 555 | "ENCRYPTunencryptedrdsinstance": { 556 | "Type": "AWS::SSM::Document", 557 | "Properties": { 558 | "Content": { 559 | "schemaVersion": "0.3", 560 | "description": "Encrypt RDS Instance Automation Document", 561 | "assumeRole": "{{automationAssumeRole}}", 562 | "parameters": { 563 | "DBInstanceIdentifier": { 564 | "description": "(Required) The DBInstanceId ID of the RDS Instance to create Snapshot from.", 565 | "type": "String" 566 | }, 567 | "EncryptedDBInstanceIdentifier": { 568 | "description": "(Optional, default provided) The name of the id of the target encrypted DB Instance to create", 569 | "default": "encrypted-{{DBInstanceIdentifier}}", 570 | "type": "String" 571 | }, 572 | "EncryptedDBSnapshotIdentifier": { 573 | "description": "(Optional, default provided) The DBSnapshotIdentifier ID of the Encrypted RDS snapshot to create.", 574 | "default": "encrypted-db-instance-snapshot-{{DBInstanceIdentifier}}", 575 | "type": "String" 576 | }, 577 | "kmsKeyId": { 578 | "description": "(Required) Customer KMS key to use during the encryption", 579 | "type": "String" 580 | }, 581 | "automationAssumeRole": { 582 | "type": "String", 583 | "description": "(Required) The ARN of the role that allows Automation to perform the actions on your behalf." 584 | } 585 | }, 586 | "mainSteps": [ 587 | { 588 | "name": "createAndVerifySnapshot", 589 | "action": "aws:executeAutomation", 590 | "timeoutSeconds": 1800, 591 | "onFailure": "step:deleteSnapshot", 592 | "nextStep": "copyToEncryptedSnapshot", 593 | "maxAttempts": 3, 594 | "inputs": { 595 | "DocumentName": "AWS-CreateRdsSnapshot", 596 | "RuntimeParameters": { 597 | "DBInstanceIdentifier": "{{DBInstanceIdentifier}}", 598 | "DBSnapshotIdentifier": "unencrypted-{{EncryptedDBSnapshotIdentifier}}", 599 | "AutomationAssumeRole": "{{automationAssumeRole}}" 600 | } 601 | } 602 | }, 603 | { 604 | "name": "copyToEncryptedSnapshot", 605 | "action": "aws:executeAwsApi", 606 | "timeoutSeconds": 300, 607 | "onFailure": "step:deleteEncryptedSnapshot", 608 | "nextStep": "waitForEncryptedSnapshot", 609 | "maxAttempts": 1, 610 | "inputs": { 611 | "Service": "rds", 612 | "Api": "CopyDBSnapshot", 613 | "KmsKeyId": "{{kmsKeyId}}", 614 | "TargetDBSnapshotIdentifier": "{{EncryptedDBSnapshotIdentifier}}", 615 | "SourceDBSnapshotIdentifier": "unencrypted-{{EncryptedDBSnapshotIdentifier}}" 616 | } 617 | }, 618 | { 619 | "name": "waitForEncryptedSnapshot", 620 | "action": "aws:waitForAwsResourceProperty", 621 | "timeoutSeconds": 1800, 622 | "onFailure": "step:deleteEncryptedSnapshot", 623 | "nextStep": "createEncryptedRDSInstanceFromSnapshot", 624 | "maxAttempts": 1, 625 | "inputs": { 626 | "Service": "rds", 627 | "Api": "DescribeDBSnapshots", 628 | "DBSnapshotIdentifier": "{{EncryptedDBSnapshotIdentifier}}", 629 | "PropertySelector": "$.DBSnapshots[0].Status", 630 | "DesiredValues": [ 631 | "available" 632 | ] 633 | } 634 | }, 635 | { 636 | "name": "createEncryptedRDSInstanceFromSnapshot", 637 | "action": "aws:executeAwsApi", 638 | "timeoutSeconds": 300, 639 | "onFailure": "step:deleteEncryptedSnapshot", 640 | "nextStep": "waitForEncryptedRDSInstance", 641 | "maxAttempts": 1, 642 | "inputs": { 643 | "Service": "rds", 644 | "Api": "RestoreDBInstanceFromDBSnapshot", 645 | "DBInstanceIdentifier": "{{EncryptedDBInstanceIdentifier}}", 646 | "DBSnapshotIdentifier": "{{EncryptedDBSnapshotIdentifier}}", 647 | "MultiAZ": true 648 | } 649 | }, 650 | { 651 | "name": "waitForEncryptedRDSInstance", 652 | "action": "aws:waitForAwsResourceProperty", 653 | "timeoutSeconds": 1200, 654 | "onFailure": "step:deleteEncryptedSnapshot", 655 | "nextStep": "deleteEncryptedSnapshot", 656 | "maxAttempts": 1, 657 | "inputs": { 658 | "Service": "rds", 659 | "Api": "DescribeDBInstances", 660 | "DBInstanceIdentifier": "{{EncryptedDBInstanceIdentifier}}", 661 | "PropertySelector": "$.DBInstances[0].DBInstanceStatus", 662 | "DesiredValues": [ 663 | "available" 664 | ] 665 | } 666 | }, 667 | { 668 | "name": "deleteEncryptedSnapshot", 669 | "action": "aws:executeAwsApi", 670 | "timeoutSeconds": 300, 671 | "onFailure": "Continue", 672 | "nextStep": "deleteSnapshot", 673 | "maxAttempts": 1, 674 | "inputs": { 675 | "Service": "rds", 676 | "Api": "DeleteDBSnapshot", 677 | "DBSnapshotIdentifier": "{{EncryptedDBSnapshotIdentifier}}" 678 | } 679 | }, 680 | { 681 | "name": "deleteSnapshot", 682 | "action": "aws:executeAwsApi", 683 | "timeoutSeconds": 300, 684 | "onFailure": "Continue", 685 | "isEnd": true, 686 | "maxAttempts": 1, 687 | "inputs": { 688 | "Service": "rds", 689 | "Api": "DeleteDBSnapshot", 690 | "DBSnapshotIdentifier": "unencrypted-{{EncryptedDBSnapshotIdentifier}}" 691 | } 692 | } 693 | ], 694 | "outputs": [ 695 | "createAndVerifySnapshot.Output" 696 | ] 697 | }, 698 | "DocumentType": "Automation" 699 | }, 700 | "Metadata": { 701 | "aws:cdk:path": "unencrypted-to-encrypted-rds/ENCRYPT-unencryptedrdsinstance" 702 | } 703 | }, 704 | "ENCRYPTunencryptedrds": { 705 | "Type": "AWS::SSM::Document", 706 | "Properties": { 707 | "Content": { 708 | "schemaVersion": "0.3", 709 | "description": "Encrypt RDS Automation Document", 710 | "assumeRole": "{{automationAssumeRole}}", 711 | "parameters": { 712 | "instanceAutomationDocument": { 713 | "description": "(Required) Document Id of the RDS instance encryption automation", 714 | "type": "String" 715 | }, 716 | "clusterAutomationDocument": { 717 | "description": "(Required) Document Id of the RDS cluster encryption automation", 718 | "type": "String" 719 | }, 720 | "resourceId": { 721 | "description": "(Required) RDS Instance Id of the resource to be remediated", 722 | "type": "String" 723 | }, 724 | "kmsKeyId": { 725 | "description": "(Required) Customer KMS key to use during the encryption", 726 | "type": "String" 727 | }, 728 | "automationAssumeRole": { 729 | "type": "String", 730 | "description": "(Required) The ARN of the role that allows Automation to perform the actions on your behalf." 731 | } 732 | }, 733 | "mainSteps": [ 734 | { 735 | "name": "determineInstanceOrCluster", 736 | "action": "aws:executeScript", 737 | "description": "Determines whether the instance is a standalone or part of a cluster", 738 | "timeoutSeconds": 300, 739 | "onFailure": "Abort", 740 | "nextStep": "chooseInstanceOrCluster", 741 | "maxAttempts": 1, 742 | "outputs": [ 743 | { 744 | "Name": "InstanceClass", 745 | "Selector": "$.Payload.instance_class", 746 | "Type": "String" 747 | }, 748 | { 749 | "Name": "IsCluster", 750 | "Selector": "$.Payload.is_cluster", 751 | "Type": "Boolean" 752 | }, 753 | { 754 | "Name": "ClusterId", 755 | "Selector": "$.Payload.cluster_id", 756 | "Type": "String" 757 | }, 758 | { 759 | "Name": "DBInstanceIdentifier", 760 | "Selector": "$.Payload.db_instance_id", 761 | "Type": "String" 762 | } 763 | ], 764 | "inputs": { 765 | "Runtime": "python3.6", 766 | "Handler": "handler", 767 | "InputPayload": { 768 | "DBResourceIdentifier": "{{resourceId}}" 769 | }, 770 | "Script": "import boto3\n\nDB_RESOURCE_ID = \"DBResourceIdentifier\"\n\ndef handler(event, context):\n rds = boto3.client('rds')\n\n resource_id = event[DB_RESOURCE_ID]\n result=rds.describe_db_instances(\n Filters=[\n {\n 'Name': 'dbi-resource-id',\n 'Values': [\n resource_id\n ]\n },\n ],\n )\n\n info=result[\"DBInstances\"][0]\n instance_class=info[\"DBInstanceClass\"]\n db_instance_id=info[\"DBInstanceIdentifier\"]\n is_cluster=False\n cluster_id=\"\"\n if \"DBClusterIdentifier\" in info:\n is_cluster=True\n cluster_id=info[\"DBClusterIdentifier\"]\n\n found= {\n 'db_instance_id': db_instance_id,\n 'instance_class' : instance_class,\n 'cluster_id': cluster_id,\n 'is_cluster': is_cluster\n }\n return(found)" 771 | } 772 | }, 773 | { 774 | "name": "chooseInstanceOrCluster", 775 | "action": "aws:branch", 776 | "description": "Branch to Instance or Cluster automation", 777 | "timeoutSeconds": 300, 778 | "onFailure": "Abort", 779 | "isEnd": true, 780 | "maxAttempts": 1, 781 | "inputs": { 782 | "Choices": [ 783 | { 784 | "NextStep": "rdsInstanceEncryptionAutomation", 785 | "Variable": "{{determineInstanceOrCluster.IsCluster}}", 786 | "BooleanEquals": false 787 | }, 788 | { 789 | "NextStep": "rdsClusterEncryptionAutomation", 790 | "Variable": "{{determineInstanceOrCluster.IsCluster}}", 791 | "BooleanEquals": true 792 | } 793 | ] 794 | } 795 | }, 796 | { 797 | "name": "rdsClusterEncryptionAutomation", 798 | "action": "aws:executeAutomation", 799 | "timeoutSeconds": 1800, 800 | "onFailure": "Abort", 801 | "isEnd": true, 802 | "maxAttempts": 1, 803 | "inputs": { 804 | "DocumentName": "{{clusterAutomationDocument}}", 805 | "RuntimeParameters": { 806 | "DBInstanceClass": [ 807 | "{{determineInstanceOrCluster.InstanceClass}}" 808 | ], 809 | "SourceDBClusterIdentifier": [ 810 | "{{determineInstanceOrCluster.ClusterId}}" 811 | ], 812 | "kmsKeyId": [ 813 | "{{kmsKeyId}}" 814 | ], 815 | "automationAssumeRole": [ 816 | "{{automationAssumeRole}}" 817 | ] 818 | } 819 | } 820 | }, 821 | { 822 | "name": "rdsInstanceEncryptionAutomation", 823 | "action": "aws:executeAutomation", 824 | "timeoutSeconds": 1800, 825 | "onFailure": "Abort", 826 | "isEnd": true, 827 | "maxAttempts": 1, 828 | "inputs": { 829 | "DocumentName": "{{instanceAutomationDocument}}", 830 | "RuntimeParameters": { 831 | "DBInstanceIdentifier": [ 832 | "{{determineInstanceOrCluster.DBInstanceIdentifier}}" 833 | ], 834 | "kmsKeyId": [ 835 | "{{kmsKeyId}}" 836 | ], 837 | "automationAssumeRole": [ 838 | "{{automationAssumeRole}}" 839 | ] 840 | } 841 | } 842 | } 843 | ] 844 | }, 845 | "DocumentType": "Automation" 846 | }, 847 | "Metadata": { 848 | "aws:cdk:path": "unencrypted-to-encrypted-rds/ENCRYPT-unencryptedrds" 849 | } 850 | }, 851 | "rdsstorageencryptedwithremediation6018FF11": { 852 | "Type": "AWS::Config::ConfigRule", 853 | "Properties": { 854 | "Source": { 855 | "Owner": "AWS", 856 | "SourceIdentifier": "RDS_STORAGE_ENCRYPTED" 857 | }, 858 | "ConfigRuleName": "rds-storage-encrypted-with-remediation", 859 | "Description": "Checks whether storage encryption is enabled for your RDS DB instances." 860 | }, 861 | "Metadata": { 862 | "aws:cdk:path": "unencrypted-to-encrypted-rds/rds-storage-encrypted-with-remediation/Resource" 863 | } 864 | }, 865 | "EncryptRDSConfigRemediation": { 866 | "Type": "AWS::Config::RemediationConfiguration", 867 | "Properties": { 868 | "ConfigRuleName": "rds-storage-encrypted-with-remediation", 869 | "TargetId": { 870 | "Ref": "ENCRYPTunencryptedrds" 871 | }, 872 | "TargetType": "SSM_DOCUMENT", 873 | "Automatic": false, 874 | "Parameters": { 875 | "instanceAutomationDocument": { 876 | "StaticValue": { 877 | "Values": [ 878 | { 879 | "Ref": "ENCRYPTunencryptedrdsinstance" 880 | } 881 | ] 882 | } 883 | }, 884 | "clusterAutomationDocument": { 885 | "StaticValue": { 886 | "Values": [ 887 | { 888 | "Ref": "ENCRYPTunencryptedrdscluster" 889 | } 890 | ] 891 | } 892 | }, 893 | "automationAssumeRole": { 894 | "StaticValue": { 895 | "Values": [ 896 | { 897 | "Fn::GetAtt": [ 898 | "EncryptRDSAutomationRoleB6031D59", 899 | "Arn" 900 | ] 901 | } 902 | ] 903 | } 904 | }, 905 | "kmsKeyId": { 906 | "StaticValue": { 907 | "Values": [ 908 | { 909 | "Ref": "rdsencryptionkeyA4CDBAAA" 910 | } 911 | ] 912 | } 913 | }, 914 | "resourceId": { 915 | "ResourceValue": { 916 | "Value": "RESOURCE_ID" 917 | } 918 | } 919 | }, 920 | "ResourceType": "AWS::RDS::DBInstance", 921 | "TargetVersion": "1" 922 | }, 923 | "Metadata": { 924 | "aws:cdk:path": "unencrypted-to-encrypted-rds/EncryptRDSConfigRemediation" 925 | } 926 | }, 927 | "CDKMetadata": { 928 | "Type": "AWS::CDK::Metadata", 929 | "Properties": { 930 | "Analytics": "v2:deflate64:H4sIAAAAAAAAE1WPzQrCMBCEn8V7urZ48qboTQSJTxDSta5tspAfRELe3SZaxNPMN8Muux10bQvtaqeevtH9uE6aHUK6BqVHcWDrg4s6iMPNSvQcncbi56KnQGyzKIOJlIEkeapl1QtPpF8Fv+6srBqw/+V/QRaj8ZBOWKsi+4mUL1BNFt4bmOnIOhq0QWi2NxogfdfIOC2XzfFCEg32pMqlnyK6CjlncXmFO9v1Brbz/w9P1LhoAxkE+dE3jk0bHRwBAAA=" 931 | }, 932 | "Metadata": { 933 | "aws:cdk:path": "unencrypted-to-encrypted-rds/CDKMetadata/Default" 934 | }, 935 | "Condition": "CDKMetadataAvailable" 936 | } 937 | }, 938 | "Conditions": { 939 | "CDKMetadataAvailable": { 940 | "Fn::Or": [ 941 | { 942 | "Fn::Or": [ 943 | { 944 | "Fn::Equals": [ 945 | { 946 | "Ref": "AWS::Region" 947 | }, 948 | "af-south-1" 949 | ] 950 | }, 951 | { 952 | "Fn::Equals": [ 953 | { 954 | "Ref": "AWS::Region" 955 | }, 956 | "ap-east-1" 957 | ] 958 | }, 959 | { 960 | "Fn::Equals": [ 961 | { 962 | "Ref": "AWS::Region" 963 | }, 964 | "ap-northeast-1" 965 | ] 966 | }, 967 | { 968 | "Fn::Equals": [ 969 | { 970 | "Ref": "AWS::Region" 971 | }, 972 | "ap-northeast-2" 973 | ] 974 | }, 975 | { 976 | "Fn::Equals": [ 977 | { 978 | "Ref": "AWS::Region" 979 | }, 980 | "ap-south-1" 981 | ] 982 | }, 983 | { 984 | "Fn::Equals": [ 985 | { 986 | "Ref": "AWS::Region" 987 | }, 988 | "ap-southeast-1" 989 | ] 990 | }, 991 | { 992 | "Fn::Equals": [ 993 | { 994 | "Ref": "AWS::Region" 995 | }, 996 | "ap-southeast-2" 997 | ] 998 | }, 999 | { 1000 | "Fn::Equals": [ 1001 | { 1002 | "Ref": "AWS::Region" 1003 | }, 1004 | "ca-central-1" 1005 | ] 1006 | }, 1007 | { 1008 | "Fn::Equals": [ 1009 | { 1010 | "Ref": "AWS::Region" 1011 | }, 1012 | "cn-north-1" 1013 | ] 1014 | }, 1015 | { 1016 | "Fn::Equals": [ 1017 | { 1018 | "Ref": "AWS::Region" 1019 | }, 1020 | "cn-northwest-1" 1021 | ] 1022 | } 1023 | ] 1024 | }, 1025 | { 1026 | "Fn::Or": [ 1027 | { 1028 | "Fn::Equals": [ 1029 | { 1030 | "Ref": "AWS::Region" 1031 | }, 1032 | "eu-central-1" 1033 | ] 1034 | }, 1035 | { 1036 | "Fn::Equals": [ 1037 | { 1038 | "Ref": "AWS::Region" 1039 | }, 1040 | "eu-north-1" 1041 | ] 1042 | }, 1043 | { 1044 | "Fn::Equals": [ 1045 | { 1046 | "Ref": "AWS::Region" 1047 | }, 1048 | "eu-south-1" 1049 | ] 1050 | }, 1051 | { 1052 | "Fn::Equals": [ 1053 | { 1054 | "Ref": "AWS::Region" 1055 | }, 1056 | "eu-west-1" 1057 | ] 1058 | }, 1059 | { 1060 | "Fn::Equals": [ 1061 | { 1062 | "Ref": "AWS::Region" 1063 | }, 1064 | "eu-west-2" 1065 | ] 1066 | }, 1067 | { 1068 | "Fn::Equals": [ 1069 | { 1070 | "Ref": "AWS::Region" 1071 | }, 1072 | "eu-west-3" 1073 | ] 1074 | }, 1075 | { 1076 | "Fn::Equals": [ 1077 | { 1078 | "Ref": "AWS::Region" 1079 | }, 1080 | "me-south-1" 1081 | ] 1082 | }, 1083 | { 1084 | "Fn::Equals": [ 1085 | { 1086 | "Ref": "AWS::Region" 1087 | }, 1088 | "sa-east-1" 1089 | ] 1090 | }, 1091 | { 1092 | "Fn::Equals": [ 1093 | { 1094 | "Ref": "AWS::Region" 1095 | }, 1096 | "us-east-1" 1097 | ] 1098 | }, 1099 | { 1100 | "Fn::Equals": [ 1101 | { 1102 | "Ref": "AWS::Region" 1103 | }, 1104 | "us-east-2" 1105 | ] 1106 | } 1107 | ] 1108 | }, 1109 | { 1110 | "Fn::Or": [ 1111 | { 1112 | "Fn::Equals": [ 1113 | { 1114 | "Ref": "AWS::Region" 1115 | }, 1116 | "us-west-1" 1117 | ] 1118 | }, 1119 | { 1120 | "Fn::Equals": [ 1121 | { 1122 | "Ref": "AWS::Region" 1123 | }, 1124 | "us-west-2" 1125 | ] 1126 | } 1127 | ] 1128 | } 1129 | ] 1130 | } 1131 | } 1132 | } --------------------------------------------------------------------------------