diff --git a/moto/cloudformation/parsing.py b/moto/cloudformation/parsing.py index fb631278a..c823bae26 100644 --- a/moto/cloudformation/parsing.py +++ b/moto/cloudformation/parsing.py @@ -10,6 +10,7 @@ from moto.datapipeline import models as datapipeline_models from moto.ec2 import models as ec2_models from moto.elb import models as elb_models from moto.iam import models as iam_models +from moto.kms import models as kms_models from moto.rds import models as rds_models from moto.redshift import models as redshift_models from moto.route53 import models as route53_models @@ -44,6 +45,7 @@ MODEL_MAP = { "AWS::DataPipeline::Pipeline": datapipeline_models.Pipeline, "AWS::IAM::InstanceProfile": iam_models.InstanceProfile, "AWS::IAM::Role": iam_models.Role, + "AWS::KMS::Key": kms_models.Key, "AWS::RDS::DBInstance": rds_models.Database, "AWS::RDS::DBSecurityGroup": rds_models.SecurityGroup, "AWS::RDS::DBSubnetGroup": rds_models.SubnetGroup, diff --git a/moto/kms/models.py b/moto/kms/models.py index 1047fe71e..7ca6e24b5 100644 --- a/moto/kms/models.py +++ b/moto/kms/models.py @@ -17,6 +17,10 @@ class Key(object): self.account_id = "0123456789012" self.key_rotation_status = False + @property + def physical_resource_id(self): + return self.id + @property def arn(self): return "arn:aws:kms:{0}:{1}:key/{2}".format(self.region, self.account_id, self.id) @@ -34,6 +38,25 @@ class Key(object): } } + def delete(self, region_name): + kms_backends[region_name].delete_key(self.id) + + @classmethod + def create_from_cloudformation_json(self, resource_name, cloudformation_json, region_name): + kms_backend = kms_backends[region_name] + properties = cloudformation_json['Properties'] + + key = kms_backend.create_key( + policy=properties['KeyPolicy'], + key_usage='ENCRYPT_DECRYPT', + description=properties['Description'], + region=region_name, + ) + key.key_rotation_status = properties['EnableKeyRotation'] + key.enabled = properties['Enabled'] + + return key + class KmsBackend(BaseBackend): @@ -46,6 +69,13 @@ class KmsBackend(BaseBackend): self.keys[key.id] = key return key + def delete_key(self, key_id): + if key_id in self.keys: + if key_id in self.key_to_aliases: + self.key_to_aliases.pop(key_id) + + return self.keys.pop(key_id) + def describe_key(self, key_id): return self.keys[key_id] diff --git a/tests/test_cloudformation/test_cloudformation_stack_integration.py b/tests/test_cloudformation/test_cloudformation_stack_integration.py index 07fb93292..cbdb3d221 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_integration.py +++ b/tests/test_cloudformation/test_cloudformation_stack_integration.py @@ -23,6 +23,7 @@ from moto import ( mock_ec2, mock_elb, mock_iam, + mock_kms, mock_lambda, mock_rds, mock_redshift, @@ -1830,3 +1831,35 @@ def test_nat_gateway(): result['NatGateways'][0]['VpcId'].should.equal(vpc_id) result['NatGateways'][0]['SubnetId'].should.equal(subnet_id) result['NatGateways'][0]['State'].should.equal('available') + +@mock_cloudformation() +@mock_kms() +def test_stack_kms(): + kms_key_template = { + 'Resources': { + 'kmskey': { + 'Properties': { + 'Description': 'A kms key', + 'EnableKeyRotation': True, + 'Enabled': True, + 'KeyPolicy': 'a policy', + }, + 'Type': 'AWS::KMS::Key' + } + } + } + kms_key_template_json = json.dumps(kms_key_template) + + cf_conn = boto3.client('cloudformation', 'us-east-1') + cf_conn.create_stack( + StackName='test_stack', + TemplateBody=kms_key_template_json, + ) + + kms_conn = boto3.client('kms', 'us-east-1') + keys = kms_conn.list_keys()['Keys'] + len(keys).should.equal(1) + result = kms_conn.describe_key(KeyId=keys[0]['KeyId']) + + result['KeyMetadata']['Enabled'].should.equal(True) + result['KeyMetadata']['KeyUsage'].should.equal('ENCRYPT_DECRYPT')