from __future__ import unicode_literals import boto3 import botocore.exceptions import sure # noqa import datetime import uuid import json from botocore.exceptions import ClientError, ParamValidationError from nose.tools import assert_raises from moto import mock_ssm, mock_cloudformation @mock_ssm def test_delete_parameter(): client = boto3.client("ssm", region_name="us-east-1") client.put_parameter( Name="test", Description="A test parameter", Value="value", Type="String" ) response = client.get_parameters(Names=["test"]) len(response["Parameters"]).should.equal(1) client.delete_parameter(Name="test") response = client.get_parameters(Names=["test"]) len(response["Parameters"]).should.equal(0) @mock_ssm def test_delete_parameters(): client = boto3.client("ssm", region_name="us-east-1") client.put_parameter( Name="test", Description="A test parameter", Value="value", Type="String" ) response = client.get_parameters(Names=["test"]) len(response["Parameters"]).should.equal(1) result = client.delete_parameters(Names=["test", "invalid"]) len(result["DeletedParameters"]).should.equal(1) len(result["InvalidParameters"]).should.equal(1) response = client.get_parameters(Names=["test"]) len(response["Parameters"]).should.equal(0) @mock_ssm def test_get_parameters_by_path(): client = boto3.client("ssm", region_name="us-east-1") client.put_parameter( Name="/foo/name1", Description="A test parameter", Value="value1", Type="String" ) client.put_parameter( Name="/foo/name2", Description="A test parameter", Value="value2", Type="String" ) client.put_parameter( Name="/bar/name3", Description="A test parameter", Value="value3", Type="String" ) client.put_parameter( Name="/bar/name3/name4", Description="A test parameter", Value="value4", Type="String", ) client.put_parameter( Name="/baz/name1", Description="A test parameter (list)", Value="value1,value2,value3", Type="StringList", ) client.put_parameter( Name="/baz/name2", Description="A test parameter", Value="value1", Type="String" ) client.put_parameter( Name="/baz/pwd", Description="A secure test parameter", Value="my_secret", Type="SecureString", KeyId="alias/aws/ssm", ) client.put_parameter( Name="foo", Description="A test parameter", Value="bar", Type="String" ) client.put_parameter( Name="baz", Description="A test parameter", Value="qux", Type="String" ) response = client.get_parameters_by_path(Path="/", Recursive=False) len(response["Parameters"]).should.equal(2) {p["Value"] for p in response["Parameters"]}.should.equal(set(["bar", "qux"])) response = client.get_parameters_by_path(Path="/", Recursive=True) len(response["Parameters"]).should.equal(9) response = client.get_parameters_by_path(Path="/foo") len(response["Parameters"]).should.equal(2) {p["Value"] for p in response["Parameters"]}.should.equal(set(["value1", "value2"])) response = client.get_parameters_by_path(Path="/bar", Recursive=False) len(response["Parameters"]).should.equal(1) response["Parameters"][0]["Value"].should.equal("value3") response = client.get_parameters_by_path(Path="/bar", Recursive=True) len(response["Parameters"]).should.equal(2) {p["Value"] for p in response["Parameters"]}.should.equal(set(["value3", "value4"])) response = client.get_parameters_by_path(Path="/baz") len(response["Parameters"]).should.equal(3) filters = [{"Key": "Type", "Option": "Equals", "Values": ["StringList"]}] response = client.get_parameters_by_path(Path="/baz", ParameterFilters=filters) len(response["Parameters"]).should.equal(1) {p["Name"] for p in response["Parameters"]}.should.equal(set(["/baz/name1"])) # note: 'Option' is optional (default: 'Equals') filters = [{"Key": "Type", "Values": ["StringList"]}] response = client.get_parameters_by_path(Path="/baz", ParameterFilters=filters) len(response["Parameters"]).should.equal(1) {p["Name"] for p in response["Parameters"]}.should.equal(set(["/baz/name1"])) filters = [{"Key": "Type", "Option": "Equals", "Values": ["String"]}] response = client.get_parameters_by_path(Path="/baz", ParameterFilters=filters) len(response["Parameters"]).should.equal(1) {p["Name"] for p in response["Parameters"]}.should.equal(set(["/baz/name2"])) filters = [ {"Key": "Type", "Option": "Equals", "Values": ["String", "SecureString"]} ] response = client.get_parameters_by_path(Path="/baz", ParameterFilters=filters) len(response["Parameters"]).should.equal(2) {p["Name"] for p in response["Parameters"]}.should.equal( set(["/baz/name2", "/baz/pwd"]) ) filters = [{"Key": "Type", "Option": "BeginsWith", "Values": ["String"]}] response = client.get_parameters_by_path(Path="/baz", ParameterFilters=filters) len(response["Parameters"]).should.equal(2) {p["Name"] for p in response["Parameters"]}.should.equal( set(["/baz/name1", "/baz/name2"]) ) filters = [{"Key": "KeyId", "Option": "Equals", "Values": ["alias/aws/ssm"]}] response = client.get_parameters_by_path(Path="/baz", ParameterFilters=filters) len(response["Parameters"]).should.equal(1) {p["Name"] for p in response["Parameters"]}.should.equal(set(["/baz/pwd"])) @mock_ssm def test_put_parameter(): client = boto3.client("ssm", region_name="us-east-1") response = client.put_parameter( Name="test", Description="A test parameter", Value="value", Type="String" ) response["Version"].should.equal(1) response = client.get_parameters(Names=["test"], WithDecryption=False) len(response["Parameters"]).should.equal(1) response["Parameters"][0]["Name"].should.equal("test") response["Parameters"][0]["Value"].should.equal("value") response["Parameters"][0]["Type"].should.equal("String") response["Parameters"][0]["Version"].should.equal(1) try: client.put_parameter( Name="test", Description="desc 2", Value="value 2", Type="String" ) raise RuntimeError("Should fail") except botocore.exceptions.ClientError as err: err.operation_name.should.equal("PutParameter") err.response["Error"]["Message"].should.equal("Parameter test already exists.") response = client.get_parameters(Names=["test"], WithDecryption=False) # without overwrite nothing change len(response["Parameters"]).should.equal(1) response["Parameters"][0]["Name"].should.equal("test") response["Parameters"][0]["Value"].should.equal("value") response["Parameters"][0]["Type"].should.equal("String") response["Parameters"][0]["Version"].should.equal(1) response = client.put_parameter( Name="test", Description="desc 3", Value="value 3", Type="String", Overwrite=True, ) response["Version"].should.equal(2) response = client.get_parameters(Names=["test"], WithDecryption=False) # without overwrite nothing change len(response["Parameters"]).should.equal(1) response["Parameters"][0]["Name"].should.equal("test") response["Parameters"][0]["Value"].should.equal("value 3") response["Parameters"][0]["Type"].should.equal("String") response["Parameters"][0]["Version"].should.equal(2) @mock_ssm def test_put_parameter_china(): client = boto3.client("ssm", region_name="cn-north-1") response = client.put_parameter( Name="test", Description="A test parameter", Value="value", Type="String" ) response["Version"].should.equal(1) @mock_ssm def test_get_parameter(): client = boto3.client("ssm", region_name="us-east-1") client.put_parameter( Name="test", Description="A test parameter", Value="value", Type="String" ) response = client.get_parameter(Name="test", WithDecryption=False) response["Parameter"]["Name"].should.equal("test") response["Parameter"]["Value"].should.equal("value") response["Parameter"]["Type"].should.equal("String") @mock_ssm def test_get_nonexistant_parameter(): client = boto3.client("ssm", region_name="us-east-1") try: client.get_parameter(Name="test_noexist", WithDecryption=False) raise RuntimeError("Should of failed") except botocore.exceptions.ClientError as err: err.operation_name.should.equal("GetParameter") err.response["Error"]["Message"].should.equal( "Parameter test_noexist not found." ) @mock_ssm def test_describe_parameters(): client = boto3.client("ssm", region_name="us-east-1") client.put_parameter( Name="test", Description="A test parameter", Value="value", Type="String", AllowedPattern=r".*", ) response = client.describe_parameters() parameters = response["Parameters"] parameters.should.have.length_of(1) parameters[0]["Name"].should.equal("test") parameters[0]["Type"].should.equal("String") parameters[0]["AllowedPattern"].should.equal(r".*") @mock_ssm def test_describe_parameters_paging(): client = boto3.client("ssm", region_name="us-east-1") for i in range(50): client.put_parameter(Name="param-%d" % i, Value="value-%d" % i, Type="String") response = client.describe_parameters() response["Parameters"].should.have.length_of(10) response["NextToken"].should.equal("10") response = client.describe_parameters(NextToken=response["NextToken"]) response["Parameters"].should.have.length_of(10) response["NextToken"].should.equal("20") response = client.describe_parameters(NextToken=response["NextToken"]) response["Parameters"].should.have.length_of(10) response["NextToken"].should.equal("30") response = client.describe_parameters(NextToken=response["NextToken"]) response["Parameters"].should.have.length_of(10) response["NextToken"].should.equal("40") response = client.describe_parameters(NextToken=response["NextToken"]) response["Parameters"].should.have.length_of(10) response["NextToken"].should.equal("50") response = client.describe_parameters(NextToken=response["NextToken"]) response["Parameters"].should.have.length_of(0) response.should_not.have.key("NextToken") @mock_ssm def test_describe_parameters_filter_names(): client = boto3.client("ssm", region_name="us-east-1") for i in range(50): p = {"Name": "param-%d" % i, "Value": "value-%d" % i, "Type": "String"} if i % 5 == 0: p["Type"] = "SecureString" p["KeyId"] = "a key" client.put_parameter(**p) response = client.describe_parameters( Filters=[{"Key": "Name", "Values": ["param-22"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(1) parameters[0]["Name"].should.equal("param-22") parameters[0]["Type"].should.equal("String") response.should_not.have.key("NextToken") @mock_ssm def test_describe_parameters_filter_type(): client = boto3.client("ssm", region_name="us-east-1") for i in range(50): p = {"Name": "param-%d" % i, "Value": "value-%d" % i, "Type": "String"} if i % 5 == 0: p["Type"] = "SecureString" p["KeyId"] = "a key" client.put_parameter(**p) response = client.describe_parameters( Filters=[{"Key": "Type", "Values": ["SecureString"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(10) parameters[0]["Type"].should.equal("SecureString") response.should.have.key("NextToken").which.should.equal("10") @mock_ssm def test_describe_parameters_filter_keyid(): client = boto3.client("ssm", region_name="us-east-1") for i in range(50): p = {"Name": "param-%d" % i, "Value": "value-%d" % i, "Type": "String"} if i % 5 == 0: p["Type"] = "SecureString" p["KeyId"] = "key:%d" % i client.put_parameter(**p) response = client.describe_parameters( Filters=[{"Key": "KeyId", "Values": ["key:10"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(1) parameters[0]["Name"].should.equal("param-10") parameters[0]["Type"].should.equal("SecureString") response.should_not.have.key("NextToken") @mock_ssm def test_describe_parameters_with_parameter_filters_keyid(): client = boto3.client("ssm", region_name="us-east-1") client.put_parameter(Name="secure-param", Value="secure-value", Type="SecureString") client.put_parameter( Name="custom-secure-param", Value="custom-secure-value", Type="SecureString", KeyId="alias/custom", ) client.put_parameter(Name="param", Value="value", Type="String") response = client.describe_parameters( ParameterFilters=[{"Key": "KeyId", "Values": ["alias/aws/ssm"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(1) parameters[0]["Name"].should.equal("secure-param") parameters[0]["Type"].should.equal("SecureString") response.should_not.have.key("NextToken") response = client.describe_parameters( ParameterFilters=[{"Key": "KeyId", "Values": ["alias/custom"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(1) parameters[0]["Name"].should.equal("custom-secure-param") parameters[0]["Type"].should.equal("SecureString") response.should_not.have.key("NextToken") response = client.describe_parameters( ParameterFilters=[{"Key": "KeyId", "Option": "BeginsWith", "Values": ["alias"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(2) response.should_not.have.key("NextToken") @mock_ssm def test_describe_parameters_with_parameter_filters_name(): client = boto3.client("ssm", region_name="us-east-1") client.put_parameter(Name="param", Value="value", Type="String") client.put_parameter(Name="/param-2", Value="value-2", Type="String") response = client.describe_parameters( ParameterFilters=[{"Key": "Name", "Values": ["param"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(1) parameters[0]["Name"].should.equal("param") parameters[0]["Type"].should.equal("String") response.should_not.have.key("NextToken") response = client.describe_parameters( ParameterFilters=[{"Key": "Name", "Values": ["/param"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(1) parameters[0]["Name"].should.equal("param") parameters[0]["Type"].should.equal("String") response.should_not.have.key("NextToken") response = client.describe_parameters( ParameterFilters=[{"Key": "Name", "Values": ["param-2"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(1) parameters[0]["Name"].should.equal("/param-2") parameters[0]["Type"].should.equal("String") response.should_not.have.key("NextToken") response = client.describe_parameters( ParameterFilters=[{"Key": "Name", "Option": "BeginsWith", "Values": ["param"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(2) response.should_not.have.key("NextToken") @mock_ssm def test_describe_parameters_with_parameter_filters_path(): client = boto3.client("ssm", region_name="us-east-1") client.put_parameter(Name="/foo/name1", Value="value1", Type="String") client.put_parameter(Name="/foo/name2", Value="value2", Type="String") client.put_parameter(Name="/bar/name3", Value="value3", Type="String") client.put_parameter(Name="/bar/name3/name4", Value="value4", Type="String") client.put_parameter(Name="foo", Value="bar", Type="String") response = client.describe_parameters( ParameterFilters=[{"Key": "Path", "Values": ["/fo"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(0) response.should_not.have.key("NextToken") response = client.describe_parameters( ParameterFilters=[{"Key": "Path", "Values": ["/"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(1) parameters[0]["Name"].should.equal("foo") parameters[0]["Type"].should.equal("String") response.should_not.have.key("NextToken") response = client.describe_parameters( ParameterFilters=[{"Key": "Path", "Values": ["/", "/foo"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(3) {parameter["Name"] for parameter in response["Parameters"]}.should.equal( {"/foo/name1", "/foo/name2", "foo"} ) response.should_not.have.key("NextToken") response = client.describe_parameters( ParameterFilters=[{"Key": "Path", "Values": ["/foo/"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(2) {parameter["Name"] for parameter in response["Parameters"]}.should.equal( {"/foo/name1", "/foo/name2"} ) response.should_not.have.key("NextToken") response = client.describe_parameters( ParameterFilters=[ {"Key": "Path", "Option": "OneLevel", "Values": ["/bar/name3"]} ] ) parameters = response["Parameters"] parameters.should.have.length_of(1) parameters[0]["Name"].should.equal("/bar/name3/name4") parameters[0]["Type"].should.equal("String") response.should_not.have.key("NextToken") response = client.describe_parameters( ParameterFilters=[{"Key": "Path", "Option": "Recursive", "Values": ["/fo"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(0) response.should_not.have.key("NextToken") response = client.describe_parameters( ParameterFilters=[{"Key": "Path", "Option": "Recursive", "Values": ["/"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(5) response.should_not.have.key("NextToken") response = client.describe_parameters( ParameterFilters=[ {"Key": "Path", "Option": "Recursive", "Values": ["/foo", "/bar"]} ] ) parameters = response["Parameters"] parameters.should.have.length_of(4) {parameter["Name"] for parameter in response["Parameters"]}.should.equal( {"/foo/name1", "/foo/name2", "/bar/name3", "/bar/name3/name4"} ) response.should_not.have.key("NextToken") response = client.describe_parameters( ParameterFilters=[{"Key": "Path", "Option": "Recursive", "Values": ["/foo/"]}] ) parameters = response["Parameters"] parameters.should.have.length_of(2) {parameter["Name"] for parameter in response["Parameters"]}.should.equal( {"/foo/name1", "/foo/name2"} ) response.should_not.have.key("NextToken") response = client.describe_parameters( ParameterFilters=[ {"Key": "Path", "Option": "Recursive", "Values": ["/bar/name3"]} ] ) parameters = response["Parameters"] parameters.should.have.length_of(1) parameters[0]["Name"].should.equal("/bar/name3/name4") parameters[0]["Type"].should.equal("String") response.should_not.have.key("NextToken") @mock_ssm def test_describe_parameters_invalid_parameter_filters(): client = boto3.client("ssm", region_name="us-east-1") client.describe_parameters.when.called_with( Filters=[{"Key": "Name", "Values": ["test"]}], ParameterFilters=[{"Key": "Name", "Values": ["test"]}], ).should.throw( ClientError, "You can use either Filters or ParameterFilters in a single request.", ) client.describe_parameters.when.called_with(ParameterFilters=[{}]).should.throw( ParamValidationError, 'Parameter validation failed:\nMissing required parameter in ParameterFilters[0]: "Key"', ) client.describe_parameters.when.called_with( ParameterFilters=[{"Key": "key"}] ).should.throw( ClientError, '1 validation error detected: Value "key" at "parameterFilters.1.member.key" failed to satisfy constraint: ' "Member must satisfy regular expression pattern: tag:.+|Name|Type|KeyId|Path|Label|Tier", ) long_key = "tag:" + "t" * 129 client.describe_parameters.when.called_with( ParameterFilters=[{"Key": long_key}] ).should.throw( ClientError, '1 validation error detected: Value "{value}" at "parameterFilters.1.member.key" failed to satisfy constraint: ' "Member must have length less than or equal to 132".format(value=long_key), ) client.describe_parameters.when.called_with( ParameterFilters=[{"Key": "Name", "Option": "over 10 chars"}] ).should.throw( ClientError, '1 validation error detected: Value "over 10 chars" at "parameterFilters.1.member.option" failed to satisfy constraint: ' "Member must have length less than or equal to 10", ) many_values = ["test"] * 51 client.describe_parameters.when.called_with( ParameterFilters=[{"Key": "Name", "Values": many_values}] ).should.throw( ClientError, '1 validation error detected: Value "{value}" at "parameterFilters.1.member.values" failed to satisfy constraint: ' "Member must have length less than or equal to 50".format(value=many_values), ) long_value = ["t" * 1025] client.describe_parameters.when.called_with( ParameterFilters=[{"Key": "Name", "Values": long_value}] ).should.throw( ClientError, '1 validation error detected: Value "{value}" at "parameterFilters.1.member.values" failed to satisfy constraint: ' "[Member must have length less than or equal to 1024, Member must have length greater than or equal to 1]".format( value=long_value ), ) client.describe_parameters.when.called_with( ParameterFilters=[{"Key": "Name", "Option": "over 10 chars"}, {"Key": "key"}] ).should.throw( ClientError, "2 validation errors detected: " 'Value "over 10 chars" at "parameterFilters.1.member.option" failed to satisfy constraint: ' "Member must have length less than or equal to 10; " 'Value "key" at "parameterFilters.2.member.key" failed to satisfy constraint: ' "Member must satisfy regular expression pattern: tag:.+|Name|Type|KeyId|Path|Label|Tier", ) client.describe_parameters.when.called_with( ParameterFilters=[{"Key": "Label"}] ).should.throw( ClientError, "The following filter key is not valid: Label. Valid filter keys include: [Path, Name, Type, KeyId, Tier].", ) client.describe_parameters.when.called_with( ParameterFilters=[{"Key": "Name"}] ).should.throw( ClientError, "The following filter values are missing : null for filter key Name.", ) client.describe_parameters.when.called_with( ParameterFilters=[{"Key": "Name", "Values": []}] ).should.throw( ParamValidationError, "Invalid length for parameter ParameterFilters[0].Values, value: 0, valid range: 1-inf", ) client.describe_parameters.when.called_with( ParameterFilters=[ {"Key": "Name", "Values": ["test"]}, {"Key": "Name", "Values": ["test test"]}, ] ).should.throw( ClientError, "The following filter is duplicated in the request: Name. A request can contain only one occurrence of a specific filter.", ) for value in ["/###", "//", "test"]: client.describe_parameters.when.called_with( ParameterFilters=[{"Key": "Path", "Values": [value]}] ).should.throw( ClientError, 'The parameter doesn\'t meet the parameter name requirements. The parameter name must begin with a forward slash "/". ' 'It can\'t be prefixed with "aws" or "ssm" (case-insensitive). ' "It must use only letters, numbers, or the following symbols: . (period), - (hyphen), _ (underscore). " 'Special characters are not allowed. All sub-paths, if specified, must use the forward slash symbol "/". ' "Valid example: /get/parameters2-/by1./path0_.", ) client.describe_parameters.when.called_with( ParameterFilters=[{"Key": "Path", "Values": ["/aws", "/ssm"]}] ).should.throw( ClientError, 'Filters for common parameters can\'t be prefixed with "aws" or "ssm" (case-insensitive). ' "When using global parameters, please specify within a global namespace.", ) client.describe_parameters.when.called_with( ParameterFilters=[{"Key": "Path", "Option": "Equals", "Values": ["test"]}] ).should.throw( ClientError, "The following filter option is not valid: Equals. Valid options include: [Recursive, OneLevel].", ) client.describe_parameters.when.called_with( ParameterFilters=[{"Key": "Tier", "Values": ["test"]}] ).should.throw( ClientError, "The following filter value is not valid: test. Valid values include: [Standard, Advanced, Intelligent-Tiering]", ) client.describe_parameters.when.called_with( ParameterFilters=[{"Key": "Type", "Values": ["test"]}] ).should.throw( ClientError, "The following filter value is not valid: test. Valid values include: [String, StringList, SecureString]", ) client.describe_parameters.when.called_with( ParameterFilters=[{"Key": "Name", "Option": "option", "Values": ["test"]}] ).should.throw( ClientError, "The following filter option is not valid: option. Valid options include: [BeginsWith, Equals].", ) @mock_ssm def test_describe_parameters_attributes(): client = boto3.client("ssm", region_name="us-east-1") client.put_parameter( Name="aa", Value="11", Type="String", Description="my description" ) client.put_parameter(Name="bb", Value="22", Type="String") response = client.describe_parameters() parameters = response["Parameters"] parameters.should.have.length_of(2) parameters[0]["Description"].should.equal("my description") parameters[0]["Version"].should.equal(1) parameters[0]["LastModifiedDate"].should.be.a(datetime.date) parameters[0]["LastModifiedUser"].should.equal("N/A") parameters[1].should_not.have.key("Description") parameters[1]["Version"].should.equal(1) @mock_ssm def test_get_parameter_invalid(): client = client = boto3.client("ssm", region_name="us-east-1") response = client.get_parameters(Names=["invalid"], WithDecryption=False) len(response["Parameters"]).should.equal(0) len(response["InvalidParameters"]).should.equal(1) response["InvalidParameters"][0].should.equal("invalid") @mock_ssm def test_put_parameter_secure_default_kms(): client = boto3.client("ssm", region_name="us-east-1") client.put_parameter( Name="test", Description="A test parameter", Value="value", Type="SecureString" ) response = client.get_parameters(Names=["test"], WithDecryption=False) len(response["Parameters"]).should.equal(1) response["Parameters"][0]["Name"].should.equal("test") response["Parameters"][0]["Value"].should.equal("kms:alias/aws/ssm:value") response["Parameters"][0]["Type"].should.equal("SecureString") response = client.get_parameters(Names=["test"], WithDecryption=True) len(response["Parameters"]).should.equal(1) response["Parameters"][0]["Name"].should.equal("test") response["Parameters"][0]["Value"].should.equal("value") response["Parameters"][0]["Type"].should.equal("SecureString") @mock_ssm def test_put_parameter_secure_custom_kms(): client = boto3.client("ssm", region_name="us-east-1") client.put_parameter( Name="test", Description="A test parameter", Value="value", Type="SecureString", KeyId="foo", ) response = client.get_parameters(Names=["test"], WithDecryption=False) len(response["Parameters"]).should.equal(1) response["Parameters"][0]["Name"].should.equal("test") response["Parameters"][0]["Value"].should.equal("kms:foo:value") response["Parameters"][0]["Type"].should.equal("SecureString") response = client.get_parameters(Names=["test"], WithDecryption=True) len(response["Parameters"]).should.equal(1) response["Parameters"][0]["Name"].should.equal("test") response["Parameters"][0]["Value"].should.equal("value") response["Parameters"][0]["Type"].should.equal("SecureString") @mock_ssm def test_get_parameter_history(): client = boto3.client("ssm", region_name="us-east-1") test_parameter_name = "test" for i in range(3): client.put_parameter( Name=test_parameter_name, Description="A test parameter version %d" % i, Value="value-%d" % i, Type="String", Overwrite=True, ) response = client.get_parameter_history(Name=test_parameter_name) parameters_response = response["Parameters"] for index, param in enumerate(parameters_response): param["Name"].should.equal(test_parameter_name) param["Type"].should.equal("String") param["Value"].should.equal("value-%d" % index) param["Version"].should.equal(index + 1) param["Description"].should.equal("A test parameter version %d" % index) len(parameters_response).should.equal(3) @mock_ssm def test_get_parameter_history_with_secure_string(): client = boto3.client("ssm", region_name="us-east-1") test_parameter_name = "test" for i in range(3): client.put_parameter( Name=test_parameter_name, Description="A test parameter version %d" % i, Value="value-%d" % i, Type="SecureString", Overwrite=True, ) for with_decryption in [True, False]: response = client.get_parameter_history( Name=test_parameter_name, WithDecryption=with_decryption ) parameters_response = response["Parameters"] for index, param in enumerate(parameters_response): param["Name"].should.equal(test_parameter_name) param["Type"].should.equal("SecureString") expected_plaintext_value = "value-%d" % index if with_decryption: param["Value"].should.equal(expected_plaintext_value) else: param["Value"].should.equal( "kms:alias/aws/ssm:%s" % expected_plaintext_value ) param["Version"].should.equal(index + 1) param["Description"].should.equal("A test parameter version %d" % index) len(parameters_response).should.equal(3) @mock_ssm def test_get_parameter_history_missing_parameter(): client = boto3.client("ssm", region_name="us-east-1") try: client.get_parameter_history(Name="test_noexist") raise RuntimeError("Should have failed") except botocore.exceptions.ClientError as err: err.operation_name.should.equal("GetParameterHistory") err.response["Error"]["Message"].should.equal( "Parameter test_noexist not found." ) @mock_ssm def test_add_remove_list_tags_for_resource(): client = boto3.client("ssm", region_name="us-east-1") client.add_tags_to_resource( ResourceId="test", ResourceType="Parameter", Tags=[{"Key": "test-key", "Value": "test-value"}], ) response = client.list_tags_for_resource( ResourceId="test", ResourceType="Parameter" ) len(response["TagList"]).should.equal(1) response["TagList"][0]["Key"].should.equal("test-key") response["TagList"][0]["Value"].should.equal("test-value") client.remove_tags_from_resource( ResourceId="test", ResourceType="Parameter", TagKeys=["test-key"] ) response = client.list_tags_for_resource( ResourceId="test", ResourceType="Parameter" ) len(response["TagList"]).should.equal(0) @mock_ssm def test_send_command(): ssm_document = "AWS-RunShellScript" params = {"commands": ["#!/bin/bash\necho 'hello world'"]} client = boto3.client("ssm", region_name="us-east-1") # note the timeout is determined server side, so this is a simpler check. before = datetime.datetime.now() response = client.send_command( InstanceIds=["i-123456"], DocumentName=ssm_document, Parameters=params, OutputS3Region="us-east-2", OutputS3BucketName="the-bucket", OutputS3KeyPrefix="pref", ) cmd = response["Command"] cmd["CommandId"].should_not.be(None) cmd["DocumentName"].should.equal(ssm_document) cmd["Parameters"].should.equal(params) cmd["OutputS3Region"].should.equal("us-east-2") cmd["OutputS3BucketName"].should.equal("the-bucket") cmd["OutputS3KeyPrefix"].should.equal("pref") cmd["ExpiresAfter"].should.be.greater_than(before) # test sending a command without any optional parameters response = client.send_command(DocumentName=ssm_document) cmd = response["Command"] cmd["CommandId"].should_not.be(None) cmd["DocumentName"].should.equal(ssm_document) @mock_ssm def test_list_commands(): client = boto3.client("ssm", region_name="us-east-1") ssm_document = "AWS-RunShellScript" params = {"commands": ["#!/bin/bash\necho 'hello world'"]} response = client.send_command( InstanceIds=["i-123456"], DocumentName=ssm_document, Parameters=params, OutputS3Region="us-east-2", OutputS3BucketName="the-bucket", OutputS3KeyPrefix="pref", ) cmd = response["Command"] cmd_id = cmd["CommandId"] # get the command by id response = client.list_commands(CommandId=cmd_id) cmds = response["Commands"] len(cmds).should.equal(1) cmds[0]["CommandId"].should.equal(cmd_id) # add another command with the same instance id to test listing by # instance id client.send_command(InstanceIds=["i-123456"], DocumentName=ssm_document) response = client.list_commands(InstanceId="i-123456") cmds = response["Commands"] len(cmds).should.equal(2) for cmd in cmds: cmd["InstanceIds"].should.contain("i-123456") # test the error case for an invalid command id with assert_raises(ClientError): response = client.list_commands(CommandId=str(uuid.uuid4())) @mock_ssm def test_get_command_invocation(): client = boto3.client("ssm", region_name="us-east-1") ssm_document = "AWS-RunShellScript" params = {"commands": ["#!/bin/bash\necho 'hello world'"]} response = client.send_command( InstanceIds=["i-123456", "i-234567", "i-345678"], DocumentName=ssm_document, Parameters=params, OutputS3Region="us-east-2", OutputS3BucketName="the-bucket", OutputS3KeyPrefix="pref", ) cmd = response["Command"] cmd_id = cmd["CommandId"] instance_id = "i-345678" invocation_response = client.get_command_invocation( CommandId=cmd_id, InstanceId=instance_id, PluginName="aws:runShellScript" ) invocation_response["CommandId"].should.equal(cmd_id) invocation_response["InstanceId"].should.equal(instance_id) # test the error case for an invalid instance id with assert_raises(ClientError): invocation_response = client.get_command_invocation( CommandId=cmd_id, InstanceId="i-FAKE" ) # test the error case for an invalid plugin name with assert_raises(ClientError): invocation_response = client.get_command_invocation( CommandId=cmd_id, InstanceId=instance_id, PluginName="FAKE" ) @mock_ssm @mock_cloudformation def test_get_command_invocations_from_stack(): stack_template = { "AWSTemplateFormatVersion": "2010-09-09", "Description": "Test Stack", "Resources": { "EC2Instance1": { "Type": "AWS::EC2::Instance", "Properties": { "ImageId": "ami-test-image-id", "KeyName": "test", "InstanceType": "t2.micro", "Tags": [ {"Key": "Test Description", "Value": "Test tag"}, {"Key": "Test Name", "Value": "Name tag for tests"}, ], }, } }, "Outputs": { "test": { "Description": "Test Output", "Value": "Test output value", "Export": {"Name": "Test value to export"}, }, "PublicIP": {"Value": "Test public ip"}, }, } cloudformation_client = boto3.client("cloudformation", region_name="us-east-1") stack_template_str = json.dumps(stack_template) response = cloudformation_client.create_stack( StackName="test_stack", TemplateBody=stack_template_str, Capabilities=("CAPABILITY_IAM",), ) client = boto3.client("ssm", region_name="us-east-1") ssm_document = "AWS-RunShellScript" params = {"commands": ["#!/bin/bash\necho 'hello world'"]} response = client.send_command( Targets=[ {"Key": "tag:aws:cloudformation:stack-name", "Values": ("test_stack",)} ], DocumentName=ssm_document, Parameters=params, OutputS3Region="us-east-2", OutputS3BucketName="the-bucket", OutputS3KeyPrefix="pref", ) cmd = response["Command"] cmd_id = cmd["CommandId"] instance_ids = cmd["InstanceIds"] invocation_response = client.get_command_invocation( CommandId=cmd_id, InstanceId=instance_ids[0], PluginName="aws:runShellScript" )