diff --git a/Documents/Automation/EncryptRootVolume/Design/Design.md b/Documents/Automation/EncryptRootVolume/Design/Design.md new file mode 100644 index 0000000..4ac9ccf --- /dev/null +++ b/Documents/Automation/EncryptRootVolume/Design/Design.md @@ -0,0 +1,26 @@ +# Encrypt EBS root volume + +## Notes + +Encrypts the root volume of an EC2 instance. This will be a replace operation and not an in-line encryption operation. + +## Document Design + +Refer to schema.json + +Document Steps: +1. aws:npark-encryptrootvolume - Execute CloudFormation Template to attach the volume. + * Parameters: + * instanceId: (Required) Instance ID of the ec2 instance whose root volume needs to be encrypted + * region: (Required) Region in which the ec2 instance belong + * KmsKeyId: (Required) Customer KMS key to use during the encryption + * devicename: (Optional) Device name of the root volume. Defaults to /dev/sda1 + * AutomationAssumeRole: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf + +## Test script + +Python script will: +# 1. Create a test stack with an instance, a volume and a KMS Key (Customer managed) +# 2. Execute automation document to replace the root volume with the encrypted one (after a copy operation of the root volume snapshot) +# 3. Ensure the Automation has executed successfull +# 4. Clean up test stack diff --git a/Documents/Automation/EncryptRootVolume/Design/schema.json b/Documents/Automation/EncryptRootVolume/Design/schema.json new file mode 100644 index 0000000..daaf06e --- /dev/null +++ b/Documents/Automation/EncryptRootVolume/Design/schema.json @@ -0,0 +1,28 @@ +{ + "schemaVersion": "0.3", + "description": "Encrypt Root Volume", + "assumeRole": "{{ AutomationAssumeRole }}", + "parameters": { + "instanceId": { + "description": "Instance ID of the ec2 instance whose root volume needs to be encrypted", + "type": "String" + }, + "region": { + "description": "Region in which the ec2 instance belong", + "type": "String" + }, + "KmsKeyId": { + "description": "Customer KMS key to use during the encryption", + "type": "String" + }, + "devicename": { + "description": "Device name of the root volume. Defaults to /dev/sda1", + "type": "String" + }, + "AutomationAssumeRole": { + "description": "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf", + "type": "String" + } + }, + "mainSteps": [] +} diff --git a/Documents/Automation/EncryptRootVolume/Documents/aws-encryptrootvolume.json b/Documents/Automation/EncryptRootVolume/Documents/aws-encryptrootvolume.json new file mode 100644 index 0000000..7db21d8 --- /dev/null +++ b/Documents/Automation/EncryptRootVolume/Documents/aws-encryptrootvolume.json @@ -0,0 +1,367 @@ +{ + "schemaVersion": "0.3", + "description": "Encrypt Root Volume Automation Document", + "assumeRole": "{{automationAssumeRole}}", + "parameters": { + "instanceId": { + "description": "(Required) Instance ID of the ec2 instance whose root volume needs to be encrypted", + "type": "String" + }, + "kmsKeyId": { + "description": "(Required) Customer KMS key to use during the encryption", + "type": "String" + }, + "automationAssumeRole": { + "type": "String", + "description": "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf.", + "default": "" + } + }, + "mainSteps": [ + { + "name": "describeInstance", + "action": "aws:executeAwsApi", + "timeoutSeconds": 30, + "onFailure": "Abort", + "nextStep": "describeInstanceRootVolume", + "maxAttempts": 1, + "inputs": { + "Service": "ec2", + "Api": "DescribeInstances", + "InstanceIds": [ + "{{instanceId}}" + ] + }, + "outputs": [ + { + "Name": "availabilityZone", + "Selector": "$.Reservations[0].Instances[0].Placement.AvailabilityZone", + "Type": "String" + }, + { + "Name": "rootDeviceName", + "Selector": "$.Reservations[0].Instances[0].RootDeviceName", + "Type": "String" + }, + { + "Name": "instanceState", + "Selector": "$.Reservations[0].Instances[0].State.Name", + "Type": "String" + } + ] + }, + + { + "name": "describeInstanceRootVolume", + "action": "aws:executeAwsApi", + "onFailure": "Abort", + "nextStep": "createSnapshot", + "timeoutSeconds": 60, + "maxAttempts": 10, + "isCritical": true, + "inputs": { + "Service": "ec2", + "Api": "DescribeVolumes", + "Filters": [ + { + "Name": "attachment.instance-id", + "Values": [ + "{{instanceId}}" + ] + }, + { + "Name": "attachment.device", + "Values": [ + "{{describeInstance.rootDeviceName}}" + ] + } + ] + }, + "outputs": [ + { + "Name": "rootDeviceVolumeId", + "Selector": "$.Volumes[0].Attachments[0].VolumeId", + "Type": "String" + }, + { + "Name": "rootDeviceVolumeType", + "Selector": "$.Volumes[0].VolumeType", + "Type": "String" + }, + { + "Name": "RootDeviceDeleteOnTermination", + "Selector": "$.Volumes[0].Attachments[0].DeleteOnTermination", + "Type": "Boolean" + } + ] + }, + + { + "name": "createSnapshot", + "action": "aws:executeAutomation", + "timeoutSeconds": 1800, + "onFailure": "Abort", + "nextStep": "extractSnapshotId", + "maxAttempts": 3, + "inputs": { + "DocumentName": "AWS-CreateSnapshot", + "RuntimeParameters": { + "VolumeId": "{{describeInstanceRootVolume.rootDeviceVolumeId}}" + } + } + }, + + { + "name": "extractSnapshotId", + "action": "aws:executeAwsApi", + "timeoutSeconds": 30, + "onFailure": "step:deleteRootVolumeSnapshot", + "nextStep": "copyAndEncryptSnapshot", + "maxAttempts": 1, + "inputs": { + "Service": "ec2", + "Api": "DescribeSnapshots", + "SnapshotIds": "{{createSnapshot.Output}}" + }, + "outputs": [ + { + "Name": "SnapshotId", + "Selector": "$.Snapshots[0].SnapshotId", + "Type": "String" + } + ] + }, + + { + "name": "copyAndEncryptSnapshot", + "action": "aws:executeAwsApi", + "timeoutSeconds": 3600, + "onFailure": "step:deleteEncryptedRootVolumeSnapshot", + "nextStep": "waitForSnapshot", + "maxAttempts": 1, + "inputs": { + "Service": "ec2", + "Api": "CopySnapshot", + "SourceSnapshotId": "{{extractSnapshotId.SnapshotId}}", + "SourceRegion": "{{global:REGION}}", + "Encrypted": true, + "KmsKeyId": "{{kmsKeyId}}", + "DestinationRegion": "{{global:REGION}}" + }, + "outputs": [ + { + "Name": "encryptedSnapshotId", + "Selector": "$.SnapshotId", + "Type": "String" + } + ] + }, + + { + "name": "waitForSnapshot", + "action": "aws:waitForAwsResourceProperty", + "timeoutSeconds": 1800, + "onFailure": "step:deleteEncryptedRootVolumeSnapshot", + "nextStep": "createVolume", + "inputs": { + "Service": "ec2", + "Api": "DescribeSnapshots", + "SnapshotIds": [ + "{{copyAndEncryptSnapshot.encryptedSnapshotId}}" + ], + "PropertySelector": "$.Snapshots[0].State", + "DesiredValues": [ + "completed" + ] + } + }, + + { + "name": "createVolume", + "action": "aws:executeAwsApi", + "timeoutSeconds": 30, + "onFailure": "step:deleteNewEncryptedVolume", + "nextStep": "stopInstance", + "maxAttempts": 1, + "inputs": { + "Service": "ec2", + "Api": "CreateVolume", + "AvailabilityZone": "{{describeInstance.availabilityZone}}", + "Encrypted": true, + "KmsKeyId": "{{kmsKeyId}}", + "SnapshotId": "{{copyAndEncryptSnapshot.encryptedSnapshotId}}", + "VolumeType": "{{describeInstanceRootVolume.rootDeviceVolumeType}}" + }, + "outputs": [ + { + "Name": "NewRootVolumeID", + "Selector": "$.VolumeId", + "Type": "String" + } + ] + }, + + { + "name": "stopInstance", + "action": "aws:executeAutomation", + "timeoutSeconds": 300, + "onFailure": "step:deleteNewEncryptedVolume", + "nextStep": "detachEBSVolume", + "maxAttempts": 1, + "inputs": { + "DocumentName": "AWS-StopEC2Instance", + "RuntimeParameters": { + "InstanceId": "{{instanceId}}" + } + } + }, + + { + "name": "detachEBSVolume", + "action": "aws:executeAutomation", + "timeoutSeconds": 300, + "onFailure": "step:attachOriginalVolume", + "nextStep": "attachNewEBSVolume", + "maxAttempts": 1, + "inputs": { + "DocumentName": "AWS-DetachEBSVolume", + "RuntimeParameters": { + "VolumeId": "{{describeInstanceRootVolume.rootDeviceVolumeId}}" + } + } + }, + + { + "name": "attachNewEBSVolume", + "action": "aws:executeAutomation", + "timeoutSeconds": 180, + "onFailure": "step:detachNewVolume", + "nextStep": "applyDeleteOnTerminationValue", + "maxAttempts": 1, + "inputs": { + "DocumentName": "AWS-AttachEBSVolume", + "RuntimeParameters": { + "Device": "{{describeInstance.rootDeviceName}}", + "InstanceId": "{{instanceId}}", + "VolumeId": "{{createVolume.NewRootVolumeID}}" + } + } + }, + + { + "name": "applyDeleteOnTerminationValue", + "action": "aws:executeAwsApi", + "onFailure": "step:detachNewVolume", + "nextStep": "restoreInstanceInitialState", + "timeoutSeconds": 60, + "maxAttempts": 10, + "isCritical": true, + "inputs": { + "Service": "ec2", + "Api": "ModifyInstanceAttribute", + "InstanceId": "{{instanceId}}", + "BlockDeviceMappings": [ + { + "DeviceName": "{{describeInstance.rootDeviceName}}", + "Ebs": { + "DeleteOnTermination": "{{describeInstanceRootVolume.RootDeviceDeleteOnTermination}}" + } + } + ] + } + }, + + { + "name": "restoreInstanceInitialState", + "action": "aws:changeInstanceState", + "onFailure": "Abort", + "isCritical": true, + "isEnd": true, + "inputs": { + "InstanceIds": [ + "{{instanceId}}" + ], + "DesiredState": "{{describeInstance.instanceState}}" + } + }, + + { + "name": "detachNewVolume", + "action": "aws:executeAutomation", + "timeoutSeconds": 300, + "onFailure": "Continue", + "nextStep": "attachOriginalVolume", + "maxAttempts": 1, + "inputs": { + "DocumentName": "AWS-DetachEBSVolume", + "RuntimeParameters": { + "VolumeId": "{{createVolume.NewRootVolumeID}}" + } + } + }, + + { + "name": "attachOriginalVolume", + "action": "aws:executeAutomation", + "timeoutSeconds": 180, + "onFailure": "Continue", + "nextStep": "deleteNewEncryptedVolume", + "maxAttempts": 1, + "inputs": { + "DocumentName": "AWS-AttachEBSVolume", + "RuntimeParameters": { + "Device": "{{describeInstance.rootDeviceName}}", + "InstanceId": "{{instanceId}}", + "VolumeId": "{{describeInstanceRootVolume.rootDeviceVolumeId}}" + } + } + }, + + { + "name": "deleteNewEncryptedVolume", + "action": "aws:executeAwsApi", + "timeoutSeconds": 300, + "onFailure": "Continue", + "nextStep": "deleteEncryptedRootVolumeSnapshot", + "maxAttempts": 1, + "inputs": { + "Service": "ec2", + "Api": "DeleteVolume", + "VolumeId": "{{createVolume.NewRootVolumeID}}" + } + }, + + { + "name": "deleteEncryptedRootVolumeSnapshot", + "action": "aws:executeAwsApi", + "onFailure": "Continue", + "nextStep": "deleteRootVolumeSnapshot", + "timeoutSeconds": 300, + "maxAttempts": 1, + "inputs": { + "Service": "ec2", + "Api": "DeleteSnapshot", + "SnapshotId": "{{copyAndEncryptSnapshot.encryptedSnapshotId}}" + } + }, + + { + "name": "deleteRootVolumeSnapshot", + "action": "aws:executeAwsApi", + "onFailure": "Continue", + "nextStep": "restoreInstanceInitialState", + "timeoutSeconds": 300, + "maxAttempts": 1, + "inputs": { + "Service": "ec2", + "Api": "DeleteSnapshot", + "SnapshotId": "{{extractSnapshotId.SnapshotId}}" + } + } + + ], + "outputs":[ + "describeInstanceRootVolume.rootDeviceVolumeId", + "createVolume.NewRootVolumeID" + ] +} diff --git a/Documents/Automation/EncryptRootVolume/Makefile b/Documents/Automation/EncryptRootVolume/Makefile new file mode 100644 index 0000000..7de9b11 --- /dev/null +++ b/Documents/Automation/EncryptRootVolume/Makefile @@ -0,0 +1,34 @@ +# +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the "Software"), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +TARGET_DIR = "./Output" + +documents: targetdir createdocuments + @echo "Done making documents" + +targetdir: + @echo "Making $(TARGET_DIR)" + mkdir -p ./Output + +createdocuments: + python ./Setup/create_document.py > ./Output/npark-EncryptRootVolume.json + +test: documents + python -m unittest discover Tests + +clean: + @echo "Removing $(TARGET_DIR)" + @rm -rf ./Output diff --git a/Documents/Automation/EncryptRootVolume/Tests/CloudFormationTemplates/TestTemplate.yml b/Documents/Automation/EncryptRootVolume/Tests/CloudFormationTemplates/TestTemplate.yml new file mode 100644 index 0000000..1bb4385 --- /dev/null +++ b/Documents/Automation/EncryptRootVolume/Tests/CloudFormationTemplates/TestTemplate.yml @@ -0,0 +1,128 @@ +# +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the "Software"), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +--- +AWSTemplateFormatVersion: '2010-09-09' +Description: Test stack for encrypting root volume of an EC2 Instance +Outputs: + InstanceId: + Description: Instance Id + Value: + Ref: Instance0 + AutomationAssumeRoleName: + Description: Automation Assume Role Name + Value: !Ref AutomationAssumeRole + AutomationAssumeRoleARN: + Description: Automation Assume Role ARN + Value: !GetAtt AutomationAssumeRole.Arn + KmsKeyId: + Description: Export of KMS Key + Export: + Name: DiskEncryptKMSKeyArn + Value: + !Ref DiskEncryptKey + +Parameters: + AMI: + Description: AMI ID for instances. + Type: String + INSTANCETYPE: + Description: AMI Instance Type (t2.micro, m1.large, etc.) + Type: String + UserARN: + Description: user ARN + Type: String +Resources: + AutomationAssumeRole: + Type: "AWS::IAM::Role" + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: + Service: + - "lambda.amazonaws.com" + - "ssm.amazonaws.com" + Action: + - "sts:AssumeRole" + - Effect: "Allow" + Principal: + AWS: !Ref UserARN + Action: + - "sts:AssumeRole" + ManagedPolicyArns: + - "arn:aws:iam::aws:policy/AdministratorAccess" + Instance0: + Type: AWS::EC2::Instance + Properties: + ImageId: !Ref AMI + InstanceType: !Ref INSTANCETYPE + Tags: + - Key: Name + Value: Test root volume encryption of an EC2 Instance + + DiskEncryptKey: + Type: AWS::KMS::Key + Properties: + KeyPolicy: + Version: 2012-10-17 + Id: key-default-1 + Statement: + - Sid: Enable IAM User Permissions + Effect: Allow + Principal: + AWS: !Join + - '' + - - 'arn:aws:iam::' + - !Ref 'AWS::AccountId' + - ':root' + Action: 'kms:*' + Resource: "*" + - Sid: Allow access for Key Administrators + Effect: Allow + Principal: + AWS: !Ref UserARN + Action: + - "kms:Create*" + - "kms:Describe*" + - "kms:Enable*" + - "kms:List*" + - "kms:Put*" + - "kms:Update*" + - "kms:Revoke*" + - "kms:Disable*" + - "kms:Get*" + - "kms:Delete*" + - "kms:TagResource" + - "kms:UntagResource" + - "kms:ScheduleKeyDeletion" + - "kms:CancelKeyDeletion" + Resource: "*" + - Sid: Allow use of the key + Effect: Allow + Principal: + AWS: !Ref UserARN + Action: + - "kms:Encrypt" + - "kms:Decrypt" + - "kms:ReEncrypt*" + - "kms:GenerateDataKey*" + - "kms:DescribeKey" + Resource: "*" + Tags: + - Key: Name + Value: DiskEncryptKey diff --git a/Documents/Automation/EncryptRootVolume/Tests/test_document.py b/Documents/Automation/EncryptRootVolume/Tests/test_document.py new file mode 100644 index 0000000..3b333fa --- /dev/null +++ b/Documents/Automation/EncryptRootVolume/Tests/test_document.py @@ -0,0 +1,199 @@ +# +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the "Software"), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +#!/usr/bin/env python + +import os +import sys +import logging +import unittest + +import ConfigParser +import boto3 +import json + +import time + +DOC_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +REPOROOT = os.path.dirname(DOC_DIR) + +# Import shared testing code +sys.path.append( + os.path.join( + REPOROOT, + 'Testing' + ) +) +sys.path.append(os.path.join( + DOC_DIR, "Documents/Lambdas" +)) +sys.path.append( + os.path.abspath(os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "lib/" + )) +) +import ssm_testing # noqa pylint: disable=import-error,wrong-import-position + +CONFIG = ConfigParser.ConfigParser() +CONFIG.readfp(open(os.path.join(REPOROOT, 'Testing', 'defaults.cfg'))) +CONFIG.read([os.path.join(REPOROOT, 'Testing', 'local.cfg')]) + +REGION = CONFIG.get('general', 'region') +PREFIX = CONFIG.get('general', 'resource_prefix') + +WINDOWS_AMI_ID = CONFIG.get('windows', 'windows2016.{}'.format(REGION)) +INSTANCE_TYPE = CONFIG.get('windows', 'instance_type') +LINUX_AMI_ID = CONFIG.get('linux', 'ami') +LINUX_INSTANCE_TYPE = CONFIG.get('linux', 'instance_type') + +SSM_DOC_NAME = PREFIX + 'encryptrootvolume' +CFN_STACK_NAME = PREFIX + 'encryptrootvolume' +TEST_CFN_STACK_NAME = PREFIX + 'encryptrootvolume' + +logging.basicConfig(level=CONFIG.get('general', 'log_level').upper()) +LOGGER = logging.getLogger(__name__) +logging.getLogger('botocore').setLevel(level=logging.WARNING) + +boto3.setup_default_session(region_name=REGION) + +iam_client = boto3.client('iam') +s3_client = boto3.client('s3') +sns_client = boto3.client('sns') +sts_client = boto3.client('sts') +ec2_client = boto3.client('ec2') + + +def verify_role_created(role_arn): + LOGGER.info("Verifying that role exists: " + role_arn) + # For whatever reason assuming a role that got created too fast fails, so we just wait until we can. + retry_count = 12 + while True: + try: + sts_client.assume_role(RoleArn=role_arn, RoleSessionName="checking_assume") + break + except Exception as e: + retry_count -= 1 + if retry_count == 0: + raise e + + LOGGER.info("Unable to assume role... trying again in 5 sec") + time.sleep(5) + + +class DocumentTest(unittest.TestCase): + def test_update_document(self): + cfn_client = boto3.client('cloudformation', region_name=REGION) + ssm_client = boto3.client('ssm', region_name=REGION) + + ssm_doc = ssm_testing.SSMTester( + ssm_client=ssm_client, + doc_filename=os.path.join(DOC_DIR, + 'Documents/npark-encryptrootvolume.json'), + doc_name=SSM_DOC_NAME, + doc_type='Automation' + ) + + test_cf_stack = ssm_testing.CFNTester( + cfn_client=cfn_client, + template_filename=os.path.abspath(os.path.join( + DOC_DIR, + "Tests/CloudFormationTemplates/TestTemplate.yml")), + stack_name=TEST_CFN_STACK_NAME + ) + + LOGGER.info('Creating Test Stack') + LOGGER.info('AMI:' + LINUX_AMI_ID) + LOGGER.info('Instance Type:' + LINUX_INSTANCE_TYPE) + test_cf_stack.create_stack([ + { + 'ParameterKey': 'AMI', + 'ParameterValue': LINUX_AMI_ID + }, + { + 'ParameterKey': 'INSTANCETYPE', + 'ParameterValue': LINUX_INSTANCE_TYPE + }, + { + 'ParameterKey': 'UserARN', + 'ParameterValue': sts_client.get_caller_identity().get('Arn') + } + ]) + LOGGER.info('Test Stack has been created') + + # Verify role exists + role_arn = test_cf_stack.stack_outputs['AutomationAssumeRoleARN'] + verify_role_created(role_arn) + try: + LOGGER.info("Creating automation document") + self.assertEqual(ssm_doc.create_document(), 'Active') + + instance_id = test_cf_stack.stack_outputs['InstanceId'] + kms_key_id = test_cf_stack.stack_outputs['KmsKeyId'] + + execution = ssm_doc.execute_automation( + params={'instanceId': [instance_id], + 'kmsKeyId': [kms_key_id], + 'automationAssumeRole': [role_arn]}) + self.assertEqual(ssm_doc.automation_execution_status(ssm_client, execution, False), 'Success') + + LOGGER.info('Encryption of root volume has been completed') + + response=ssm_doc.automation_execution_status(ssm_client, execution) + if response == 'Success': + response = ec2_client.describe_instances( + InstanceIds=[ + instance_id + ], + DryRun=False + ) + rootdevicename = response['Reservations'][0]['Instances'][0]['RootDeviceName'] + + response = ec2_client.describe_volumes( + Filters=[ + { + 'Name': 'attachment.instance-id', + 'Values': [ + instance_id + ], + }, + { + 'Name': 'attachment.device', + 'Values': [ + rootdevicename + ], + }, + ], + DryRun=False + ) + is_encrypted=response['Volumes'][0]['Encrypted'] + self.assertEqual(is_encrypted, True) + if is_encrypted: + LOGGER.info("All Tests Successful, will clean up now") + else: + LOGGER.info("FAIL: root volume is NOT encrypted, will clean up now") + + finally: + try: + ssm_doc.destroy() + except Exception: + pass + + test_cf_stack.delete_stack() + + +if __name__ == '__main__': + unittest.main()