8000 Implemented AWS::KMS::* Resource provider for CloudFormation (#9304) · codeperl/localstack@99708f4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 99708f4

Browse files
authored
Implemented AWS::KMS::* Resource provider for CloudFormation (localstack#9304)
* Added AWS::KMS::Key resource to the resource_provider. * Support for AWS::KMS::Alias resource
1 parent 2ee626b commit 99708f4

File tree

8 files changed

+535
-0
lines changed

8 files changed

+535
-0
lines changed

localstack/services/cloudformation/resource_provider.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@
6767
"AWS::CloudWatch::Alarm": "ResourceProvider",
6868
"AWS::CloudWatch::CompositeAlarm": "ResourceProvider",
6969
# "AWS::ECR::Repository": "ResourceProvider", # FIXME: add full -ext provider & override logic for -ext
70+
"AWS::KMS::Key": "ResourceProvider",
71+
"AWS::KMS::Alias": "ResourceProvider",
7072
}
7173

7274

localstack/services/kms/resource_providers/__init__.py

Whitespace-only changes.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# LocalStack Resource Provider Scaffolding v2
2+
from __future__ import annotations
3+
4+
from pathlib import Path
5+
from typing import Optional, TypedDict
6+
7+
import localstack.services.cloudformation.provider_utils as util
8+
from localstack.services.cloudformation.resource_provider import (
9+
OperationStatus,
10+
ProgressEvent,
11+
ResourceProvider,
12+
ResourceRequest,
13+
)
14+
15+
16+
class KMSAliasProperties(TypedDict):
17+
AliasName: Optional[str]
18+
TargetKeyId: Optional[str]
19+
20+
21+
REPEATED_INVOCATION = "repeated_invocation"
22+
23+
24+
class KMSAliasProvider(ResourceProvider[KMSAliasProperties]):
25+
26+
TYPE = "AWS::KMS::Alias" # Autogenerated. Don't change
27+
SCHEMA = util.get_schema_path(Path(__file__)) # Autogenerated. Don't change
28+
29+
def create(
30+
self,
31+
request: ResourceRequest[KMSAliasProperties],
32+
) -> ProgressEvent[KMSAliasProperties]:
33+
"""
34+
Create a new resource.
35+
36+
Primary identifier fields:
37+
- /properties/AliasName
38+
39+
Required properties:
40+
- AliasName
41+
- TargetKeyId
42+
43+
Create-only properties:
44+
- /properties/AliasName
45+
46+
47+
48+
IAM permissions required:
49+
- kms:CreateAlias
50+
51+
"""
52+
model = request.desired_state
53+
kms = request.aws_client_factory.kms
54+
55+
kms.create_alias(AliasName=model["AliasName"], TargetKeyId=model["TargetKeyId"])
56+
57+
return ProgressEvent(
58+
status=OperationStatus.SUCCESS,
59+
resource_model=model,
60+
custom_context=request.custom_context,
61+
)
62+
63+
def read(
64+
self,
65+
request: ResourceRequest[KMSAliasProperties],
66+
) -> ProgressEvent[KMSAliasProperties]:
67+
"""
68+
Fetch resource information
69+
70+
IAM permissions required:
71+
- kms:ListAliases
72+
"""
73+
raise NotImplementedError
74+
75+
def delete(
76+
self,
77+
request: ResourceRequest[KMSAliasProperties],
78+
) -> ProgressEvent[KMSAliasProperties]:
79+
"""
80+
Delete a resource
81+
82+
IAM permissions required:
83+
- kms:DeleteAlias
84+
"""
85+
model = request.desired_state
86+
kms = request.aws_client_factory.kms
87+
88+
kms.delete_alias(AliasName=model["AliasName"])
89+
90+
return ProgressEvent(
91+
status=OperationStatus.SUCCESS,
92+
resource_model=model,
93+
custom_context=request.custom_context,
94+
)
95+
96+
def update(
97+
self,
98+
request: ResourceRequest[KMSAliasProperties],
99+
) -> ProgressEvent[KMSAliasProperties]:
100+
"""
101+
Update a resource
102+
103+
IAM permissions required:
104+
- kms:UpdateAlias
105+
"""
106+
raise NotImplementedError
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"typeName": "AWS::KMS::Alias",
3+
"description": "The AWS::KMS::Alias resource specifies a display name for an AWS KMS key in AWS Key Management Service (AWS KMS). You can use an alias to identify an AWS KMS key in cryptographic operations.",
4+
"sourceUrl": "https://github.com/aws-cloudformation/aws-cloudformation-rpdk.git",
5+
"properties": {
6+
"AliasName": {
7+
"description": "Specifies the alias name. This value must begin with alias/ followed by a name, such as alias/ExampleAlias. The alias name cannot begin with alias/aws/. The alias/aws/ prefix is reserved for AWS managed keys.",
8+
"type": "string",
9+
"pattern": "^(alias/)[a-zA-Z0-9:/_-]+$",
10+
"minLength": 1,
11+
"maxLength": 256
12+
},
13+
"TargetKeyId": {
14+
"description": "Identifies the AWS KMS key to which the alias refers. Specify the key ID or the Amazon Resource Name (ARN) of the AWS KMS key. You cannot specify another alias. For help finding the key ID and ARN, see Finding the Key ID and ARN in the AWS Key Management Service Developer Guide.",
15+
"type": "string",
16+
"minLength": 1,
17+
"maxLength": 256
18+
}
19+
},
20+
"additionalProperties": false,
21+
"required": [
22+
"AliasName",
23+
"TargetKeyId"
24+
],
25+
"createOnlyProperties": [
26+
"/properties/AliasName"
27+
],
28+
"primaryIdentifier": [
29+
"/properties/AliasName"
30+
],
31+
"tagging": {
32+
"taggable": false
33+
},
34+
"handlers": {
35+
"create": {
36+
"permissions": [
37+
"kms:CreateAlias"
38+
]
39+
},
40+
"read": {
41+
"permissions": [
42+
"kms:ListAliases"
43+
]
44+
},
45+
"update": {
46+
"permissions": [
47+
"kms:UpdateAlias"
48+
]
49+
},
50+
"delete": {
51+
"permissions": [
52+
"kms:DeleteAlias"
53+
]
54+
},
55+
"list": {
56+
"permissions": [
57+
"kms:ListAliases"
58+
]
59+
}
60+
}
61+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from typing import Optional, Type
2+
3+
from localstack.services.cloudformation.resource_provider import (
4+
CloudFormationResourceProviderPlugin,
5+
ResourceProvider,
6+
)
7+
8+
9+
class KMSAliasProviderPlugin(CloudFormationResourceProviderPlugin):
10+
name = "AWS::KMS::Alias"
11+
12+
def __init__(self):
13+
self.factory: Optional[Type[ResourceProvider]] = None
14+
15+
def load(self):
16+
from localstack.services.kms.resource_providers.aws_kms_alias import KMSAliasProvider
17+
18+
self.factory = KMSAliasProvider
Lines changed: 158 additions & 0 deletions
< F438 tr class="diff-line-row">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# LocalStack Resource Provider Scaffolding v2
2+
from __future__ import annotations
3+
4+
import json
5+
from pathlib import Path
6+
from typing import Optional, TypedDict
7+
8+
import localstack.services.cloudformation.provider_utils as util
9+
from localstack.services.cloudformation.resource_provider import (
10+
OperationStatus,
11+
ProgressEvent,
12+
ResourceProvider,
13+
ResourceRequest,
14+
)
15+
16+
17+
class KMSKeyProperties(TypedDict):
18+
KeyPolicy: Optional[dict | str]
19+
Arn: Optional[str]
20+
Description: Optional[str]
21+
EnableKeyRotation: Optional[bool]
22+
Enabled: Optional[bool]
23+
KeyId: Optional[str]
24+
KeySpec: Optional[str]
25+
KeyUsage: Optional[str]
26+
MultiRegion: Optional[bool]
27+
PendingWindowInDays: Optional[int]
28+
Tags: Optional[list[Tag]]
29+
30+
31+
class Tag(TypedDict):
32+
Key: Optional[str]
33+
Value: Optional[str]
34+
35+
36+
REPEATED_INVOCATION = "repeated_invocation"
37+
38+
39+
class KMSKeyProvider(ResourceProvider[KMSKeyProperties]):
40+
41+
TYPE = "AWS::KMS::Key" # Autogenerated. Don't change
42+
SCHEMA = util.get_schema_path(Path(__file__)) # Autogenerated. Don't change
43+
44+
def create(
45+
self,
46+
request: ResourceRequest[KMSKeyProperties],
47+
) -> ProgressEvent[KMSKeyProperties]:
48+
"""
49+
Create a new resource.
50+
51+
Primary identifier fields:
52+
- /properties/KeyId
53+
54+
Required properties:
55+
- KeyPolicy
56+
57+
58+
59+
Read-only properties:
60+
- /properties/Arn
61+
- /properties/KeyId
62+
63+
IAM permissions required:
64+
- kms:CreateKey
65+
- kms:EnableKeyRotation 10000
66+
- kms:DisableKey
67+
- kms:TagResource
68+
69+
"""
70+
model = request.desired_state
71+
kms = request.aws_client_factory.kms
72+
73+
params = util.select_attributes(model, ["Description", "KeySpec", "KeyUsage"])
74+
75+
if model.get("KeyPolicy"):
76+
params["Policy"] = json.dumps(model["KeyPolicy"])
77+
78+
if model.get("Tags"):
79+
params["Tags"] = [
80+
{"TagKey": tag["Key"], "TagValue": tag["Value"]} for tag in model.get("Tags", [])
81+
]
82+
response = kms.create_key(**params)
83+
model["KeyId"] = response["KeyMetadata"]["KeyId"]
84+
model["Arn"] = response["KeyMetadata"]["Arn"]
85+
86+
# key is created but some fields map to separate api calls
87+
if model.get("EnableKeyRotation", False):
88+
kms.enable_key_rotation(KeyId=model["KeyId"])
89+
else:
90+
kms.disable_key_rotation(KeyId=model["KeyId"])
91+
92+
if model.get("Enabled", True):
93+
kms.enable_key(KeyId=model["KeyId"])
94+
else:
95+
kms.disable_key(KeyId=model["KeyId"])
96+
97+
return ProgressEvent(
98+
status=OperationStatus.SUCCESS,
99+
resource_model=model,
100+
custom_context=request.custom_context,
101+
)
102+
103+
def read(
104+
self,
105+
request: ResourceRequest[KMSKeyProperties],
106+
) -> ProgressEvent[KMSKeyProperties]:
107+
"""
108+
Fetch resource information
109+
110+
IAM permissions required:
111+
- kms:DescribeKey
112+
- kms:GetKeyPolicy
113+
- kms:GetKeyRotationStatus
114+
- kms:ListResourceTags
115+
"""
116+
raise NotImplementedError
117+
118+
def delete(
119+
self,
120+
request: ResourceRequest[KMSKeyProperties],
121+
) -> ProgressEvent[KMSKeyProperties]:
122+
"""
123+
Delete a resource
124+
125+
IAM permissions required:
126+
- kms:DescribeKey
127+
- kms:ScheduleKeyDeletion
128+
"""
129+
model = request.desired_state
130+
kms = request.aws_client_factory.kms
131+
132+
kms.schedule_key_deletion(KeyId=model["KeyId"])
133+
134+
return ProgressEvent(
135+
status=OperationStatus.SUCCESS,
136+
resource_model=model,
137+
custom_context=request.custom_context,
138+
)
139+
140+
def update(
141+
self,
142+
request: ResourceRequest[KMSKeyProperties],
143+
) -> ProgressEvent[KMSKeyProperties]:
144+
"""
145+
Update a resource
146+
147+
IAM permissions required:
148+
- kms:DescribeKey
149+
- kms:DisableKey
150+
- kms:DisableKeyRotation
151+
- kms:EnableKey
152+
- kms:EnableKeyRotation
153+
- kms:PutKeyPolicy
154+
- kms:TagResource
155+
- kms:UntagResource
156+
- kms:UpdateKeyDescription
157+
"""
158+
raise NotImplementedError

0 commit comments

Comments
 (0)
0