8000 Implemented AWS::KMS::* Resource provider for CloudFormation by Morijarti · Pull Request #9304 · localstack/localstack · GitHub
[go: up one dir, main page]

Skip to content

Implemented AWS::KMS::* Resource provider for CloudFormation #9304

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

Merged
merged 6 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Added AWS::KMS::Key resource to the resource_provider.
  • Loading branch information
Morijarti committed Oct 6, 2023
commit ff1b36f4696362e74682105edf6ebf8ae47b6d16
1 change: 1 addition & 0 deletions localstack/services/cloudformation/resource_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"AWS::DynamoDB::Table": "ResourceProvider",
"AWS::CloudWatch::Alarm": "ResourceProvider",
"AWS::CloudWatch::CompositeAlarm": "ResourceProvider",
"AWS::KMS::Key": "ResourceProvider",
}


Expand Down
Empty file.
159 changes: 159 additions & 0 deletions localstack/services/kms/resource_providers/aws_kms_key.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# LocalStack Resource Provider Scaffolding v2
from __future__ import annotations

import json
from pathlib import Path
from typing import Optional, Type, TypedDict

import localstack.services.cloudformation.provider_utils as util
from localstack.services.cloudformation.resource_provider import (
CloudFormationResourceProviderPlugin,
OperationStatus,
ProgressEvent,
ResourceProvider,
ResourceRequest,
)


class KMSKeyProperties(TypedDict):
KeyPolicy: Optional[dict | str]
Arn: Optional[str]
Description: Optional[str]
EnableKeyRotation: Optional[bool]
Enabled: Optional[bool]
KeyId: Optional[str]
KeySpec: Optional[str]
KeyUsage: Optional[str]
MultiRegion: Optional[bool]
PendingWindowInDays: Optional[int]
Tags: Optional[list[Tag]]


class Tag(TypedDict):
Key: Optional[str]
Value: Optional[str]


REPEATED_INVOCATION = "repeated_invocation"


class KMSKeyProvider(ResourceProvider[KMSKeyProperties]):

TYPE = "AWS::KMS::Key" # Autogenerated. Don't change
SCHEMA = util.get_schema_path(Path(__file__)) # Autogenerated. Don't change

def create(
self,
request: ResourceRequest[KMSKeyProperties],
) -> ProgressEvent[KMSKeyProperties]:
"""
Create a new resource.

Primary identifier fields:
- /properties/KeyId

Required properties:
- KeyPolicy



Read-only properties:
- /properties/Arn
- /properties/KeyId

IAM permissions required:
- kms:CreateKey
- kms:EnableKeyRotation
- kms:DisableKey
- kms:TagResource

"""
model = request.desired_state
kms = request.aws_client_factory.kms

params = util.select_attributes(model, ["Description", "KeySpec", "KeyUsage"])

if model.get("KeyPolicy"):
params["Policy"] = json.dumps(model["KeyPolicy"])

if model.get("Tags"):
params["Tags"] = [
{"TagKey": tag["Key"], "TagValue": tag["Value"]} for tag in model.get("Tags", [])
]
response = kms.create_key(**params)
model["KeyId"] = response["KeyMetadata"]["KeyId"]
model["Arn"] = response["KeyMetadata"]["Arn"]

# key is created but some fields map to separate api calls
if model.get("EnableKeyRotation", False):
kms.enable_key_rotation(KeyId=model["KeyId"])
else:
kms.disable_key_rotation(KeyId=model["KeyId"])

if model.get("Enabled", True):
kms.enable_key(KeyId=model["KeyId"])
else:
kms.disable_key(KeyId=model["KeyId"])

return ProgressEvent(
status=OperationStatus.SUCCESS,
resource_model=model,
custom_context=request.custom_context,
)

def read(
self,
request: ResourceRequest[KMSKeyProperties],
) -> ProgressEvent[KMSKeyProperties]:
"""
Fetch resource information

IAM permissions required:
- kms:DescribeKey
- kms:GetKeyPolicy
- kms:GetKeyRotationStatus
- kms:ListResourceTags
"""
raise NotImplementedError

def delete(
self,
request: ResourceRequest[KMSKeyProperties],
) -> ProgressEvent[KMSKeyProperties]:
"""
Delete a resource

IAM permissions required:
- kms:DescribeKey
- kms:ScheduleKeyDeletion
"""
model = request.desired_state
kms = request.aws_client_factory.kms

kms.schedule_key_deletion(KeyId=model["KeyId"])

return ProgressEvent(
status=OperationStatus.SUCCESS,
resource_model=model,
custom_context=request.custom_context,
)

def update(
self,
request: ResourceRequest[KMSKeyProperties],
) -> ProgressEvent[KMSKeyProperties]:
"""
Update a resource

IAM permissions required:
- kms:DescribeKey
- kms:DisableKey
- kms:DisableKeyRotation
- kms:EnableKey
- kms:EnableKeyRotation
- kms:PutKeyPolicy
- kms:TagResource
- kms:UntagResource
- kms:UpdateKeyDescription
"""
raise NotImplementedError
172 changes: 172 additions & 0 deletions localstack/services/kms/resource_providers/aws_kms_key.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
{
"typeName": "AWS::KMS::Key",
"description": "The AWS::KMS::Key resource specifies an AWS KMS key in AWS Key Management Service (AWS KMS). Authorized users can use the AWS KMS key to encrypt and decrypt small amounts of data (up to 4096 bytes), but they are more commonly used to generate data keys. You can also use AWS KMS keys to encrypt data stored in AWS services that are integrated with AWS KMS or within their applications.",
"sourceUrl": "https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-kms",
"definitions": {
"Tag": {
"description": "A key-value pair to associate with a resource.",
"type": "object",
"properties": {
"Key": {
"type": "string",
"description": "The key name of the tag. You can specify a value that is 1 to 128 Unicode characters in length and cannot be prefixed with aws:. You can use any of the following characters: the set of Unicode letters, digits, whitespace, _, ., /, =, +, and -.",
"minLength": 1,
"maxLength": 128
},
"Value": {
"type": "string",
"description": "The value for the tag. You can specify a value that is 0 to 256 Unicode characters in length and cannot be prefixed with aws:. You can use any of the following characters: the set of Unicode letters, digits, whitespace, _, ., /, =, +, and -.",
"minLength": 0,
"maxLength": 256
}
},
"additionalProperties": false,
"required": [
"Key",
"Value"
]
}
},
"properties": {
"Description": {
"description": "A description of the AWS KMS key. Use a description that helps you to distinguish this AWS KMS key from others in the account, such as its intended use.",
"type": "string",
"minLength": 0,
"maxLength": 8192
},
"Enabled": {
"description": "Specifies whether the AWS KMS key is enabled. Disabled AWS KMS keys cannot be used in cryptographic operations.",
"type": "boolean"
},
"EnableKeyRotation": {
"description": "Enables automatic rotation of the key material for the specified AWS KMS key. By default, automation key rotation is not enabled.",
"type": "boolean"
},
"KeyPolicy": {
"description": "The key policy that authorizes use of the AWS KMS key. The key policy must observe the following rules.",
"type": [
"object",
"string"
]
},
"KeyUsage": {
"description": "Determines the cryptographic operations for which you can use the AWS KMS key. The default value is ENCRYPT_DECRYPT. This property is required only for asymmetric AWS KMS keys. You can't change the KeyUsage value after the AWS KMS key is created.",
"type": "string",
"default": "ENCRYPT_DECRYPT",
"enum": [
"ENCRYPT_DECRYPT",
"SIGN_VERIFY",
"GENERATE_VERIFY_MAC"
]
},
"KeySpec": {
"description": "Specifies the type of AWS KMS key to create. The default value is SYMMETRIC_DEFAULT. This property is required only for asymmetric AWS KMS keys. You can't change the KeySpec value after the AWS KMS key is created.",
"type": "string",
"default": "SYMMETRIC_DEFAULT",
"enum": [
"SYMMETRIC_DEFAULT",
"RSA_2048",
"RSA_3072",
"RSA_4096",
"ECC_NIST_P256",
"ECC_NIST_P384",
"ECC_NIST_P521",
"ECC_SECG_P256K1",
"HMAC_224",
"HMAC_256",
"HMAC_384",
"HMAC_512",
"SM2"
]
},
"MultiRegion": {
"description": "Specifies whether the AWS KMS key should be Multi-Region. You can't change the MultiRegion value after the AWS KMS key is created.",
"type": "boolean",
"default": false
},
"PendingWindowInDays": {
"description": "Specifies the number of days in the waiting period before AWS KMS deletes an AWS KMS key that has been removed from a CloudFormation stack. Enter a value between 7 and 30 days. The default value is 30 days.",
"type": "integer",
"minimum": 7,
"maximum": 30
},
"Tags": {
"description": "An array of key-value pairs to apply to this resource.",
"type": "array",
"uniqueItems": true,
"insertionOrder": false,
"items": {
"$ref": "#/definitions/Tag"
}
},
"Arn": {
"type": "string"
},
"KeyId": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"KeyPolicy"
],
"readOnlyProperties": [
"/properties/Arn",
"/properties/KeyId"
],
"primaryIdentifier": [
"/properties/KeyId"
],
"writeOnlyProperties": [
"/properties/PendingWindowInDays"
],
"tagging": {
"taggable": true,
"tagOnCreate": true,
"tagUpdatable": true,
"cloudFormationSystemTags": false
},
"handlers": {
"create": {
"permissions": [
"kms:CreateKey",
"kms:EnableKeyRotation",
"kms:DisableKey",
"kms:TagResource"
]
},
"read": {
"permissions": [
"kms:DescribeKey",
"kms:GetKeyPolicy",
"kms:GetKeyRotationStatus",
"kms:ListResourceTags"
]
},
"update": {
"permissions": [
"kms:DescribeKey",
"kms:DisableKey",
"kms:DisableKeyRotation",
"kms:EnableKey",
"kms:EnableKeyRotation",
"kms:PutKeyPolicy",
"kms:TagResource",
"kms:UntagResource",
"kms:UpdateKeyDescription"
]
},
"delete": {
"permissions": [
"kms:DescribeKey",
"kms:ScheduleKeyDeletion"
]
},
"list": {
"permissions": [
"kms:ListKeys",
"kms:DescribeKey"
]
}
}
}
19 changes: 19 additions & 0 deletions localstack/services/kms/resource_providers/aws_kms_key_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from typing import Optional, Type

from localstack.services.cloudformation.resource_provider import (
CloudFormationResourceProviderPlugin,
ResourceProvider,
)


class KMSKeyProviderPlugin(CloudFormationResourceProviderPlugin):
name = "AWS::KMS::Key"

def __init__(self):
self.factory: Optional[Type[ResourceProvider]] = None

def load(self):
from localstack.services.kms.resource_providers.aws_kms_key import KMSKeyProvider

self.factory = KMSKeyProvider

0