{
	"AWSTemplateFormatVersion" : "2010-09-09",
    "Description" : "A template containing everything required to replicate secrets between regions",
    "Parameters" : {
		"TargetRegion" : {
		    "Type" : "String",
            "Description" : "The name of the region that you want your secrets replicated to"
		},
        "ReplicaKmsKeyArn" : {
            "Type" : "String",
            "Description" : "The ARN of the KMS key in the replica region used to encrypt the replica. This is only required if the secrets being replicated are configured to use a custom KMS key rather than the default.",
            "Default" : ""
        }
	},
    
    "Resources" : {
		"ReplicatorRole" : {
		    "Type" : "AWS::IAM::Role",
		    "Properties" : {
				"RoleName" : "SecretsManagerRegionReplicatorRole",
                "ManagedPolicyArns" : [
					"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
				],
		        "AssumeRolePolicyDocument" : {
					"Version":"2012-10-17",
					"Statement": [{
						"Effect":"Allow",
						"Principal": {
							"Service":"lambda.amazonaws.com"
						},
						"Action":"sts:AssumeRole"
					}]
				},
                "Policies" : [{
					"PolicyName": "ReplicatorPermissions",
					"PolicyDocument": {
						"Version": "2012-10-17",
						"Statement": [
							{
								"Sid": "KMSPermissions",
								"Effect": "Allow",
								"Action": [
									"kms:Decrypt",
									"kms:Encrypt",
									"kms:GenerateDataKey"
								],
								"Resource": "*"
							},
							{
								"Sid": "SecretsManagerLocalRegion",
								"Effect": "Allow",
								"Action": [
									"secretsmanager:DescribeSecret",
									"secretsmanager:GetSecretValue"
								],
								"Resource": { "Fn::Join" : [ ":", [ "arn:aws:secretsmanager", { "Ref" : "AWS::Region" }, { "Ref" : "AWS::AccountId" }, "secret:*"] ] }
							},
							{
								"Sid": "SecretsManagerRemoteRegion",
								"Effect": "Allow",
								"Action": [
									"secretsmanager:CreateSecret",
									"secretsmanager:UpdateSecretVersionStage",
									"secretsmanager:PutSecretValue",
									"secretsmanager:DescribeSecret"
								],
								"Resource": { "Fn::Join" : [ ":", [ "arn:aws:secretsmanager", { "Ref" : "TargetRegion" }, { "Ref" : "AWS::AccountId" }, "secret:*"] ] }
							}
						]
					}
				}]
		    }
		},

        "ReplicatorLambdaFunction" : {
            "Type" : "AWS::Lambda::Function",
            "Properties" : {
				"FunctionName" : "SecretsManagerRegionReplicator",
                "Description" : "Replicates a secret to another region after a successful rotation",
                "MemorySize" : 128,
                "Timeout" : 30,
                "Handler" : "index.lambda_handler",
                "Role"    : {"Fn::GetAtt" : [ "ReplicatorRole", "Arn"]},
                "Runtime" : "python3.6",
                "Environment" : {
					"Variables" : {
						"TargetRegion": { "Ref" : "TargetRegion" },
						"ReplicaKmsKeyArn": { "Ref" : "ReplicaKmsKeyArn" }
					}
				},
                "Code" : {
					"ZipFile" : {"Fn::Join" : [ "\n", [
						"import boto3",
						"from os import environ",
						"",
						"targetRegion = environ.get('TargetRegion')",
						"if targetRegion == None:",
						"    raise Exception('Environment variable \"TargetRegion\" must be set')",
						"",
						"smSource = boto3.client('secretsmanager')",
						"smTarget = boto3.client('secretsmanager', region_name=targetRegion)",
						"",
						"def lambda_handler(event, context):",
						"    detail = event['detail']",
						"",
						"    print('Retrieving SecretArn from event data')",
						"    secretArn = detail['additionalEventData']['SecretId']",
						"",
						"    print('Retrieving new version of Secret \"{0}\"'.format(secretArn))",
						"    newSecret = smSource.get_secret_value(SecretId = secretArn)",
						"    ",
						"    secretName = newSecret['Name']",
						"    currentVersion = newSecret['VersionId']",
						"",
						"    replicaSecretExists = True",
						"    print('Replicating secret \"{0}\" (Version {1}) to region \"{2}\"'.format(secretName, currentVersion, targetRegion))",
						"    try:",
						"        smTarget.put_secret_value(",
						"            SecretId = secretName,",
						"            ClientRequestToken = currentVersion,",
						"            SecretString = newSecret['SecretString']",
						"        )",
						"        pass",
						"    except smTarget.exceptions.ResourceNotFoundException:",
						"        print('Secret \"{0}\" does not exist in target region \"{1}\". Creating it now with default values'.format(secretName, targetRegion))",
						"        replicaSecretExists = False",
						"    except smTarget.exceptions.ResourceExistsException:",
						"        print('Secret version \"{0}\" has already been created, this must be a duplicate invocation'.format(currentVersion))",
						"        pass",
						"    ",
						"    if replicaSecretExists == False:",
						"        secretMeta = smSource.describe_secret(SecretId = secretArn)",
						"        if secretMeta['KmsKeyId'] != None:",
						"            replicaKmsKeyArn = environ.get('ReplicaKmsKeyArn')",
						"            if replicaKmsKeyArn == None:",
						"                raise Exception('Cannot create replica of a secret that uses a custom KMS key unless the \"ReplicaKmsKeyArn\" environment variable is set. Alternatively, you can also create the key manually in the replica region with the same name')",
						"",
						"            smTarget.create_secret(",
						"                Name = secretName,",
						"                ClientRequestToken = currentVersion,",
						"                KmsKeyId = replicaKmsKeyArn,",
						"                SecretString = newSecret['SecretString'],",
						"                Description = secretMeta['Description']",
						"            )",
						"        else:",
						"            smTarget.create_secret(",
						"                Name = secretName,",
						"                ClientRequestToken = currentVersion,",
						"                SecretString = newSecret['SecretString'],",
						"                Description = secretMeta['Description']",
						"            )",
						"    else:",
						"        secretMeta = smTarget.describe_secret(SecretId = secretName)",
						"        for previousVersion, labelList in secretMeta['VersionIdsToStages'].items():",
						"            if 'AWSCURRENT' in labelList and previousVersion != currentVersion:",
						"                print('Moving \"AWSCURRENT\" label from version \"{0}\" to new version \"{1}\"'.format(previousVersion, currentVersion))",
						"                smTarget.update_secret_version_stage(",
						"                    SecretId = secretName,",
						"                    VersionStage = 'AWSCURRENT',",
						"                    MoveToVersionId = currentVersion,",
						"                    RemoveFromVersionId = previousVersion",
						"                )",
						"                break",
						"",
						"    print('Secret {0} replicated successfully to region \"{1}\"'.format(secretName, targetRegion))"
					] ]}
				}
            }
        },

        "ReplicatorEventTrigger" : {
            "Type" : "AWS::Events::Rule",
            "Properties" : {
				"Name" : "SecretsManagerCrossRegionReplicator",
                "Description" : "Triggers a lambda function to replicate a new version of a secret whenever a secret completes rotation",
                "State" : "ENABLED",
                "Targets" : [{
					"Arn" : {"Fn::GetAtt" : [ "ReplicatorLambdaFunction", "Arn"]},
                    "Id" : { "Fn::Select" : [ 2, { "Fn::Split" : [ "/", { "Ref" : "AWS::StackId" } ] } ] }
				}],
                "EventPattern" : {
					"source": [ "aws.secretsmanager" ],
					"detail-type": [ "AWS Service Event via CloudTrail" ],
					"detail": {
						"eventSource": [ "secretsmanager.amazonaws.com" ],
						"eventName": [ "RotationSucceeded" ]
					}
				}
            }
        },
		
        "ReplicatorLambdaPermission" : {
            "Type" : "AWS::Lambda::Permission",
            "Properties" : {
                "Action" : "lambda:InvokeFunction",
                "FunctionName" : {"Fn::GetAtt" : [ "ReplicatorLambdaFunction", "Arn"]},
                "Principal"    : "events.amazonaws.com",
                "SourceArn" : {"Fn::GetAtt" : [ "ReplicatorEventTrigger", "Arn"]}
            }
        }
	}
}