555 lines
21 KiB
Python
555 lines
21 KiB
Python
import json
|
|
from typing import Any, Dict, Tuple, Union
|
|
|
|
from moto.core.responses import BaseResponse
|
|
from .exceptions import ValidationException, ParameterAlreadyExists
|
|
from .models import ssm_backends, SimpleSystemManagerBackend
|
|
|
|
|
|
class SimpleSystemManagerResponse(BaseResponse):
|
|
def __init__(self) -> None:
|
|
super().__init__(service_name="ssm")
|
|
|
|
@property
|
|
def ssm_backend(self) -> SimpleSystemManagerBackend:
|
|
return ssm_backends[self.current_account][self.region]
|
|
|
|
def create_document(self) -> str:
|
|
content = self._get_param("Content")
|
|
requires = self._get_param("Requires")
|
|
attachments = self._get_param("Attachments")
|
|
name = self._get_param("Name")
|
|
version_name = self._get_param("VersionName")
|
|
document_type = self._get_param("DocumentType")
|
|
document_format = self._get_param("DocumentFormat", "JSON")
|
|
target_type = self._get_param("TargetType")
|
|
tags = self._get_param("Tags")
|
|
|
|
result = self.ssm_backend.create_document(
|
|
content=content,
|
|
requires=requires,
|
|
attachments=attachments,
|
|
name=name,
|
|
version_name=version_name,
|
|
document_type=document_type,
|
|
document_format=document_format,
|
|
target_type=target_type,
|
|
tags=tags,
|
|
)
|
|
|
|
return json.dumps({"DocumentDescription": result})
|
|
|
|
def delete_document(self) -> str:
|
|
name = self._get_param("Name")
|
|
document_version = self._get_param("DocumentVersion")
|
|
version_name = self._get_param("VersionName")
|
|
force = self._get_param("Force", False)
|
|
self.ssm_backend.delete_document(
|
|
name=name,
|
|
document_version=document_version,
|
|
version_name=version_name,
|
|
force=force,
|
|
)
|
|
|
|
return json.dumps({})
|
|
|
|
def get_document(self) -> str:
|
|
name = self._get_param("Name")
|
|
version_name = self._get_param("VersionName")
|
|
document_version = self._get_param("DocumentVersion")
|
|
document_format = self._get_param("DocumentFormat", "JSON")
|
|
|
|
document = self.ssm_backend.get_document(
|
|
name=name,
|
|
document_version=document_version,
|
|
document_format=document_format,
|
|
version_name=version_name,
|
|
)
|
|
|
|
return json.dumps(document)
|
|
|
|
def describe_document(self) -> str:
|
|
name = self._get_param("Name")
|
|
document_version = self._get_param("DocumentVersion")
|
|
version_name = self._get_param("VersionName")
|
|
|
|
result = self.ssm_backend.describe_document(
|
|
name=name, document_version=document_version, version_name=version_name
|
|
)
|
|
|
|
return json.dumps({"Document": result})
|
|
|
|
def update_document(self) -> str:
|
|
content = self._get_param("Content")
|
|
attachments = self._get_param("Attachments")
|
|
name = self._get_param("Name")
|
|
version_name = self._get_param("VersionName")
|
|
document_version = self._get_param("DocumentVersion")
|
|
document_format = self._get_param("DocumentFormat", "JSON")
|
|
target_type = self._get_param("TargetType")
|
|
|
|
result = self.ssm_backend.update_document(
|
|
content=content,
|
|
attachments=attachments,
|
|
name=name,
|
|
version_name=version_name,
|
|
document_version=document_version,
|
|
document_format=document_format,
|
|
target_type=target_type,
|
|
)
|
|
|
|
return json.dumps({"DocumentDescription": result})
|
|
|
|
def update_document_default_version(self) -> str:
|
|
name = self._get_param("Name")
|
|
document_version = self._get_param("DocumentVersion")
|
|
|
|
result = self.ssm_backend.update_document_default_version(
|
|
name=name, document_version=document_version
|
|
)
|
|
return json.dumps({"Description": result})
|
|
|
|
def list_documents(self) -> str:
|
|
document_filter_list = self._get_param("DocumentFilterList")
|
|
filters = self._get_param("Filters")
|
|
max_results = self._get_param("MaxResults", 10)
|
|
next_token = self._get_param("NextToken", "0")
|
|
|
|
documents, token = self.ssm_backend.list_documents(
|
|
document_filter_list=document_filter_list,
|
|
filters=filters,
|
|
max_results=max_results,
|
|
token=next_token,
|
|
)
|
|
|
|
return json.dumps({"DocumentIdentifiers": documents, "NextToken": token})
|
|
|
|
def describe_document_permission(self) -> str:
|
|
name = self._get_param("Name")
|
|
|
|
result = self.ssm_backend.describe_document_permission(name=name)
|
|
return json.dumps(result)
|
|
|
|
def modify_document_permission(self) -> str:
|
|
account_ids_to_add = self._get_param("AccountIdsToAdd")
|
|
account_ids_to_remove = self._get_param("AccountIdsToRemove")
|
|
name = self._get_param("Name")
|
|
permission_type = self._get_param("PermissionType")
|
|
shared_document_version = self._get_param("SharedDocumentVersion")
|
|
|
|
self.ssm_backend.modify_document_permission(
|
|
name=name,
|
|
account_ids_to_add=account_ids_to_add,
|
|
account_ids_to_remove=account_ids_to_remove,
|
|
shared_document_version=shared_document_version,
|
|
permission_type=permission_type,
|
|
)
|
|
return "{}"
|
|
|
|
def delete_parameter(self) -> Union[str, Tuple[str, Dict[str, int]]]:
|
|
name = self._get_param("Name")
|
|
result = self.ssm_backend.delete_parameter(name)
|
|
if result is None:
|
|
error = {
|
|
"__type": "ParameterNotFound",
|
|
"message": f"Parameter {name} not found.",
|
|
}
|
|
return json.dumps(error), dict(status=400)
|
|
return json.dumps({})
|
|
|
|
def delete_parameters(self) -> str:
|
|
names = self._get_param("Names")
|
|
result = self.ssm_backend.delete_parameters(names)
|
|
|
|
response: Dict[str, Any] = {"DeletedParameters": [], "InvalidParameters": []}
|
|
|
|
for name in names:
|
|
if name in result:
|
|
response["DeletedParameters"].append(name)
|
|
else:
|
|
response["InvalidParameters"].append(name)
|
|
return json.dumps(response)
|
|
|
|
def get_parameter(self) -> Union[str, Tuple[str, Dict[str, int]]]:
|
|
name = self._get_param("Name")
|
|
with_decryption = self._get_param("WithDecryption")
|
|
|
|
if (
|
|
name.startswith("/aws/reference/secretsmanager/")
|
|
and with_decryption is not True
|
|
):
|
|
raise ValidationException(
|
|
"WithDecryption flag must be True for retrieving a Secret Manager secret."
|
|
)
|
|
|
|
result = self.ssm_backend.get_parameter(name)
|
|
|
|
if result is None:
|
|
error = {
|
|
"__type": "ParameterNotFound",
|
|
"message": f"Parameter {name} not found.",
|
|
}
|
|
return json.dumps(error), dict(status=400)
|
|
|
|
response = {"Parameter": result.response_object(with_decryption, self.region)}
|
|
return json.dumps(response)
|
|
|
|
def get_parameters(self) -> str:
|
|
names = self._get_param("Names")
|
|
with_decryption = self._get_param("WithDecryption")
|
|
|
|
result = self.ssm_backend.get_parameters(names)
|
|
|
|
response: Dict[str, Any] = {"Parameters": [], "InvalidParameters": []}
|
|
|
|
for name, parameter in result.items():
|
|
param_data = parameter.response_object(with_decryption, self.region)
|
|
response["Parameters"].append(param_data)
|
|
|
|
valid_param_names = [name for name, parameter in result.items()]
|
|
for name in names:
|
|
if name not in valid_param_names:
|
|
response["InvalidParameters"].append(name)
|
|
return json.dumps(response)
|
|
|
|
def get_parameters_by_path(self) -> str:
|
|
path = self._get_param("Path")
|
|
with_decryption = self._get_param("WithDecryption")
|
|
recursive = self._get_param("Recursive", False)
|
|
filters = self._get_param("ParameterFilters")
|
|
token = self._get_param("NextToken")
|
|
max_results = self._get_param("MaxResults", 10)
|
|
|
|
result, next_token = self.ssm_backend.get_parameters_by_path(
|
|
path,
|
|
recursive,
|
|
filters,
|
|
next_token=token,
|
|
max_results=max_results,
|
|
)
|
|
|
|
response: Dict[str, Any] = {"Parameters": [], "NextToken": next_token}
|
|
|
|
for parameter in result:
|
|
param_data = parameter.response_object(with_decryption, self.region)
|
|
response["Parameters"].append(param_data)
|
|
|
|
return json.dumps(response)
|
|
|
|
def describe_parameters(self) -> str:
|
|
page_size = 10
|
|
filters = self._get_param("Filters")
|
|
parameter_filters = self._get_param("ParameterFilters")
|
|
token = self._get_param("NextToken")
|
|
if hasattr(token, "strip"):
|
|
token = token.strip()
|
|
if not token:
|
|
token = "0"
|
|
token = int(token)
|
|
|
|
result = self.ssm_backend.describe_parameters(filters, parameter_filters)
|
|
|
|
response: Dict[str, Any] = {"Parameters": []}
|
|
|
|
end = token + page_size
|
|
for parameter in result[token:]:
|
|
response["Parameters"].append(parameter.describe_response_object(False))
|
|
|
|
token += 1
|
|
if len(response["Parameters"]) == page_size:
|
|
response["NextToken"] = str(end)
|
|
break
|
|
|
|
return json.dumps(response)
|
|
|
|
def put_parameter(self) -> Union[str, Tuple[str, Dict[str, int]]]:
|
|
name = self._get_param("Name")
|
|
description = self._get_param("Description")
|
|
value = self._get_param("Value")
|
|
type_ = self._get_param("Type")
|
|
allowed_pattern = self._get_param("AllowedPattern")
|
|
keyid = self._get_param("KeyId")
|
|
overwrite = self._get_param("Overwrite", False)
|
|
tags = self._get_param("Tags", [])
|
|
data_type = self._get_param("DataType", "text")
|
|
|
|
result = self.ssm_backend.put_parameter(
|
|
name,
|
|
description,
|
|
value,
|
|
type_,
|
|
allowed_pattern,
|
|
keyid,
|
|
overwrite,
|
|
tags,
|
|
data_type,
|
|
)
|
|
|
|
if result is None:
|
|
raise ParameterAlreadyExists
|
|
|
|
response = {"Version": result}
|
|
return json.dumps(response)
|
|
|
|
def get_parameter_history(self) -> Union[str, Tuple[str, Dict[str, int]]]:
|
|
name = self._get_param("Name")
|
|
with_decryption = self._get_param("WithDecryption")
|
|
next_token = self._get_param("NextToken")
|
|
max_results = self._get_param("MaxResults", 50)
|
|
|
|
result, new_next_token = self.ssm_backend.get_parameter_history(
|
|
name, next_token, max_results
|
|
)
|
|
|
|
if result is None:
|
|
error = {
|
|
"__type": "ParameterNotFound",
|
|
"message": f"Parameter {name} not found.",
|
|
}
|
|
return json.dumps(error), dict(status=400)
|
|
|
|
response = {
|
|
"Parameters": [
|
|
p_v.describe_response_object(
|
|
decrypt=with_decryption, include_labels=True
|
|
)
|
|
for p_v in result
|
|
],
|
|
"NextToken": new_next_token,
|
|
}
|
|
|
|
return json.dumps(response)
|
|
|
|
def label_parameter_version(self) -> str:
|
|
name = self._get_param("Name")
|
|
version = self._get_param("ParameterVersion")
|
|
labels = self._get_param("Labels")
|
|
|
|
invalid_labels, version = self.ssm_backend.label_parameter_version(
|
|
name, version, labels
|
|
)
|
|
|
|
response = {"InvalidLabels": invalid_labels, "ParameterVersion": version}
|
|
return json.dumps(response)
|
|
|
|
def add_tags_to_resource(self) -> str:
|
|
resource_id = self._get_param("ResourceId")
|
|
resource_type = self._get_param("ResourceType")
|
|
tags = {t["Key"]: t["Value"] for t in self._get_param("Tags")}
|
|
self.ssm_backend.add_tags_to_resource(
|
|
resource_type=resource_type, resource_id=resource_id, tags=tags
|
|
)
|
|
return json.dumps({})
|
|
|
|
def remove_tags_from_resource(self) -> str:
|
|
resource_id = self._get_param("ResourceId")
|
|
resource_type = self._get_param("ResourceType")
|
|
keys = self._get_param("TagKeys")
|
|
self.ssm_backend.remove_tags_from_resource(
|
|
resource_type=resource_type, resource_id=resource_id, keys=keys
|
|
)
|
|
return json.dumps({})
|
|
|
|
def list_tags_for_resource(self) -> str:
|
|
resource_id = self._get_param("ResourceId")
|
|
resource_type = self._get_param("ResourceType")
|
|
tags = self.ssm_backend.list_tags_for_resource(
|
|
resource_type=resource_type, resource_id=resource_id
|
|
)
|
|
tag_list = [{"Key": k, "Value": v} for (k, v) in tags.items()]
|
|
response = {"TagList": tag_list}
|
|
return json.dumps(response)
|
|
|
|
def send_command(self) -> str:
|
|
comment = self._get_param("Comment", "")
|
|
document_name = self._get_param("DocumentName")
|
|
timeout_seconds = self._get_int_param("TimeoutSeconds")
|
|
instance_ids = self._get_param("InstanceIds", [])
|
|
max_concurrency = self._get_param("MaxConcurrency", "50")
|
|
max_errors = self._get_param("MaxErrors", "0")
|
|
notification_config = self._get_param("NotificationConfig")
|
|
output_s3_bucket_name = self._get_param("OutputS3BucketName", "")
|
|
output_s3_key_prefix = self._get_param("OutputS3KeyPrefix", "")
|
|
output_s3_region = self._get_param("OutputS3Region", "")
|
|
parameters = self._get_param("Parameters", {})
|
|
service_role_arn = self._get_param("ServiceRoleArn", "")
|
|
targets = self._get_param("Targets", [])
|
|
command = self.ssm_backend.send_command(
|
|
comment=comment,
|
|
document_name=document_name,
|
|
timeout_seconds=timeout_seconds,
|
|
instance_ids=instance_ids,
|
|
max_concurrency=max_concurrency,
|
|
max_errors=max_errors,
|
|
notification_config=notification_config,
|
|
output_s3_bucket_name=output_s3_bucket_name,
|
|
output_s3_key_prefix=output_s3_key_prefix,
|
|
output_s3_region=output_s3_region,
|
|
parameters=parameters,
|
|
service_role_arn=service_role_arn,
|
|
targets=targets,
|
|
)
|
|
return json.dumps({"Command": command.response_object()})
|
|
|
|
def list_commands(self) -> str:
|
|
command_id = self._get_param("CommandId")
|
|
instance_id = self._get_param("InstanceId")
|
|
commands = self.ssm_backend.list_commands(command_id, instance_id)
|
|
response = {"Commands": [command.response_object() for command in commands]}
|
|
return json.dumps(response)
|
|
|
|
def get_command_invocation(self) -> str:
|
|
command_id = self._get_param("CommandId")
|
|
instance_id = self._get_param("InstanceId")
|
|
plugin_name = self._get_param("PluginName")
|
|
response = self.ssm_backend.get_command_invocation(
|
|
command_id, instance_id, plugin_name
|
|
)
|
|
return json.dumps(response)
|
|
|
|
def create_maintenance_window(self) -> str:
|
|
name = self._get_param("Name")
|
|
desc = self._get_param("Description", None)
|
|
duration = self._get_int_param("Duration")
|
|
cutoff = self._get_int_param("Cutoff")
|
|
schedule = self._get_param("Schedule")
|
|
schedule_timezone = self._get_param("ScheduleTimezone")
|
|
schedule_offset = self._get_int_param("ScheduleOffset")
|
|
start_date = self._get_param("StartDate")
|
|
end_date = self._get_param("EndDate")
|
|
tags = self._get_param("Tags")
|
|
window_id = self.ssm_backend.create_maintenance_window(
|
|
name=name,
|
|
description=desc,
|
|
duration=duration,
|
|
cutoff=cutoff,
|
|
schedule=schedule,
|
|
schedule_timezone=schedule_timezone,
|
|
schedule_offset=schedule_offset,
|
|
start_date=start_date,
|
|
end_date=end_date,
|
|
tags=tags,
|
|
)
|
|
return json.dumps({"WindowId": window_id})
|
|
|
|
def get_maintenance_window(self) -> str:
|
|
window_id = self._get_param("WindowId")
|
|
window = self.ssm_backend.get_maintenance_window(window_id)
|
|
return json.dumps(window.to_json())
|
|
|
|
def register_target_with_maintenance_window(self) -> str:
|
|
window_target_id = self.ssm_backend.register_target_with_maintenance_window(
|
|
window_id=self._get_param("WindowId"),
|
|
resource_type=self._get_param("ResourceType"),
|
|
targets=self._get_param("Targets"),
|
|
owner_information=self._get_param("OwnerInformation"),
|
|
name=self._get_param("Name"),
|
|
description=self._get_param("Description"),
|
|
)
|
|
return json.dumps({"WindowTargetId": window_target_id})
|
|
|
|
def describe_maintenance_window_targets(self) -> str:
|
|
window_id = self._get_param("WindowId")
|
|
filters = self._get_param("Filters", [])
|
|
targets = [
|
|
target.to_json()
|
|
for target in self.ssm_backend.describe_maintenance_window_targets(
|
|
window_id, filters
|
|
)
|
|
]
|
|
return json.dumps({"Targets": targets})
|
|
|
|
def deregister_target_from_maintenance_window(self) -> str:
|
|
window_id = self._get_param("WindowId")
|
|
window_target_id = self._get_param("WindowTargetId")
|
|
self.ssm_backend.deregister_target_from_maintenance_window(
|
|
window_id, window_target_id
|
|
)
|
|
return "{}"
|
|
|
|
def describe_maintenance_windows(self) -> str:
|
|
filters = self._get_param("Filters", None)
|
|
windows = [
|
|
window.to_json()
|
|
for window in self.ssm_backend.describe_maintenance_windows(filters)
|
|
]
|
|
return json.dumps({"WindowIdentities": windows})
|
|
|
|
def delete_maintenance_window(self) -> str:
|
|
window_id = self._get_param("WindowId")
|
|
self.ssm_backend.delete_maintenance_window(window_id)
|
|
return "{}"
|
|
|
|
def create_patch_baseline(self) -> str:
|
|
baseline_id = self.ssm_backend.create_patch_baseline(
|
|
name=self._get_param("Name"),
|
|
operating_system=self._get_param("OperatingSystem"),
|
|
global_filters=self._get_param("GlobalFilters", {}),
|
|
approval_rules=self._get_param("ApprovalRules", {}),
|
|
approved_patches=self._get_param("ApprovedPatches", []),
|
|
approved_patches_compliance_level=self._get_param(
|
|
"ApprovedPatchesComplianceLevel"
|
|
),
|
|
approved_patches_enable_non_security=self._get_param(
|
|
"ApprovedPatchesEnableNonSecurity"
|
|
),
|
|
rejected_patches=self._get_param("RejectedPatches", []),
|
|
rejected_patches_action=self._get_param("RejectedPatchesAction"),
|
|
description=self._get_param("Description"),
|
|
sources=self._get_param("Sources", []),
|
|
tags=self._get_param("Tags", []),
|
|
)
|
|
return json.dumps({"BaselineId": baseline_id})
|
|
|
|
def describe_patch_baselines(self) -> str:
|
|
filters = self._get_param("Filters", None)
|
|
baselines = [
|
|
baseline.to_json()
|
|
for baseline in self.ssm_backend.describe_patch_baselines(filters)
|
|
]
|
|
return json.dumps({"BaselineIdentities": baselines})
|
|
|
|
def delete_patch_baseline(self) -> str:
|
|
baseline_id = self._get_param("BaselineId")
|
|
self.ssm_backend.delete_patch_baseline(baseline_id)
|
|
return "{}"
|
|
|
|
def register_task_with_maintenance_window(self) -> str:
|
|
window_task_id = self.ssm_backend.register_task_with_maintenance_window(
|
|
window_id=self._get_param("WindowId"),
|
|
targets=self._get_param("Targets"),
|
|
task_arn=self._get_param("TaskArn"),
|
|
service_role_arn=self._get_param("ServiceRoleArn"),
|
|
task_type=self._get_param("TaskType"),
|
|
task_parameters=self._get_param("TaskParameters"),
|
|
task_invocation_parameters=self._get_param("TaskInvocationParameters"),
|
|
priority=self._get_param("Priority"),
|
|
max_concurrency=self._get_param("MaxConcurrency"),
|
|
max_errors=self._get_param("MaxErrors"),
|
|
logging_info=self._get_param("LoggingInfo"),
|
|
name=self._get_param("Name"),
|
|
description=self._get_param("Description"),
|
|
cutoff_behavior=self._get_param("CutoffBehavior"),
|
|
alarm_configurations=self._get_param("AlarmConfigurations"),
|
|
)
|
|
return json.dumps({"WindowTaskId": window_task_id})
|
|
|
|
def describe_maintenance_window_tasks(self) -> str:
|
|
window_id = self._get_param("WindowId")
|
|
filters = self._get_param("Filters", [])
|
|
tasks = [
|
|
task.to_json()
|
|
for task in self.ssm_backend.describe_maintenance_window_tasks(
|
|
window_id, filters
|
|
)
|
|
]
|
|
return json.dumps({"Tasks": tasks})
|
|
|
|
def deregister_task_from_maintenance_window(self) -> str:
|
|
window_id = self._get_param("WindowId")
|
|
window_task_id = self._get_param("WindowTaskId")
|
|
self.ssm_backend.deregister_task_from_maintenance_window(
|
|
window_id, window_task_id
|
|
)
|
|
return "{}"
|