diff --git a/moto/ssm/exceptions.py b/moto/ssm/exceptions.py index 4c6b69ca6..29813c22d 100644 --- a/moto/ssm/exceptions.py +++ b/moto/ssm/exceptions.py @@ -139,3 +139,13 @@ class ParameterMaxVersionLimitExceeded(JsonRESTError): def __init__(self, message: str): super().__init__("ParameterMaxVersionLimitExceeded", message) + + +class ParameterAlreadyExists(JsonRESTError): + code = 400 + + def __init__(self) -> None: + super().__init__( + "ParameterAlreadyExists", + "The parameter already exists. To overwrite this value, set the overwrite option in the request to true.", + ) diff --git a/moto/ssm/models.py b/moto/ssm/models.py index 8b21fd4d7..1a31475a6 100644 --- a/moto/ssm/models.py +++ b/moto/ssm/models.py @@ -1952,7 +1952,11 @@ class SimpleSystemManagerBackend(BaseBackend): ) raise ValidationException(invalid_prefix_error) - if not _valid_parameter_type(parameter_type): + if ( + not _valid_parameter_type(parameter_type) + and not overwrite + and name not in self._parameters + ): raise ValidationException( f"1 validation error detected: Value '{parameter_type}' at 'type' failed to satisfy constraint: Member must satisfy enum value set: [SecureString, StringList, String]", ) diff --git a/moto/ssm/responses.py b/moto/ssm/responses.py index b53ba3c5c..d781b260d 100644 --- a/moto/ssm/responses.py +++ b/moto/ssm/responses.py @@ -2,7 +2,7 @@ import json from typing import Any, Dict, Tuple, Union from moto.core.responses import BaseResponse -from .exceptions import ValidationException +from .exceptions import ValidationException, ParameterAlreadyExists from .models import ssm_backends, SimpleSystemManagerBackend @@ -286,11 +286,7 @@ class SimpleSystemManagerResponse(BaseResponse): ) if result is None: - error = { - "__type": "ParameterAlreadyExists", - "message": f"Parameter {name} already exists.", - } - return json.dumps(error), dict(status=400) + raise ParameterAlreadyExists response = {"Version": result} return json.dumps(response) diff --git a/tests/test_ssm/test_ssm_boto3.py b/tests/test_ssm/test_ssm_boto3.py index daf6a73a9..6c227ee8d 100644 --- a/tests/test_ssm/test_ssm_boto3.py +++ b/tests/test_ssm/test_ssm_boto3.py @@ -253,7 +253,7 @@ def test_put_parameter(name): except botocore.exceptions.ClientError as err: err.operation_name.should.equal("PutParameter") err.response["Error"]["Message"].should.equal( - f"Parameter {name} already exists." + "The parameter already exists. To overwrite this value, set the overwrite option in the request to true." ) response = client.get_parameters(Names=[name], WithDecryption=False) @@ -431,6 +431,61 @@ def test_put_parameter_invalid_type(): ) +@mock_ssm +def test_update_parameter(): + # Setup + client = boto3.client("ssm", "us-east-1") + param_name = "test_param" + updated_value = "UpdatedValue" + client.put_parameter( + Description="Description", + Name=param_name, + Type="String", + Value="Value", + ) + + # Execute + response = client.put_parameter( + Name=param_name, + Overwrite=True, + Value=updated_value, + ) + new_param = client.get_parameter(Name=param_name) + + # Verify + assert response["ResponseMetadata"]["HTTPStatusCode"] == 200 + assert new_param["Parameter"]["Value"] == updated_value + + +@mock_ssm +def test_update_parameter_already_exists_error(): + # Setup + client = boto3.client("ssm", "us-east-1") + client.put_parameter( + Description="Description", + Name="Name", + Type="String", + Value="Value", + ) + + # Execute + with pytest.raises(ClientError) as exc: + client.put_parameter( + Name="Name", + Value="UpdatedValue", + ) + + # Verify + ex = exc.value + assert ex.operation_name.should.equal("PutParameter") + assert ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400) + assert ex.response["Error"]["Code"] == "ParameterAlreadyExists" + assert ( + ex.response["Error"]["Message"] + == "The parameter already exists. To overwrite this value, set the overwrite option in the request to true." + ) + + @mock_ssm def test_get_parameter(): client = boto3.client("ssm", region_name="us-east-1")