Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial version of EncryptRootVolume Document #34

Merged
merged 10 commits into from
Apr 9, 2019
6 changes: 2 additions & 4 deletions Documents/Automation/EncryptRootVolume/Design/Design.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,5 @@ Document Steps:
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 successfully
# 4. Detach the old volume
# 5. Attach the new (and encrypted) volume
# 5. Clean up test stack
# 3. Ensure the Automation has executed successfull
# 4. Clean up test stack
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"schemaVersion": "0.3",
"description": "Encrypt Root Volume Automation Document",
"assumeRole": "{{ AutomationAssumeRole }}",
"assumeRole": "{{automationAssumeRole}}",
"parameters": {
"InstanceId": {
"instanceId": {
alemartini marked this conversation as resolved.
Show resolved Hide resolved
"description": "(Required) Instance ID of the ec2 instance whose root volume needs to be encrypted",
"type": "String"
},
"KmsKeyId": {
"kmsKeyId": {
"description": "(Required) Customer KMS key to use during the encryption",
"type": "String"
},
"AutomationAssumeRole": {
"automationAssumeRole": {
"type": "String",
"description": "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf.",
"default": ""
Expand All @@ -24,19 +24,24 @@
"Service": "ec2",
"Api": "DescribeInstances",
"InstanceIds": [
"{{InstanceId}}"
"{{instanceId}}"
]
},
"outputs": [
{
"Name": "AvailabilityZone",
"Name": "availabilityZone",
"Selector": "$.Reservations[0].Instances[0].Placement.AvailabilityZone",
"Type": "String"
},
{
"Name": "RootDeviceName",
"Name": "rootDeviceName",
"Selector": "$.Reservations[0].Instances[0].RootDeviceName",
"Type": "String"
},
{
"Name": "instanceState",
"Selector": "$.Reservations[0].Instances[0].State.Name",
"Type": "String"
}
],
"name": "describeInstance",
Expand All @@ -58,23 +63,28 @@
{
"Name": "attachment.instance-id",
"Values": [
"{{ InstanceId }}"
"{{instanceId}}"
]
},
{
"Name": "attachment.device",
"Values": [
"{{ describeInstance.RootDeviceName }}"
"{{describeInstance.rootDeviceName}}"
]
}
]
},
"outputs": [
{
"Name": "RootDeviceVolumeId",
"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",
Expand All @@ -89,10 +99,10 @@
"inputs": {
"DocumentName": "AWS-CreateSnapshot",
"RuntimeParameters": {
"VolumeId": "{{describeInstanceRootVolume.RootDeviceVolumeId}}"
"VolumeId": "{{describeInstanceRootVolume.rootDeviceVolumeId}}"
}
},
"name": "CreateSnapshot",
"name": "createSnapshot",
"action": "aws:executeAutomation",
"timeoutSeconds": 3600,
"onFailure": "Abort"
Expand All @@ -102,16 +112,16 @@
"inputs": {
"Service": "ec2",
"Api": "DescribeSnapshots",
"SnapshotIds": "{{CreateSnapshot.Output}}"
"SnapshotIds": "{{createSnapshot.Output}}"
},
"outputs": [
{
"Name": "SNAPSHOTID",
"Name": "SnapshotId",
"Selector": "$.Snapshots[0].SnapshotId",
"Type": "String"
}
],
"name": "extractSnapshotID",
"name": "extractSnapshotId",
"action": "aws:executeAwsApi",
"timeoutSeconds": 30,
"onFailure": "Abort"
Expand All @@ -121,41 +131,50 @@
"inputs": {
"Service": "ec2",
"Api": "CopySnapshot",
"SourceSnapshotId": "{{extractSnapshotID.SNAPSHOTID}}",
"SourceSnapshotId": "{{extractSnapshotId.SnapshotId}}",
"SourceRegion": "{{global:REGION}}",
"Encrypted": true,
"KmsKeyId": "{{KmsKeyId}}",
"KmsKeyId": "{{kmsKeyId}}",
"DestinationRegion": "{{global:REGION}}"
},
"outputs": [
{
"Name": "EncryptedSnapshotID",
"Name": "encryptedSnapshotId",
"Selector": "$.SnapshotId",
"Type": "String"
}
],
"name": "CopySnapshot",
"name": "copyAndEncryptSnapshot",
"action": "aws:executeAwsApi",
"timeoutSeconds": 3600,
"onFailure": "Abort"
},
{
"name": "waitForSnapshot",
awsandrewpark marked this conversation as resolved.
Show resolved Hide resolved
"action": "aws:waitForAwsResourceProperty",
"timeoutSeconds": "180",
awsandrewpark marked this conversation as resolved.
Show resolved Hide resolved
"inputs": {
"Duration": "PT2M"
},
"name": "sleep1",
"action": "aws:sleep"
"Service":"ec2",
"Api":"DescribeSnapshots",
"SnapshotIds": [
"{{copyAndEncryptSnapshot.encryptedSnapshotId}}"
],
"PropertySelector": "$.Snapshots[0].State",
"DesiredValues": [
"completed"
]
}
},
{
"maxAttempts": 1,
"inputs": {
"Service": "ec2",
"Api": "CreateVolume",
"AvailabilityZone": "{{describeInstance.AvailabilityZone}}",
"AvailabilityZone": "{{describeInstance.availabilityZone}}",
"Encrypted": true,
"KmsKeyId": "{{KmsKeyId}}",
"SnapshotId": "{{CopySnapshot.EncryptedSnapshotID}}",
"VolumeType": "gp2"
"KmsKeyId": "{{kmsKeyId}}",
"SnapshotId": "{{copyAndEncryptSnapshot.encryptedSnapshotId}}",
"VolumeType": "{{describeInstanceRootVolume.rootDeviceVolumeType}}"
},
"outputs": [
{
Expand All @@ -164,20 +183,20 @@
"Type": "String"
}
],
"name": "CreateVolume",
"name": "createVolume",
"action": "aws:executeAwsApi",
"timeoutSeconds": 30,
"onFailure": "Abort"
"onFailure": "step:handleFailureAndQuit"
},
{
"maxAttempts": 1,
"inputs": {
"DocumentName": "AWS-StopEC2Instance",
"RuntimeParameters": {
"InstanceId": "{{InstanceId}}"
"InstanceId": "{{instanceId}}"
}
},
"name": "StopInstance",
"name": "stopInstance",
"action": "aws:executeAutomation",
"timeoutSeconds": 300,
"onFailure": "Abort"
Expand All @@ -187,10 +206,10 @@
"inputs": {
"DocumentName": "AWS-DetachEBSVolume",
"RuntimeParameters": {
"VolumeId": "{{describeInstanceRootVolume.RootDeviceVolumeId}}"
"VolumeId": "{{describeInstanceRootVolume.rootDeviceVolumeId}}"
}
},
"name": "DetachEBSVolume",
"name": "detachEBSVolume",
awsandrewpark marked this conversation as resolved.
Show resolved Hide resolved
"action": "aws:executeAutomation",
"timeoutSeconds": 300,
"onFailure": "Abort"
Expand All @@ -200,29 +219,29 @@
"inputs": {
"DocumentName": "AWS-AttachEBSVolume",
"RuntimeParameters": {
"Device": "{{describeInstance.RootDeviceName}}",
"InstanceId": "{{InstanceId}}",
"VolumeId": "{{CreateVolume.NewRootVolumeID}}"
"Device": "{{describeInstance.rootDeviceName}}",
"InstanceId": "{{instanceId}}",
"VolumeId": "{{createVolume.NewRootVolumeID}}"
}
},
"name": "AttachNewEBSVolume",
"name": "attachNewEBSVolume",
"action": "aws:executeAutomation",
"timeoutSeconds": 180,
"onFailure": "Abort"
},
{
"name": "ApplyDeleteOnTerminationValue",
"name": "applyDeleteOnTerminationValue",
"action": "aws:executeAwsApi",
"onFailure": "Abort",
"timeoutSeconds": 60,
"maxAttempts": 10,
"inputs": {
"Service": "ec2",
"Api": "ModifyInstanceAttribute",
"InstanceId": "{{InstanceId}}",
"InstanceId": "{{instanceId}}",
"BlockDeviceMappings": [
{
"DeviceName": "{{describeInstance.RootDeviceName}}",
"DeviceName": "{{describeInstance.rootDeviceName}}",
"Ebs": {
"DeleteOnTermination": "{{describeInstanceRootVolume.RootDeviceDeleteOnTermination}}"
}
Expand All @@ -231,18 +250,45 @@
},
"isCritical": true
},

{
"maxAttempts": 1,
"name": "restoreInstanceInitialState",
awsandrewpark marked this conversation as resolved.
Show resolved Hide resolved
"action": "aws:changeInstanceState",
"onFailure": "Abort",
"inputs": {
"DocumentName": "AWS-StartEC2Instance",
"RuntimeParameters": {
"InstanceId": "{{InstanceId}}"
}
"InstanceIds": [
"{{instanceId}}"
],
"DesiredState": "{{describeInstance.instanceState}}"
},
"name": "StartInstance",
"action": "aws:executeAutomation",
"timeoutSeconds": 300,
"onFailure": "Abort"
"isCritical": true,
"isEnd": true
},

{
"name": "handleFailureAndQuit",
awsandrewpark marked this conversation as resolved.
Show resolved Hide resolved
"action": "aws:executeAwsApi",
"onFailure": "Abort",
"timeoutSeconds": 300,
"maxAttempts": 1,
"inputs": {
"Service": "ec2",
"Api": "DeleteSnapshot",
"SnapshotId": "{{copyAndEncryptSnapshot.encryptedSnapshotId}}"
}
},
{
"name": "handleFailureAndQuit2",
awsandrewpark marked this conversation as resolved.
Show resolved Hide resolved
"action": "aws:executeAwsApi",
"onFailure": "Abort",
"timeoutSeconds": 300,
"maxAttempts": 1,
"inputs": {
"Service": "ec2",
"Api": "DeleteSnapshot",
"SnapshotId": "{{extractSnapshotId.SnapshotId}}"
},
"isEnd":true
}
awsandrewpark marked this conversation as resolved.
Show resolved Hide resolved
]
}
39 changes: 34 additions & 5 deletions Documents/Automation/EncryptRootVolume/Tests/test_document.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
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):
Expand Down Expand Up @@ -145,17 +146,45 @@ def test_update_document(self):

execution = ssm_doc.execute_automation(
params={'instanceId': [instance_id],
'region': [REGION],
'KmsKeyId': [kms_key_id],
'devicename': ['/dev/xvda'],
'AutomationAssumeRole': [role_arn]})
'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':
awsandrewpark marked this conversation as resolved.
Show resolved Hide resolved
LOGGER.info("All Tests Successful, will clean up now")
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:
Expand Down