{"id":22062141,"url":"https://github.com/belodetek/cfn-generic-custom-resource","last_synced_at":"2025-10-13T21:44:53.181Z","repository":{"id":92447836,"uuid":"169480099","full_name":"belodetek/cfn-generic-custom-resource","owner":"belodetek","description":"CloudFormation generic custom resource provider.","archived":false,"fork":false,"pushed_at":"2025-09-06T19:53:42.000Z","size":371,"stargazers_count":28,"open_issues_count":2,"forks_count":6,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-13T21:40:58.932Z","etag":null,"topics":["acm-pca","aws","aws-acm","aws-backup","aws-cloudformation","aws-lambda","boto3","cloudformation","cognito","devops","python3","vpc-peering","vpn-client"],"latest_commit_sha":null,"homepage":"https://anton.belodedenko.me/generic-custom-resource-provider","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/belodetek.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2019-02-06T21:32:09.000Z","updated_at":"2025-09-06T19:53:46.000Z","dependencies_parsed_at":"2024-03-18T22:46:17.998Z","dependency_job_id":"e634005a-dff4-429a-9f81-7d8a74be3bac","html_url":"https://github.com/belodetek/cfn-generic-custom-resource","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/belodetek/cfn-generic-custom-resource","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/belodetek%2Fcfn-generic-custom-resource","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/belodetek%2Fcfn-generic-custom-resource/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/belodetek%2Fcfn-generic-custom-resource/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/belodetek%2Fcfn-generic-custom-resource/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/belodetek","download_url":"https://codeload.github.com/belodetek/cfn-generic-custom-resource/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/belodetek%2Fcfn-generic-custom-resource/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279017053,"owners_count":26085951,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["acm-pca","aws","aws-acm","aws-backup","aws-cloudformation","aws-lambda","boto3","cloudformation","cognito","devops","python3","vpc-peering","vpn-client"],"created_at":"2024-11-30T18:17:33.033Z","updated_at":"2025-10-13T21:44:53.164Z","avatar_url":"https://github.com/belodetek.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cfn-custom-resource-provider [![Build Status](https://github.com/belodetek/cfn-generic-custom-resource/actions/workflows/flowzone.yml/badge.svg)](https://github.com/belodetek/cfn-generic-custom-resource/actions/workflows/flowzone.yml)\n\n\u003e **TL;DR** One *Custom Resource provider* to Rule Them All, inspect the [code](https://github.com/ab77/cfn-generic-custom-resource/blob/master/generic_provider/generic_provider.py), read the [blog](https://anton.belodedenko.me/generic-custom-resource-provider/), try some [examples](http://cloudformation.belodetek.io/#mock-client-requests) and consider [contributing](CONTRIBUTING.md) 🤓\n\n## ToC\n\n### CloudFormation demo stacks\n* [Client VPN](#client-vpn-demo)\n* [Cognito IdP](#cognito-demo)\n* [VPC peering](#vpc-peering-demo)\n* [AWS Backup (EFS)](#aws-backup-efs)\n* [ACM Private CA](#acm-private-ca)\n\n### mock requests\n\n#### client\n* [ACM](#acm)\n* [S3](#s3)\n* [AWS Backup](#backup)\n* [Directory Services](#directory-services)\n* [IAM](#iam)\n* [KMS](#kms)\n* [Relational Database Service](#relational-database-service)\n* [Database Migration Service](#database-migration-service)\n* [EC2](#ec2)\n* [KMS](#kms)\n\n#### resources\n* [resources requests](#mock-resources-requests)\n\n## about\n\u003e a lot of the examples below have been replaced by native CloudFormation functionality\n\nThe idea behind this project was to make available a flexible and simple tool to enable\ncreation of any AWS resource supported by the API. We implement this functionality via a\ngeneric CloudFormation [Custom Resources] provider in Python (3), using [boto3]. The word\n\"generic\" is used here in a sense of having just one Lambda function, which can be used to\ncreate different custom resources by varying the input parameters.\n\nFor more information, please read this blog [post].\n\n## CloudFormation\n\u003e All shell-fu is Bash; `git`, `pip`, `awscli` and `jq` required.\n\n### init\n\n    git clone --recurse-submodules --remote-submodules\\\n      https://github.com/ab77/cfn-generic-custom-resource\\\n      \u0026\u0026 cd cfn-generic-custom-resource\n\n### create bucket\n\u003e 📝 creates a new bucket with a random GUID; ensure `~/.aws/credentials` and\n  `~/.aws/config` are configured (run `aws configure ...`) and export `AWS_PROFILE` and\n  `AWS_REGION` environment variables\n\n    bucket=$(uuid)\n    aws s3 mb s3://${bucket}\n\n#### install requirements (venv)\n\u003e 📝 AWS Lambda provided boto3 library doesn't support Client VPN resources at the time of writing, so we need to package it with the code\n\n    pip install virtualenv --user\n\n    pushd generic_provider\n\n    python -m virtualenv venv\n\n    source venv/bin/activate\n\n    pip install --upgrade pip\n\n    pip install --upgrade -r requirements.txt -t .\n\n    popd\n\n#### compile dependencies\n\n    docker ps \u0026\u0026 pushd generic_provider \u0026\u0026 make \u0026\u0026 popd\n\n### Client VPN demo\n\u003e ☢️ beware of the currently eye-watering Client VPN [pricing](https://aws.amazon.com/vpn/pricing/)\n\n#### certificates\n\u003e 📜 [issue](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/authentication-authrization.html) certificates with [easy-rsa](https://github.com/OpenVPN/easy-rsa) and upload to ACM, using fictional domain `foo.bar`\n\n    domain_name='foo.bar'\n\n    git clone https://github.com/OpenVPN/easy-rsa\n\n    pushd easy-rsa/easyrsa3\n\n    ./easyrsa init-pki\n\n    ./easyrsa build-ca nopass\n\n    ./easyrsa build-server-full server.${domain_name} nopass\\\n      \u0026\u0026 ./easyrsa build-client-full client1.${domain_name} nopass\n\n    popd\n\n    server_certificate=$(aws acm import-certificate\\\n      --certificate file://easy-rsa/easyrsa3/pki/issued/server.${domain_name}.crt\\\n      --private-key file://easy-rsa/easyrsa3/pki/private/server.${domain_name}.key\\\n      --certificate-chain file://easy-rsa/easyrsa3/pki/ca.crt | jq -r '.CertificateArn')\n\n    client_certificate=$(aws acm import-certificate\\\n      --certificate file://easy-rsa/easyrsa3/pki/issued/client1.${domain_name}.crt\\\n      --private-key file://easy-rsa/easyrsa3/pki/private/client1.${domain_name}.key\\\n      --certificate-chain file://easy-rsa/easyrsa3/pki/ca.crt | jq -r '.CertificateArn')\n\n#### package assets\n\u003e 📦 package CloudFormation templates and Lambda function(s) and upload to S3\n\n    pushd client-vpn; for template in lambda main client-vpn; do\n        aws cloudformation package\\\n          --template-file ${template}-template.yaml\\\n          --s3-bucket ${bucket}\\\n          --output-template-file ${template}.yaml\n    done; popd\n\n#### deploy stack\n\u003e 📝  creates Client VPN endpoint with `certificate-authentication`; for `directory-service-authentication` or both, specify additional `DirectoryId` parameter\n\n    stack_name='client-vpn-demo'\n    vpc_id=$(aws ec2 describe-vpcs | jq -r .Vpcs[0].VpcId)\n    subnets=(\n        $(aws ec2 describe-subnets | jq -r \".Subnets[0] | select(.VpcId==\\\"${vpc_id}\\\").SubnetId\")\n        $(aws ec2 describe-subnets | jq -r \".Subnets[1] | select(.VpcId==\\\"${vpc_id}\\\").SubnetId\")\n    )\n    subnet_count=${#subnets[@]}\n    cidr=172.16.0.0/22\n\n\n    pushd client-vpn; aws cloudformation deploy\\\n      --template-file main.yaml\\\n      --stack-name ${stack_name}\\\n      --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM\\\n      --parameter-overrides\\\n      VpcId=${vpc_id}\\\n      CidrBlock=${cidr}\\\n      SubnetIds=$(echo ${subnets[*]} | tr ' ' ',')\\\n      SubnetCount=${subnet_count}\\\n      ServerCertificateArn=${server_certificate}\\\n      ClientRootCertificateChainArn=${client_certificate}\\\n      --tags\\\n      Name=${stack_name}\\\n      Region=${AWS_REGION}\\\n      Profile=${AWS_PROFILE}\\\n      AccountId=$(aws sts get-caller-identity | jq -r '.Account'); popd\n\n#### download profile\n\n    vpn_stack=$(aws cloudformation list-exports\\\n      | jq -r \".Exports[] | select(.Name==\\\"VPNStackName-${stack_name}\\\").Value\")\n\n    client_vpn_endpoint=$(aws cloudformation list-exports\\\n      | jq -r \".Exports[] | select(.Name | startswith(\\\"ClientVpnEndpointId-${vpn_stack}\\\")).Value\")\n\n    aws ec2 export-client-vpn-client-configuration\\\n      --client-vpn-endpoint-id ${client_vpn_endpoint} | jq -r '.ClientConfiguration' \u003e client.ovpn\n\n#### connect\n* [macOS](https://tunnelblick.net/downloads.html)\n* [Windows/Linux](https://openvpn.net/community-downloads/)\n\n### Cognito demo\n\u003e 📝 make sure to [create bucket](#create-bucket) and [install requirements](#install-requirements) first\n\n#### update bucket policy\n\u003e ⚠️ public read access required for access to `MetadataURL`, adjust as necessary\n\n```\ntmpfile=$(mktemp)\ncat \u003c\u003c EOF \u003e ${tmpfile}\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"$(date +%s)\",\n      \"Effect\": \"Allow\",\n      \"Principal\": \"*\",\n      \"Action\": [\n        \"s3:GetObject\"\n      ],\n      \"Resource\": [\n        \"arn:aws:s3:::${bucket}/*\"\n      ]\n    }\n  ]\n}\nEOF\n\naws s3api put-bucket-policy\\\n  --bucket ${bucket}\\\n  --policy file://${tmpfile}\\\n  \u0026\u0026 rm ${tmpfile}\n```\n\n#### download metadata\n* login to [Google Apps Admin](https://admin.google.com)\n* navigate to `Apps -\u003e SAML Apps --\u003e + --\u003e SETUP MY OWN CUSTOM APP`\n* select `(Option 2) IDP metadata`, download and save\n\n#### copy metadata\n\n    domain_name='foo.bar'\n\n    aws s3 cp GoogleIDPMetadata-${domain_name}.xml s3://${bucket}/\n\n#### package assets\n\n    pushd cognito-idp; for template in lambda main cognito; do\n        aws cloudformation package\\\n          --template-file ${template}-template.yaml\\\n          --s3-bucket ${bucket}\\\n          --output-template-file ${template}.yaml\n    done; popd\n\n#### deploy stack\n\n    stack_name='c0gn1t0-demo'\n    metadata_url=https://${bucket}.s3.amazonaws.com/GoogleIDPMetadata-${domain_name}.xml\n\n    pushd cognito-idp; aws cloudformation deploy\\\n      --template-file main.yaml\\\n      --stack-name ${stack_name}\\\n      --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM\\\n      --parameter-overrides\\\n      DomainName=${domain_name}\\\n      MetadataURL=${metadata_url}\\\n      --tags\\\n      Name=${stack_name}\\\n      Region=${AWS_REGION}\\\n      Profile=${AWS_PROFILE}\\\n      AccountId=$(aws sts get-caller-identity | jq -r '.Account'); popd\n\n\n    cognito_stack=$(aws cloudformation list-exports\\\n      | jq -r \".Exports[] | select(.Name==\\\"CognitoStackName-${stack_name}\\\").Value\")\n\n    user_pool_id=$(aws cloudformation list-exports\\\n      | jq -r \".Exports[] | select(.Name | startswith(\\\"UserPoolId-${cognito_stack}\\\")).Value\")\n\n\n    echo \"ACS URL: https://${stack_name}.auth.${AWS_REGION}.amazoncognito.com/saml2/idpresponse\"\n    echo \"Entity ID: urn:amazon:cognito:sp:${user_pool_id}\"\n\n#### configure G Suite\n\u003e [Cognito IdP](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-configuring-federation-with-saml-2-0-idp.html) with [Google SAML](https://support.google.com/a/answer/6087519?hl=en)\n\n* login to [Google Apps Admin](https://admin.google.com)\n* navigate to `Apps -\u003e SAML Apps --\u003e + --\u003e SETUP MY OWN CUSTOM APP`\n* set `ACS URL` as per above\n* set `Entity ID` as per above\n* continue with [ALB configuration](https://aws.amazon.com/blogs/aws/built-in-authentication-in-alb/)\n\n### VPC peering demo\n\u003e creates a peering connection between source and destination VPCs, including tags and routes in both directions\n\n#### package assets\n\n    pushd vpc-peering; for template in lambda main; do\n        aws cloudformation package\\\n          --template-file ${template}-template.yaml\\\n          --s3-bucket ${bucket}\\\n          --output-template-file ${template}.yaml\n    done; popd\n\n#### create IAM role\n\u003e ☢ ensure appropriate [VPCPeeringRole](lambda-template.yaml#L12) exists in the VPC accepter AWS account and review IAM role permissions\n\n      VPCPeeringRole:\n        Type: 'AWS::IAM::Role'\n        Properties:\n          RoleName: 'VPCPeeringRole'\n          AssumeRolePolicyDocument:\n            Version: '2012-10-17'\n            Statement:\n            - Effect: Allow\n              Principal:\n                AWS:\n                # list your VPC peering requester (source) AWS accounts here\n                - '123456789000'\n                ...\n              Action: sts:AssumeRole\n          Path: '/'\n          ...\n\n#### update IAM role\n\u003e ☢ add VPC requester AWS accounts to [CustomResourceLambdaRole](lambda-template.yaml#L94) under the `AmazonSTSPolicy` policy and review IAM role permissions\n\n      - PolicyName: AmazonSTSPolicy\n        PolicyDocument:\n          Version: '2012-10-17'\n          Statement:\n          - Effect: Allow\n            Action:\n            - 'sts:AssumeRole'\n            - 'sts:PassRole'\n            Resource:\n            # list your VPC peering accepter (target) AWS accounts here\n            - !Sub 'arn:${AWS::Partition}:iam::123456789001:role/VPCPeeringRole'\n            ...\n\n#### deploy stack\n\u003e 📝 optionally enable EC2 nested stack and supply `SecurityGroup` in the accepter VPC as well as `TargetPort`\n\n    # peering between VPCs in this mock account 123456789000 (requester) and 123456789001 (accepter)\n    stack_name='vpc-peering-demo'\n\n    # create IPv6 routes (both VPCs must be IPv6)\n    ipv6='false'\n\n    # requester VPC\n    source_vpc='vpc-abcdef1234567890'\n\n    # comma separated list of one or more route table id(s) in the requester VPC'\n    source_route_table_ids='rtb-abcdef1234567890'\n\n    # accepter VPC\n    source_vpc='vpc-1234567890abcdef'\n\n    # VPC accepter AWS account\n    target_account_id=123456789001\n\n    # VPC accepter AWS region\n    target_region=${AWS_REGION}\n\n    # comma separated list of one or more route table id(s) in the accepter VPC'\n    target_route_table_ids='rtb-1234567890abcdef'\n\n\n    source_route_table_ids=($(echo ${source_route_table_ids} | sed 's/,/ /g' | tr ' ' '\\n'))\\\n      \u0026\u0026 source_route_tables=${#source_route_table_ids[@]}\\\n      \u0026\u0026 source_route_table_ids=\"$(echo ${source_route_table_ids[*]} | tr ' ' ',')\"\n\n    target_route_table_ids=($(echo ${target_route_table_ids} | sed 's/,/ /g' | tr ' ' '\\n'))\\\n      \u0026\u0026 target_route_tables=${#target_route_table_ids[@]}\\\n      \u0026\u0026 target_route_table_ids=\"$(echo ${target_route_table_ids[*]} | tr ' ' ',')\"\n\n    pushd vpc-peering; aws cloudformation deploy\\\n      --template-file main.yaml\\\n      --stack-name ${stack_name}\\\n      --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM\\\n      --parameter-overrides\\\n      SourceVpcId=${source_vpc}\\\n      SourceRouteTableIds=${source_route_table_ids}\\\n      SourceRouteTables=${source_route_tables}\\\n      TargetRegion=${target_region}\\\n      TargetAccountId=${target_account_id}\\\n      TargetVpcId=${target_vpc}\\\n      TargetRouteTableIds=${target_route_table_ids}\\\n      TargetRouteTables=${target_route_tables}\\\n      EC2Template=false\\\n      --tags\\\n      Name=${stack_name}\\\n      Region=${AWS_REGION}\\\n      Profile=${AWS_PROFILE}\\\n      AccountId=$(aws sts get-caller-identity | jq -r '.Account'); popd\n\n### AWS Backup (EFS)\n\u003e also see mock [examples](#backup) below\n\n#### package assets\n\n    pushd aws-backup; for template in lambda; do\n        aws cloudformation package\\\n          --template-file ${template}-template.yaml\\\n          --s3-bucket ${bucket}\\\n          --output-template-file ${template}.yaml\n    done; popd\n\n#### deploy stack\n\u003e see [resource ARNs and namespaces](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) for `ResourceId` parameter\n\n    stack_name='aws-backup-demo'\n\n    # specify resource ARN and name\n    resource_id=\"arn:aws:elasticfilesystem:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):file-system/fs-abcde1234\"\n    resource_name='efs-resource-name'\n\n\n    pushd aws-backup; aws cloudformation deploy\\\n      --template-file backup.yaml\\\n      --stack-name ${stack_name}\\\n      --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM\\\n      --parameter-overrides\\\n      NameTag=${stack_name}\\\n      ResourceId=${resource_id}\\\n      ResourceName=${resource_name}\\\n      --tags\\\n      Name=${stack_name}\\\n      Region=${AWS_REGION}\\\n      Profile=${AWS_PROFILE}\\\n      AccountId=$(aws sts get-caller-identity | jq -r '.Account'); popd\n\n### ACM Private CA\n\u003e also see mock [examples](#acm-pca) below\n\n#### package assets\n\n    pushd acm-pca; for template in lambda main; do\n        aws cloudformation package\\\n          --template-file ${template}-template.yaml\\\n          --s3-bucket ${bucket}\\\n          --output-template-file ${template}.yaml\n    done; popd\n\n#### deploy stack\n\u003e ⚠️ ensure to clean-up the CA resources to avoid $400/month surprise on your next AWS bill\n\n    stack_name='acm-pca'\n\n    # change to a domain you own with postmaster and hostmaster email addresses forwarded for ACM SSL certificate validation\n    domain_name='belodetek.io'\n\n\n    pushd acm-pca; aws cloudformation deploy\\\n      --template-file main.yaml\\\n      --stack-name ${stack_name}\\\n      --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM\\\n      --parameter-overrides\\\n      NameTag=${stack_name}\\\n      DomainWithoutDot=${domain_name}\\\n      S3Template=true\\\n      IAMTemplate=true\\\n      R53Template=true\\\n      ACMTemplate=true\\\n      CFTemplate=true\\\n      IAMTemplate=true\\\n      PCATemplate=true\\\n      --tags\\\n      Name=${stack_name}\\\n      Region=${AWS_REGION}\\\n      Profile=${AWS_PROFILE}\\\n      AccountId=$(aws sts get-caller-identity | jq -r '.Account'); popd\n\n## mock client requests\n\u003e 🐞 useful to debug resource creation of AWS resources from a local workstation\n\n### ACM\n\u003e [ACM](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/acm.html) API reference\n\n#### request_certificate\n\u003e mock CloudFormation request to [request_certificate](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/acm.html#ACM.Client.request_certificate) in a different account and/or region (e.g. for CloudFront)\n\n    # https://forums.aws.amazon.com/thread.jspa?messageID=912980\n    aws_region='us-east-1'\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"PhysicalResourceId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"acm\\\",\n          \\\"AgentRegion\\\": \\\"${aws_region}\\\",\n          \\\"AgentCreateMethod\\\": \\\"request_certificate\\\",\n          \\\"AgentUpdateMethod\\\": \\\"update_certificate_options\\\",\n          \\\"AgentDeleteMethod\\\": \\\"delete_certificate\\\",\n          \\\"AgentWaitMethod\\\": \\\"certificate_validated\\\",\n          \\\"AgentWaitQueryExpr\\\": \\\"$.CertificateArn\\\",\n          \\\"AgentWaitResourceId\\\": \\\"CertificateArn\\\",\n          \\\"AgentResourceId\\\": \\\"CertificateArn\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"DomainName\\\": \\\"foo.baz.com\\\",\n              \\\"ValidationMethod\\\": \\\"EMAIL\\\",\n              \\\"SubjectAlternativeNames\\\": [\n                  \\\"bar.baz.com\\\"\n              ],\n              \\\"DomainValidationOptions\\\": [\n                  {\n                      \\\"DomainName\\\": \\\"foo.baz.com\\\",\n                      \\\"ValidationDomain\\\": \\\"baz.com\\\"\n                  },\n                  {\n                      \\\"DomainName\\\": \\\"bar.baz.com\\\",\n                      \\\"ValidationDomain\\\": \\\"baz.com\\\"\n                  }\n              ]\n          },\n          \\\"AgentUpdateArgs\\\": {\n              \\\"Options\\\": {\n                  \\\"CertificateTransparencyLoggingPreference\\\": \\\"DISABLED\\\"\n              }\n          }\n      }\n    }\" | jq -c | VERBOSE=1 ./generic_provider.py\n    popd\n\n### ACM-PCA\n\u003e [ACM-PCA](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/acm-pca.html) API reference\n\n#### create_certificate_authority\n\u003e mock CloudFormation request to [create_certificate_authority](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/acm-pca.html#ACMPCA.Client.create_certificate_authority)\n\n(⚠️ ensure to clean-up the CA resources to avoid $400/month surprise on your next AWS bill)\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"PhysicalResourceId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"acm-pca\\\",\n          \\\"AgentCreateMethod\\\": \\\"create_certificate_authority\\\",\n          \\\"AgentUpdateMethod\\\": \\\"update_certificate_authority\\\",\n          \\\"AgentDeleteMethod\\\": \\\"delete_certificate_authority\\\",\n          \\\"AgentWaitQueryExpr\\\": \\\"$.CertificateAuthorityArn\\\",\n          \\\"AgentWaitResourceId\\\": \\\"CertificateAuthorityArn\\\",\n          \\\"AgentWaitDeleteExceptions\\\": [\n            \\\"botocore.exceptions.WaiterError\\\"\n          ],\n          \\\"AgentResourceId\\\": \\\"CertificateAuthorityArn\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"CertificateAuthorityConfiguration\\\": {\n                  \\\"KeyAlgorithm\\\": \\\"RSA_2048\\\",\n                  \\\"SigningAlgorithm\\\": \\\"SHA256WITHRSA\\\",\n                  \\\"Subject\\\": {\n                      \\\"Country\\\": \\\"FB\\\",\n                      \\\"Organization\\\": \\\"foo-bar\\\",\n                      \\\"OrganizationalUnit\\\": \\\"foo-bar\\\",\n                      \\\"CommonName\\\": \\\"foo@bar.com\\\"\n                  }\n              },\n              \\\"RevocationConfiguration\\\": {\n                  \\\"CrlConfiguration\\\": {\n                      \\\"Enabled\\\": true,\n                      \\\"ExpirationInDays\\\": 7,\n                      \\\"CustomCname\\\": \\\"foo.bar.com\\\",\n                      \\\"S3BucketName\\\": \\\"foo-bar\\\"\n                  }\n              },\n              \\\"CertificateAuthorityType\\\": \\\"SUBORDINATE\\\",\n              \\\"Tags\\\": [\n                  {\n                      \\\"Key\\\": \\\"Name\\\",\n                      \\\"Value\\\": \\\"foo-bar\\\"\n\n                  }\n              ]\n          },\n          \\\"AgentUpdateArgs\\\": {\n              \\\"RevocationConfiguration\\\": {\n                  \\\"CrlConfiguration\\\": {\n                      \\\"Enabled\\\": true,\n                      \\\"ExpirationInDays\\\": 7,\n                      \\\"CustomCname\\\": \\\"foo.bar.com\\\"\n                  }\n              },\n              \\\"Status\\\": \\\"ACTIVE\\\"\n          },\n          \\\"AgentDeleteArgs\\\": {\n              \\\"PermanentDeletionTimeInDays\\\": 7\n          }\n      }\n    }\" | jq -c | VERBOSE=1 ./generic_provider.py\n    popd\n\n#### create_self_signed_cert\n\u003e mock CloudFormation request to [create_self_signed_cert](https://github.com/ab77/cfn-generic-custom-resource/blob/master/generic_provider/acm_pca.py)\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"PhysicalResourceId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"custom\\\",\n          \\\"AgentService\\\": \\\"acm_pca\\\",\n          \\\"AgentCreateMethod\\\": \\\"create_self_signed_cert\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"PrivateKey\\\": \\\"/rsa-private-keys/acm-pca/key_pair\\\",\n              \\\"Country\\\": \\\"US\\\",\n              \\\"Org\\\": \\\"foo\\\",\n              \\\"OrgUnit\\\": \\\"bar\\\",\n              \\\"CommonName\\\": \\\"foo-bar\\\",\n              \\\"Serial\\\": 1,\n              \\\"ValidityInSeconds\\\": 315360000,\n              \\\"Digest\\\": \\\"sha256\\\"\n          }\n      }\n    }\" | jq -c | VERBOSE=1 ./generic_provider.py | jq -r .Data.Certificate \u003e ca.crt\\\n    \u0026\u0026 openssl x509 -in ca.crt -text -noout\n    popd\n\n#### get_certificate_authority_csr\n\u003e mock CloudFormation request to [get_certificate_authority_csr](https://github.com/ab77/cfn-generic-custom-resource/blob/master/generic_provider/acm_pca.py)\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"PhysicalResourceId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"acm-pca\\\",\n          \\\"AgentCreateMethod\\\": \\\"get_certificate_authority_csr\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"CertificateAuthorityArn\\\": \\\"arn:aws:acm-pca:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):certificate-authority/$(uuid)\\\",\n          }\n        }\n      }\" | jq -c | VERBOSE=1 ./generic_provider.py | jq -r .Data.Csr \u003e csr.pem \u0026\u0026 openssl req -in csr.pem -text -noout\n      popd\n\n#### sign_csr\n\u003e mock CloudFormation request to [sign_csr](https://github.com/ab77/cfn-generic-custom-resource/blob/master/generic_provider/acm_pca.py) request\n\n    # upload RSA private (signing) key to the SSM Parameter Store\n    openssl genrsa -out signing.key 4096 \u0026\u0026 signing_key=$(cat signing.key)\n\n    aws ssm put-parameter --type SecureString\\\n      --name '/rsa-private-keys/acm-pca/key_pair'\\\n      --value ${signing_key}\n\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"PhysicalResourceId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"custom\\\",\n          \\\"AgentService\\\": \\\"acm_pca\\\",\n          \\\"AgentCreateMethod\\\": \\\"sign_csr\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"PrivateKey\\\": \\\"/rsa-private-keys/acm-pca/key_pair\",\n              \\\"Csr\\\": \\\"$(cat csr.pem | base64)\\\",\n              \\\"ValidityInSeconds\\\": 315360000,\n              \\\"Digest\\\": \\\"sha256\\\"\n          }\n      }\n    }\" | jq -c | VERBOSE=1 ./generic_provider.py \u003e test.crt \u0026\u0026 openssl x509 -in test.crt -text -noout\n    popd\n\n#### import_certificate_authority_certificate\n\u003e mock CloudFormation request to [import_certificate_authority_certificate](https://github.com/ab77/cfn-generic-custom-resource/blob/master/generic_provider/acm_pca.py)\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"PhysicalResourceId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"custom\\\",\n          \\\"AgentService\\\": \\\"acm_pca\\\",\n          \\\"AgentCreateMethod\\\": \\\"import_certificate_authority_certificate\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"CertificateAuthorityArn\\\": \\\"arn:aws:acm-pca:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):certificate-authority/$(uuid)\\\",\n              \\\"Certificate\\\": \\\"$(cat test.crt | base64)\\\"\n              \\\"CACertificate\\\": \\\"$(cat csr.pem | base64)\\\"\n          }\n      }\n    }\" | jq -c | VERBOSE=1 ./generic_provider.py\n    popd\n\n### S3\n\u003e [S3](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html) API reference\n\n#### put-bucket-notification-configuration\n\u003e mock CloudFormation request to [add](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.put_bucket_notification_configuration) a bucket Lambda notification configuration\n\n    bucket='foo'\n\n    lambda_function='bar'\n\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"PhysicalResourceId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"s3\\\",\n          \\\"AgentCreateMethod\\\": \\\"put_bucket_notification_configuration\\\",\n          \\\"AgentUpdateMethod\\\": \\\"put_bucket_notification_configuration\\\",\n          \\\"AgentDeleteMethod\\\": \\\"put_bucket_notification_configuration\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"Bucket\\\": \\\"${bucket}\\\",\n              \\\"NotificationConfiguration\\\": {\n                  \\\"LambdaFunctionConfigurations\\\": [{\n                      \\\"LambdaFunctionArn\\\": \\\"arn:aws:lambda:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):function:${lambda_function}\\\",\n                      \\\"Events\\\": [\n                          \\\"s3:ObjectRemoved:*\\\"\n                      ],\n                      \\\"Filter\\\": {\n                          \\\"Key\\\": {\n                              \\\"FilterRules\\\": [\n                                  {\n                                      \\\"Name\\\": \\\"prefix\\\",\n                                      \\\"Value\\\": \\\"foo/\\\"\n                                  },\n                                  {\n                                      \\\"Name\\\": \\\"suffix\\\",\n                                      \\\"Value\\\": \\\".bar\\\"\n                                  }\n                              ]\n                          }\n                      }\n                  }]\n              }\n          },\n          \\\"AgentUpdateArgs\\\": {\n              \\\"Bucket\\\": \\\"${bucket}\\\",\n              \\\"NotificationConfiguration\\\": {\n                  \\\"LambdaFunctionConfigurations\\\": [{\n                      \\\"LambdaFunctionArn\\\": \\\"arn:aws:lambda:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):function:${lambda_function}\\\",\n                      \\\"Events\\\": [\n                          \\\"s3:ObjectRemoved:*\\\"\n                      ],\n                      \\\"Filter\\\": {\n                          \\\"Key\\\": {\n                              \\\"FilterRules\\\": [\n                                  {\n                                      \\\"Name\\\": \\\"prefix\\\",\n                                      \\\"Value\\\": \\\"foo/\\\"\n                                  },\n                                  {\n                                      \\\"Name\\\": \\\"suffix\\\",\n                                      \\\"Value\\\": \\\".bar\\\"\n                                  }\n                              ]\n                          }\n                      }\n                  }]\n              }\n          },\n          \\\"AgentDeleteArgs\\\": {\n              \\\"Bucket\\\": \\\"${bucket}\\\",\n              \\\"NotificationConfiguration\\\": {}\n          }\n      }\n    }\" | jq -c | VERBOSE=1 ./generic_provider.py\n    popd\n\n#### put_bucket_replication\n\u003e mock CloudFormation request to [add](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/put_bucket_replication.html) a bucket replication rule\n  .. also see [bucket replication rules](examples/s3/bucket-replication-rule.yml) template\n\n     pushd generic_provider\n     aws_account_id=$(aws sts get-caller-identity | jq -r '.Account')\n\n     echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:${aws_account_id}:stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"ResourceProperties\\\": {\n        \\\"AgentService\\\": \\\"s3\\\",\n        \\\"AgentType\\\": \\\"client\\\",\n        \\\"AgentCreateMethod\\\": \\\"put_bucket_replication\\\",\n        \\\"AgentCreateArgs\\\": {\n          \\\"Bucket\\\": \\\"foo\\\",\n          \\\"ReplicationConfiguration\\\": {\n            \\\"Role\\\": \\\"arn:aws:iam::${aws_account_id}:role/bucket-replication-role\\\",\n            \\\"Rules\\\": [\n              {\n                \\\"Status\\\": \\\"Enabled\\\",\n                \\\"SourceSelectionCriteria\\\": {\n                  \\\"SseKmsEncryptedObjects\\\": {\n                    \\\"Status\\\": \\\"Enabled\\\"\n                  }\n                },\n                \\\"Prefix\\\": \\\"\\\",\n                \\\"Destination\\\": {\n                  \\\"Bucket\\\": \\\"arn:aws:s3:::bar\\\",\n                  \\\"StorageClass\\\": \\\"StorageClass\\\",\n                  \\\"EncryptionConfiguration\\\": {\n                    \\\"ReplicaKmsKeyID\\\": \\\"arn:aws:kms:eu-central-1:${aws_account_id}:alias/aws/s3\\\"\n                  }\n                }\n              }\n            ]\n          }\n        }\n      }\n    }\" | jq -c | ./generic_provider.py\n    popd\n\n### Backup\n\u003e [Backup](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/backup.html) API reference\n\n#### create-backup-vault\n\u003e mock CloudFormation request to [create](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/backup.html#Backup.Client.create_backup_vault) a backup vault\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"PhysicalResourceId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"backup\\\",\n          \\\"AgentCreateMethod\\\": \\\"create_backup_vault\\\",\n          \\\"AgentDeleteMethod\\\": \\\"delete_backup_vault\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"BackupVaultName\\\": \\\"foo-bar\\\",\n              \\\"EncryptionKeyArn\\\": \\\"arn:aws:kms:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):key/$(uuid)\\\",\n              \\\"BackupVaultTags\\\": {\n                \\\"Name\\\": \\\"foo-bar\\\"\n              }\n          },\n          \\\"AgentDeleteArgs\\\": {\n              \\\"BackupVaultName\\\": \\\"foo-bar\\\"\n          }\n      }\n    }\" | jq -c | VERBOSE=1 ./generic_provider.py\n    popd\n\n#### create-backup-plan\n\u003e mock CloudFormation request to [create](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/backup.html#Backup.Client.create_backup_plan) a backup plan\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"backup\\\",\n          \\\"AgentCreateMethod\\\": \\\"create_backup_plan\\\",\n          \\\"AgentUpdateMethod\\\": \\\"update_backup_plan\\\",\n          \\\"AgentDeleteMethod\\\": \\\"delete_backup_plan\\\",\n          \\\"AgentResourceId\\\": \\\"BackupPlanId\\\",\n          \\\"AgentWaitQueryExpr\\\": \\\"$.BackupPlanId\\\",\n          \\\"AgentCreateArgs\\\": {\n            \\\"BackupPlan\\\": {\n              \\\"BackupPlanName\\\": \\\"foo-bar\\\",\n              \\\"Rules\\\": [\n                {\n                  \\\"RuleName\\\": \\\"foo-bar\\\",\n                  \\\"TargetBackupVaultName\\\": \\\"Default\\\",\n                  \\\"ScheduleExpression\\\": \\\"cron(0 2 * * ? *)\\\",\n                  \\\"StartWindowMinutes\\\": 60,\n                  \\\"CompletionWindowMinutes\\\": 180,\n                  \\\"Lifecycle\\\": {\n                    \\\"MoveToColdStorageAfterDays\\\": 30,\n                    \\\"DeleteAfterDays\\\": 365\n                  }\n                }\n              ]\n            },\n            \\\"BackupPlanTags\\\": {\n              \\\"Name\\\": \\\"foo-bar\\\"\n            }\n          },\n          \\\"AgentUpdateArgs\\\": {\n            \\\"BackupPlan\\\": {\n              \\\"BackupPlanName\\\": \\\"foo-bar\\\",\n              \\\"Rules\\\": [\n                {\n                  \\\"RuleName\\\": \\\"foo-bar\\\",\n                  \\\"TargetBackupVaultName\\\": \\\"Default\\\",\n                  \\\"ScheduleExpression\\\": \\\"cron(0 2 * * ? *)\\\",\n                  \\\"StartWindowMinutes\\\": 60,\n                  \\\"CompletionWindowMinutes\\\": 180,\n                  \\\"Lifecycle\\\": {\n                    \\\"MoveToColdStorageAfterDays\\\": 30,\n                    \\\"DeleteAfterDays\\\": 365\n                  }\n                }\n              ]\n            }\n          }\n        }\n      }\n    }\" | jq -c | VERBOSE=1 ./generic_provider.py\n    popd\n\n#### create-backup-selection\n\u003e mock CloudFormation request to [create](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/backup.html#Backup.Client.create_backup_selection) a backup slection\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"backup\\\",\n          \\\"AgentCreateMethod\\\": \\\"create_backup_selection\\\",\n          \\\"AgentDeleteMethod\\\": \\\"delete_backup_selection\\\",\n          \\\"AgentResourceId\\\": \\\"SelectionId\\\",\n          \\\"AgentWaitQueryExpr\\\": \\\"$.SelectionId\\\",\n          \\\"AgentCreateArgs\\\": {\n            \\\"BackupPlanId\\\": \\\"$(uuid)\\\",\n            \\\"BackupSelection\\\": {\n              \\\"SelectionName\\\": \\\"foo-bar\\\",\n              \\\"IamRoleArn\\\": \\\"arn:aws:iam::$(aws sts get-caller-identity | jq -r '.Account'):role/service-role/AWSBackupDefaultServiceRole\\\",\n              \\\"Resources\\\": [\n                \\\"arn:aws:elasticfilesystem:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):file-system/fs-abcde1234\\\"\n              ],\n              \\\"ListOfTags\\\": [\n                {\n                  \\\"ConditionType\\\": \\\"STRINGEQUALS\\\",\n                  \\\"ConditionKey\\\": \\\"AccountId\\\",\n                  \\\"ConditionValue\\\": \\\"$(aws sts get-caller-identity | jq -r '.Account')\\\"\n                }\n              ]\n            }\n          },\n          \\\"AgentDeleteArgs\\\": {\n            \\\"BackupPlanId\\\": \\\"$(uuid)\\\"\n          }\n        }\n      }\n    }\" | jq -c | VERBOSE=1 ./generic_provider.py\n    popd\n\n### Directory Services\n\u003e [Directory Services](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ds.html) API reference\n\n#### AD Connector\n\u003e mock CloudFormation request to create [AD Connector](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/directory_ad_connector.html)\n\n    mock_lambda_event=$(echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"ResourceProperties\\\": {\n        \\\"AgentService\\\": \\\"ds\\\",\n        \\\"AgentType\\\": \\\"client\\\",\n        \\\"AgentCreateMethod\\\": \\\"connect_directory\\\",\n        \\\"AgentDeleteMethod\\\": \\\"delete_directory\\\",\n        \\\"AgentWaitMethod\\\": \\\"describe_directories\\\",\n        \\\"AgentWaitQueryExpr\\\": \\\"$.DirectoryDescriptions[].Stage\\\",\n        \\\"AgentWaitCreateQueryValues\\\": [\n            \\\"Active\\\"\n        ],\n        \\\"AgentWaitUpdateQueryValues\\\": [],\n        \\\"AgentWaitDeleteQueryValues\\\": [],\n        \\\"AgentResourceId\\\": \\\"DirectoryId\\\",\n        \\\"AgentWaitResourceId\\\": [\n          \\\"DirectoryIds\\\"\n        ],\n        \\\"AgentCreateArgs\\\": {\n          \\\"Size\\\": \\\"Small\\\",\n          \\\"Description\\\": \\\"Active Directory connection.\\\",\n          \\\"Name\\\": \\\"foo-bar.local\\\",\n          \\\"ShortName\\\": \\\"foo-bar\\\",\n          \\\"Password\\\": \\\"bar\\\",\n          \\\"ConnectSettings\\\": {\n            \\\"VpcId\\\": \\\"vpc-abcdef1234567890\\\",\n            \\\"SubnetIds\\\": [\n              \\\"subnet-1234567890abcdef\\\",\n              \\\"subnet-abcdef1234567890\\\"\n            ],\n            \\\"CustomerDnsIps\\\": [\n              \\\"1.2.3.4\\\",\n              \\\"4.5.6.7\\\"\n            ],\n            \\\"CustomerUserName\\\": \\\"foo\\\"\n          }\n        }\n      }\n    }\" | jq -c)\\\n    \u0026\u0026 pushd generic_provider\\\n    \u0026\u0026 ./generic_provider.py \"${mock_lambda_event}\"\\\n    \u0026\u0026 popd\n\n### IAM\n\u003e [IAM](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iam.html) API reference\n\n#### SSH public key\n\u003e mock CloudFormation request to [upload](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iam.html#IAM.Client.upload_ssh_public_key) SSH public key\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"iam\\\",\n          \\\"AgentResourceId\\\": \\\"SSHPublicKeyId\\\",\n          \\\"AgentWaitQueryExpr\\\": \\\"$.SSHPublicKey.SSHPublicKeyId\\\",\n          \\\"AgentCreateMethod\\\": \\\"upload_ssh_public_key\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"UserName\\\": \\\"foo-bar\\\",\n              \\\"SSHPublicKeyBody\\\": \\\"$(cat ~/.ssh/id_rsa.pub | head -n 1)\\\"\n          },\n          \\\"AgentDeleteMethod\\\": \\\"delete_ssh_public_key\\\",\n          \\\"AgentDeleteArgs\\\": {\n            \\\"UserName\\\": \\\"foo-bar\\\"\n          }\n      }\n    }\" | jq -c | ./generic_provider.py\n    popd\n\n### KMS\n\u003e [KMS](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kms.html) API reference\n\n#### encrypt\n\u003e mock CloudFormation request to [encrypt](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kms.html#KMS.Client.encrypt) with KMS\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentCreateArgs\\\": {\n              \\\"KeyId\\\": \\\"arn:aws:kms:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):key/$(uuid)\\\",\n              \\\"Plaintext\\\": \\\"foo-bar\\\"\n          },\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"kms\\\",\n          \\\"AgentCreateMethod\\\": \\\"encrypt\\\"\n      }\n    }\" | jq -c | ./generic_provider.py\n    popd\n\n### Relational Database Service\n\u003e [RDS](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rds.html) API reference\n\n#### modify-db-cluster\n\u003e mock CloudFormation request to [enable](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rds.html#RDS.Client.modify_db_cluster) RDS CloudWatch metrics\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"PhysicalResourceId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceProperties\\\": {\n        \\\"AgentService\\\": \\\"rds\\\",\n        \\\"AgentType\\\": \\\"client\\\",\n        \\\"AgentCreateMethod\\\": \\\"modify_db_cluster\\\",\n        \\\"AgentCreateArgs\\\": {\n          \\\"DBClusterIdentifier\\\": \\\"foo-bar\\\",\n          \\\"CloudwatchLogsExportConfiguration\\\": {\n            \\\"EnableLogTypes\\\": [\n              \\\"error\\\",\n              \\\"slowquery\\\"\n            ],\n            \\\"DisableLogTypes\\\": []\n          }\n        },\n        \\\"AgentDeleteMethod\\\": \\\"modify_db_cluster\\\",\n        \\\"AgentDeleteArgs\\\": {\n          \\\"DBClusterIdentifier\\\": \\\"foo-bar\\\",\n          \\\"CloudwatchLogsExportConfiguration\\\": {\n            \\\"DisableLogTypes\\\": [\n              \\\"error\\\",\n              \\\"slowquery\\\"\n            ],\n            \\\"EnableLogTypes\\\": []\n          }\n        },\n        \\\"AgentWaitMethod\\\": \\\"describe_db_instances\\\",\n        \\\"AgentWaitDelay\\\": \\\"60\\\",\n        \\\"AgentWaitArgs\\\": {\n          \\\"Filters\\\": [\n            {\n              \\\"Name\\\": \\\"db-cluster-id\\\",\n              \\\"Values\\\": [\n                \\\"foo-bar\\\"\n              ]\n            },\n            {\n              \\\"Name\\\": \\\"db-instance-id\\\",\n              \\\"Values\\\": [\n                \\\"foo-bar\\\"\n              ]\n            }\n          ]\n        },\n        \\\"AgentWaitQueryExpr\\\": \\\"$.DBInstances[*].DBInstanceStatus\\\",\n        \\\"AgentWaitCreateQueryValues\\\": [\n            \\\"available\\\"\n        ],\n        \\\"AgentWaitDeleteQueryValues\\\": [\n            \\\"available\\\"\n        ]\n      }\n    }\" | jq -c | ./generic_provider.py\n    popd\n\n#### modify-db-instance\n\u003e mock CloudFormation request to [enable](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rds.html#RDS.Client.modify_db_cluster) RDS Performance Insights\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"PhysicalResourceId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceProperties\\\": {\n        \\\"AgentService\\\": \\\"rds\\\",\n        \\\"AgentType\\\": \\\"client\\\",\n        \\\"AgentCreateMethod\\\": \\\"modify_db_instance\\\",\n        \\\"AgentCreateArgs\\\": {\n          \\\"DBInstanceIdentifier\\\": \\\"abcdefghij1234\\\",\n          \\\"EnablePerformanceInsights\\\": true,\n          \\\"PerformanceInsightsKMSKeyId\\\": \\\"arn:aws:kms:${AWS_REGION}:1234567890:key/$(uuid)\\\",\n          \\\"PerformanceInsightsRetentionPeriod\\\": 7,\n          \\\"ApplyImmediately\\\": true\n        },\n        \\\"AgentDeleteMethod\\\": \\\"modify_db_instance\\\",\n        \\\"AgentDeleteArgs\\\": {\n          \\\"DBInstanceIdentifier\\\": \\\"1234abcdefghij\\\",\n          \\\"EnablePerformanceInsights\\\": false,\n          \\\"ApplyImmediately\\\": true\n        },\n        \\\"AgentWaitMethod\\\": \\\"describe_db_instances\\\",\n        \\\"AgentWaitDelay\\\": \\\"60\\\",\n        \\\"AgentWaitArgs\\\": {\n          \\\"Filters\\\": [\n            {\n              \\\"Name\\\": \\\"db-cluster-id\\\",\n              \\\"Values\\\": [\n                \\\"1234abcdefghij\\\"\n              ]\n            },\n            {\n              \\\"Name\\\": \\\"db-instance-id\\\",\n              \\\"Values\\\": [\n                \\\"abcdefghij1234\\\"\n              ]\n            }\n          ]\n        },\n        \\\"AgentWaitQueryExpr\\\": \\\"$.DBInstances[*].DBInstanceStatus\\\",\n        \\\"AgentWaitCreateQueryValues\\\": [\n            \\\"available\\\"\n        ],\n        \\\"AgentWaitDeleteQueryValues\\\": [\n            \\\"available\\\"\n        ]\n      }\n    }\" | jq -c | ./generic_provider.py\n    popd\n\n### Database Migration Service\n\u003e [DMS](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dms.html) API reference\n\n#### describe-replication-tasks\n\u003e mock CloudFormation request to [describe](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dms.html#DatabaseMigrationService.Client.describe_replication_tasks) running replication tasks\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"ResourceProperties\\\": {\n        \\\"AgentService\\\": \\\"dms\\\",\n        \\\"AgentType\\\": \\\"client\\\",\n        \\\"AgentCreateMethod\\\": \\\"describe_replication_tasks\\\",\n        \\\"AgentCreateArgs\\\": {\n          \\\"Filters\\\": [\n            {\n              \\\"Name\\\": \\\"replication-instance-arn\\\",\n              \\\"Values\\\": [\n                \\\"arn:aws:dms:us-west-2:313347522657:rep:ABCDEFGHIJKLMNOPQRSTUVWXYZ\\\"\n              ]\n            }\n          ]\n        },\n        \\\"AgentWaitQueryExpr\\\": \\\"$.ReplicationTasks[?(@.Status=='running')].ReplicationTaskArn\\\"\n      }\n    }\" | jq -c | ./generic_provider.py\n    popd\n\n#### stop-replication-task\n\u003e mock CloudFormation request to [stop](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dms.html#DatabaseMigrationService.Client.stop_replication_task) replication task\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"ResourceProperties\\\": {\n        \\\"AgentService\\\": \\\"dms\\\",\n        \\\"AgentType\\\": \\\"client\\\",\n        \\\"AgentWaitMethod\\\": \\\"replication_task_stopped\\\",\n        \\\"AgentWaitArgs\\\": {\n          \\\"Filters\\\": [\n            {\n              \\\"Name\\\": \\\"replication-task-arn\\\",\n              \\\"Values\\\": [\n                \\\"arn:aws:dms:${AWS_REGION}:1234567890:task:ABCDEFGHIJKLMNOPQRSTUVWXYZ\\\"\n              ]\n            }\n          ]\n        },\n        \\\"AgentCreateMethod\\\": \\\"stop_replication_task\\\",\n        \\\"AgentCreateExceptions\\\": [\n          \\\"agent.exceptions.InvalidResourceStateFault\\\",\n          \\\"agent.exceptions.ClientError\\\"\n        ],\n        \\\"AgentWaitCreateExceptions\\\": [\n          \\\"botocore.exceptions.WaiterError\\\"\n\n        ],\n        \\\"AgentCreateArgs\\\": {\n          \\\"ReplicationTaskArn\\\": \\\"arn:aws:dms:${AWS_REGION}:1234567890:task:ABCDEFGHIJKLMNOPQRSTUVWXYZ\\\"\n        }\n      }\n    }\" | jq -c | ./generic_provider.py\n    popd\n\n### EC2\n\u003e [EC2](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html) API reference\n\n#### create_launch_template_from_configuration\n\u003e mock CloudFormation request to [create_launch_template_from_configuration](https://github.com/ab77/cfn-generic-custom-resource/blob/master/generic_provider/autoscaling.py)\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"PhysicalResourceId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"custom\\\",\n          \\\"AgentService\\\": \\\"autoscaling\\\",\n          \\\"AgentCreateMethod\\\": \\\"create_launch_template_from_configuration\\\",\n          \\\"AgentDeleteMethod\\\": \\\"delete_launch_template\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"LaunchConfigurationName\\\": \\\"awseb-e-abcdef1234-stack-AWSEBAutoScalingLaunchConfiguration-99F00TRKDCBAR\\\",\n              \\\"LaunchTemplateName\\\": \\\"foo-bar\\\",\n              \\\"Description\\\": \\\"foo-bar\\\",\n              \\\"TagSpecifications\\\": [\n                  {\n                      \\\"ResourceType\\\": \\\"launch-template\\\",\n                      \\\"Tags\\\": [\n                          {\n                              \\\"Key\\\": \\\"Name\\\",\n                              \\\"Value\\\": \\\"foo-bar\\\"\n                          }\n                      ]\n                  }\n              ]\n          },\n          \\\"AgentDeleteArgs\\\": {\n              \\\"LaunchTemplateName\\\": \\\"foo-bar\\\"\n          }\n      }\n    }\" | jq -c | VERBOSE=1 ./generic_provider.py\n    popd\n\n#### update_auto_scaling_group\n\u003e mock CloudFormation request to update ASGs created by Elastic Beanstalk with [MixedInstancesPolicy](https://github.com/ab77/cfn-generic-custom-resource/blob/master/generic_provider/autoscaling.py)\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"PhysicalResourceId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"custom\\\",\n          \\\"AgentService\\\": \\\"autoscaling\\\",\n          \\\"AgentResponseNode\\\": \\\"ResponseMetadata\\\",\n          \\\"AgentCreateMethod\\\": \\\"update_auto_scaling_group\\\",\n          \\\"AgentUpdateMethod\\\": \\\"update_auto_scaling_group\\\",\n          \\\"AgentCreateArgs\\\": {\n            \\\"AutoScalingGroupName\\\": \\\"awseb-e-abcdef1234-stack-AWSEBAutoScalingGroup-99F00TRKDCBAR\\\",\n            \\\"MixedInstancesPolicy\\\": {\n              \\\"LaunchTemplate\\\": {\n                \\\"LaunchTemplateSpecification\\\": {\n                  \\\"LaunchTemplateId\\\": \\\"lt-abcdef1234567890\\\",\n                  \\\"Version\\\": \\\"1\\\"\n                },\n                \\\"Overrides\\\": [\n                  {\n                    \\\"InstanceType\\\": \\\"t3a.nano\\\"\n                  },\n                  {\n                    \\\"InstanceType\\\": \\\"t3a.micro\\\"\n                  },\n                  {\n                    \\\"InstanceType\\\": \\\"t3a.small\\\"\n                  },\n                  {\n                    \\\"InstanceType\\\": \\\"t3a.medium\\\"\n                  },\n                  {\n                    \\\"InstanceType\\\": \\\"t3a.large\\\"\n                  }\n                ]\n              },\n              \\\"InstancesDistribution\\\": {\n                \\\"OnDemandBaseCapacity\\\": 0,\n                \\\"OnDemandPercentageAboveBaseCapacity\\\": 50\n              }\n            }\n          }\n        }\n      }\n    }\" | jq -c | VERBOSE=1 ./generic_provider.py\n    popd\n\n#### create-tags\n\u003e mock CloudFormation request to [tag](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.ServiceResource.create_tags) resources\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"ec2\\\",\n          \\\"AgentCreateMethod\\\": \\\"create_tags\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"Resources\\\": [\n                  \\\"eipalloc-12345677890\\\"\n              ],\n              \\\"Tags\\\": [\n                  {\n                      \\\"Key\\\": \\\"foo\\\",\n                      \\\"Value\\\": \\\"bar\\\"\n                  }\n              ]\n          },\n          \\\"AgentDeleteMethod\\\": \\\"delete_tags\\\",\n          \\\"AgentDeleteArgs\\\": {\n              \\\"Resources\\\": [\n                  \\\"eipalloc-12345677890\\\"\n              ],\n              \\\"Tags\\\": [\n                  {\n                      \\\"Key\\\": \\\"foo\\\",\n                      \\\"Value\\\": \\\"bar\\\"\n                  }\n              ]\n          }\n      }\n    }\" | jq -c | ./generic_provider.py\n    popd\n\n#### authorize-security-group-ingress\n\u003e mock CloudFormation request to [authorize_security_group_ingress](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.Client.authorize_security_group_ingress) in another account\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"ec2\\\",\n          \\\"RoleArn\\\": \\\"arn:aws:iam::1234567890:role/CrossAccountRole\\\",\n          \\\"AgentRegion\\\": \\\"us-east-1\\\",\n          \\\"AgentCreateMethod\\\": \\\"authorize_security_group_ingress\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"GroupId\\\": \\\"sg-1234567890abcdef\\\",\n              \\\"IpPermissions\\\": [\n                  {\n                      \\\"FromPort\\\": 22,\n                      \\\"IpProtocol\\\": \\\"tcp\\\",\n                      \\\"IpRanges\\\": [\n                          {\n                              \\\"CidrIp\\\": \\\"172.16.0.0/16\\\",\n                              \\\"Description\\\": \\\"foo-bar\\\"\n                          }\n\n                      ],\n                      \\\"ToPort\\\": 22\n                  }\n              ]\n          },\n          \\\"AgentDeleteMethod\\\": \\\"revoke_security_group_ingress\\\",\n          \\\"AgentDeleteArgs\\\": {\n              \\\"GroupId\\\": \\\"sg-1234567890abcdef\\\",\n              \\\"IpPermissions\\\": [\n                  {\n                      \\\"FromPort\\\": 22,\n                      \\\"IpProtocol\\\": \\\"tcp\\\",\n                      \\\"IpRanges\\\": [\n                          {\n                              \\\"CidrIp\\\": \\\"172.16.0.0/16\\\",\n                              \\\"Description\\\": \\\"foo-bar\\\"\n                          }\n\n                      ],\n                      \\\"ToPort\\\": 22\n                  }\n              ]\n          }\n      }\n    }\" | jq -c | ./generic_provider.py\n    popd\n\n#### modify-subnet-attribute\n\u003e mock CloudFormation request to [modify](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.Client.modify_subnet_attribute) subnet attribute(s)\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"ec2\\\",\n          \\\"AgentCreateMethod\\\": \\\"modify_subnet_attribute\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"MapPublicIpOnLaunch\\\": {\n                  \\\"Value\\\": true\n              },\n              \\\"SubnetId\\\": \\\"subnet-abcdef1234567890\\\"\n          }\n      }\n    }\" | jq -c | ./generic_provider.py\n    popd\n\n#### get-parameter\n\u003e mock CloudFormation request to [get](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm.html#SSM.Client.get_parameter) existing SSM parameter (stored outside of stack)\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"ssm\\\",\n          \\\"AgentCreateMethod\\\": \\\"get_parameter\\\",\n          \\\"AgentWaitQueryExpr\\\": \\\"$.Parameter.Value\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"Name\\\": \\\"/foo/bar\\\",\n              \\\"WithDecryption\\\": true\n          }\n      }\n    }\" | jq -c | ./generic_provider.py\n    popd\n\n#### put-parameter\n\u003e mock CloudFormation request to [put](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm.html#SSM.Client.put_parameter) SSM parameter\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"ssm\\\",\n          \\\"AgentCreateMethod\\\": \\\"put_parameter\\\",\n          \\\"AgentUpdateMethod\\\": \\\"put_parameter\\\",\n          \\\"AgentDeleteMethod\\\": \\\"delete_parameter\\\",\n          \\\"AgentResourceId\\\": \\\"Name\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"Name\\\": \\\"/foo/bar\\\",\n              \\\"Value\\\": \\\"foo-bar\\\",\n              \\\"Type\\\": \\\"SecureString\\\",\n              \\\"Overwrite\\\": false\n          },\n          \\\"AgentUpdateArgs\\\": {\n              \\\"Name\\\": \\\"/foo/bar\\\",\n              \\\"Value\\\": \\\"foo-bar\\\",\n              \\\"Type\\\": \\\"SecureString\\\",\n              \\\"Overwrite\\\": true\n          },\n          \\\"AgentDeleteArgs\\\": {\n              \\\"Name\\\": \\\"/foo/bar\\\"\n          }\n      }\n    }\" | jq -c | ./generic_provider.py\n    popd\n\n#### get the latest launch template version\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"PhysicalResourceId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"ec2\\\",\n          \\\"AgentCreateMethod\\\": \\\"describe_launch_template_versions\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"LaunchTemplateId\\\": \\\"lt-deadbeef1234567890\\\"\n          },\n          \\\"AgentResponseNode\\\": \\\"$.[0].VersionNumber\\\"\n      }\n    }\" | jq -c | VERBOSE=1 ./generic_provider.py\n    popd\n\n### EKS\n\u003e [EKS](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/eks.html) API reference\n\n#### get addon version(s)\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"eks\\\",\n          \\\"AgentCreateMethod\\\": \\\"describe_addon_versions\\\",\n          \\\"AgentWaitQueryExpr\\\": \\\"$.[0].addonVersions[0].addonVersion\\\",\n          \\\"AgentResponseNode\\\": \\\"$.[0].addonVersions[0]\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"kubernetesVersion\\\": \\\"1.31\\\",\n              \\\"addonName\\\": \\\"kube-proxy\\\"\n          }\n      }\n    }\" | jq -c | ./generic_provider.py\n    popd\n\n#### update nodegroup to latest launch template version\n\n    pushd generic_provider\n    echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"ResourceProperties\\\": {\n          \\\"AgentType\\\": \\\"client\\\",\n          \\\"AgentService\\\": \\\"eks\\\",\n          \\\"AgentCreateMethod\\\": \\\"update_nodegroup_version\\\",\n          \\\"AgentCreateArgs\\\": {\n              \\\"clusterName\\\": \\\"foo-bar\\\",\n              \\\"nodegroupName\\\": \\\"foo-bar\\\",\n              \\\"launchTemplate\\\": {\n                \\\"id\\\": \\\"lt-deadbeef1234567890\\\",\n                \\\"version\\\": \\\"21\\\"\n              }\n          }\n      }\n    }\" | jq -c | ./generic_provider.py\n    popd\n\n## mock resources requests\n\u003e [EC2](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html) API reference\n\n#### network-interfaces-attribute\n\u003e mock CloudFormation request to [obtain](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.Instance.network_interfaces_attribute) instance public IPv6 address\n\n     pushd generic_provider\n     echo \"{\n      \\\"RequestType\\\": \\\"Create\\\",\n      \\\"ResponseURL\\\": \\\"https://cloudformation-custom-resource-response-${AWS_REGION}.s3.amazonaws.com/\\\",\n      \\\"StackId\\\": \\\"arn:aws:cloudformation:${AWS_REGION}:$(aws sts get-caller-identity | jq -r '.Account'):stack/MockStack/$(uuid)\\\",\n      \\\"RequestId\\\": \\\"$(uuid)\\\",\n      \\\"ResourceType\\\": \\\"Custom::MockResource\\\",\n      \\\"LogicalResourceId\\\": \\\"MockResource\\\",\n      \\\"ResourceProperties\\\": {\n        \\\"AgentService\\\": \\\"ec2\\\",\n        \\\"AgentType\\\": \\\"resource\\\",\n        \\\"AgentWaitQueryExpr\\\": \\\"$..Ipv6Address\\\",\n        \\\"AgentResourceId\\\": \\\"Ipv6Address\\\",\n        \\\"AgentCreateMethod\\\": \\\"network_interfaces_attribute\\\",\n        \\\"AgentCreateArgs\\\": {\n          \\\"ResourceName\\\": \\\"Instance\\\",\n          \\\"ResourceId\\\": \\\"i-abcdef1234567890\\\"\n        }\n      }\n    }\" | jq -c | ./generic_provider.py\n    popd\n\n\u003e--belodetek 😬\n\n\n[post]: https://anton.belodedenko.me/generic-custom-resource-provider/\n[Custom Resources]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html\n[boto3]: https://boto3.amazonaws.com/v1/documentation/api/latest/index.html\n[bucket replication rules]: examples/s3/bucket-replication-rule.yml\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbelodetek%2Fcfn-generic-custom-resource","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbelodetek%2Fcfn-generic-custom-resource","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbelodetek%2Fcfn-generic-custom-resource/lists"}