Repository: aws-cloudformation/community-registry-extensions (language: python)

Issue:
AwsCommunity::Time::Sleep
### What type of extension are you looking for?

Resource

### Describe the extension you'd like to request

A resource that simply sleeps for X seconds. Sometimes resources aren't actually fully deployed when CloudFormation starts to deploy a dependent resource, and a sleep would solve it.

### Describe the solution you'd like

```
Resources:

  Sleep10:
    Type: AwsCommunity::Time::Sleep
    Properties:
      Seconds: 10
```

### Additional context

_No response_

### Is this something that you'd be interested in working on?

- [ ] 👋 I may be able to implement this feature request

### Would this feature include a breaking change?

- [ ] ⚠️ This feature might incur a breaking change


Discussion / hints:
See https://github.com/aws-cloudformation/cloudformation-coverage-roadmap/issues/589
@ericzbeard the one issue with this resource is that needs something dynamic to update and redo the sleep. 

Thoughts on something like this?

```yaml

Resources:
  Instance:
      Type: AWS::EC2::Instance
      Properties: ...
  Sleep10:
      Type: AwsCommunity::Time::Sleep
      Properties:
        Seconds: 10
        SleepOn: !Ref Instance
```
Could we do the same with a DependsOn? Or does it need to be a ref in the properties?
DependsOn wouldn't help here.  I'm thinking if you wanted to put a sleep after resource X is created.  DependsOn would work there (along with a Ref).  However if that resource had to be recreated you may want it to sleep again. The only way I can accomplish that is if a property changes so the update handler would get called. 
I guess `SleepOn` isn't really descriptive enough. I'm still not sure I fully understand the scenario. You're saying that if I update the EC2 instance, nothing will cause the sleep to happen unless the sleep resource itself also had a change, despite there being a `DependsOn`? Maybe `SleepAfterUpdate`?
> I guess `SleepOn` isn't really descriptive enough. I'm still not sure I fully understand the scenario. You're saying that if I update the EC2 instance, nothing will cause the sleep to happen unless the sleep resource itself also had a change, despite there being a `DependsOn`? 

Correct.  DependsOn will only affect the ordering but the resource handlers won't be called unless something has changed inside the properties.

> Maybe `SleepAfterUpdate`?

I'm fine with this if you like that more.



Fix patch:
diff --git a/release/awscommunity/cicd.yml b/release/awscommunity/cicd.yml
index fdca2616..18092fed 100644
--- a/release/awscommunity/cicd.yml
+++ b/release/awscommunity/cicd.yml
@@ -179,6 +179,19 @@ Resources:
       ManagedPolicyArns:
         - Fn::ImportValue: !Sub "cep-${Env}-common-build-project-policy"
 
+  TimeSleepBuildProjectRole:
+    Type: AWS::IAM::Role
+    Properties:
+      AssumeRolePolicyDocument:
+        Statement:
+          - Action: sts:AssumeRole
+            Effect: Allow
+            Principal:
+              Service: codebuild.amazonaws.com
+        Version: '2012-10-17'
+      ManagedPolicyArns:
+        - Fn::ImportValue: !Sub "cep-${Env}-common-build-project-policy"
+
   CloudFrontWebAclAssociationBuildProjectRole:
     Type: AWS::IAM::Role
     Properties:
@@ -409,6 +422,40 @@ Resources:
       Roles:
         - !Ref TimeStaticBuildProjectRole
 
+  TimeSleepBuildProjectRolePolicy:
+    Type: AWS::IAM::Policy
+    Properties:
+      PolicyDocument:
+        Statement:
+          - Action:
+              - iam:CreateRole
+              - iam:DeleteRole
+              - iam:GetRole
+              - dynamodb:*
+            Effect: Allow
+            Resource: "*"
+          - Action:
+              - codebuild:StartBuild
+              - codebuild:BatchGetBuilds
+              - codebuild:StopBuild
+              - codebuild:RetryBuild
+              - codebuild:StartBuildBatch
+              - codebuild:RetryBuildBatch
+              - codebuild:StopBuildBatch
+            Effect: Allow
+            Resource: 
+              - !GetAtt TimeSleepBuildProject.Arn
+          - Action:
+              - ssm:DeleteParameter
+              - ssm:GetParameter
+              - ssm:PutParameter
+            Effect: Allow
+            Resource: "*"
+        Version: '2012-10-17'
+      PolicyName: delete-bucket-contents-build-project-policy
+      Roles:
+        - !Ref TimeSleepBuildProjectRole
+
   CloudFrontWebAclAssociationBuildProjectPolicy:
     Type: AWS::IAM::Policy
     Properties:
@@ -650,6 +697,28 @@ Resources:
         BuildSpec: !Sub "resources/${Env}-buildspec-go.yml"
       TimeoutInMinutes: 480
 
+  TimeSleepBuildProject:
+    Type: AWS::CodeBuild::Project
+    Properties:
+      Name: !Sub "${PrefixLower}-${Env}-time-sleep"
+      Artifacts:
+        Type: CODEPIPELINE
+      Environment:
+        ComputeType: BUILD_GENERAL1_LARGE
+        Image: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/cep-cicd:latest"
+        ImagePullCredentialsType: SERVICE_ROLE
+        PrivilegedMode: true
+        Type: LINUX_CONTAINER
+        EnvironmentVariables:
+        - Name: RESOURCE_PATH
+          Type: PLAINTEXT
+          Value: "placeholder-for-path-to-resource"
+      ServiceRole: !GetAtt TimeSleepBuildProjectRole.Arn
+      Source:
+        Type: CODEPIPELINE
+        BuildSpec: !Sub "resources/${Env}-buildspec-go.yml"
+      TimeoutInMinutes: 480
+
   S3BucketVersioningEnabledBuildProject:
     Type: AWS::CodeBuild::Project
     Properties:
@@ -922,6 +991,7 @@ Resources:
               - !GetAtt S3BucketVersioningEnabledBuildProject.Arn
               - !GetAtt S3PublicAccessControlsRestrictedBuildProject.Arn
               - !GetAtt TimeStaticBuildProject.Arn
+              - !GetAtt TimeSleepBuildProject.Arn
               - !GetAtt CloudFrontLoggingEnabledBuildProject.Arn
               - !GetAtt CloudFrontWebAclAssociationBuildProject.Arn
               - !GetAtt S3BucketModuleBuildProject.Arn
@@ -987,6 +1057,7 @@ Resources:
                 - !GetAtt S3BucketVersioningEnabledBuildProjectRole.Arn
                 - !GetAtt S3PublicAccessControlsRestrictedBuildProjectRole.Arn
                 - !GetAtt TimeStaticBuildProjectRole.Arn
+                - !GetAtt TimeSleepBuildProjectRole.Arn
                 - !GetAtt AlternateContactBuildProjectRole.Arn
                 - !GetAtt CloudFrontLoggingEnabledBuildProjectRole.Arn
                 - !GetAtt CloudFrontWebAclAssociationBuildProjectRole.Arn
@@ -1145,6 +1216,30 @@ Resources:
                     }
                   ]
               RunOrder: 1
+            - Name: TimeSleep
+              InputArtifacts:
+                - Name: extensions-source
+              ActionTypeId:
+                Category: Build
+                Owner: AWS
+                Provider: CodeBuild
+                Version: 1
+              Configuration:
+                ProjectName: !Ref TimeSleepBuildProject
+                # BatchEnabled: 
+                #   !If
+                #     - IsProd
+                #     - true
+                #     - false
+                EnvironmentVariables: |-
+                  [
+                    {
+                      "name": "RESOURCE_PATH",
+                      "type": "PLAINTEXT",
+                      "value": "resources/Time_Sleep"
+                    }
+                  ]
+              RunOrder: 1
             - Name: Account_AlternateContact
               InputArtifacts:
                 - Name: extensions-source
diff --git a/resources/Time_Sleep/.gitignore b/resources/Time_Sleep/.gitignore
new file mode 100644
index 00000000..65f75e20
--- /dev/null
+++ b/resources/Time_Sleep/.gitignore
@@ -0,0 +1,17 @@
+# macOS
+.DS_Store
+._*
+
+# our logs
+rpdk.log*
+
+#compiled file
+bin/
+
+#vender
+vender/
+
+# contains credentials
+sam-tests/
+awscommunity-time-sleep.zip
+.hypothesis/
\ No newline at end of file
diff --git a/resources/Time_Sleep/.rpdk-config b/resources/Time_Sleep/.rpdk-config
new file mode 100644
index 00000000..15d810d5
--- /dev/null
+++ b/resources/Time_Sleep/.rpdk-config
@@ -0,0 +1,21 @@
+{
+    "artifact_type": "RESOURCE",
+    "typeName": "AwsCommunity::Time::Sleep",
+    "language": "go",
+    "runtime": "go1.x",
+    "entrypoint": "handler",
+    "testEntrypoint": "handler",
+    "settings": {
+        "version": false,
+        "subparser_name": null,
+        "verbose": 0,
+        "force": false,
+        "type_name": null,
+        "artifact_type": null,
+        "endpoint_url": null,
+        "region": null,
+        "target_schemas": [],
+        "import_path": "github.com/aws-cloudformation/awscommunity-registry-extensions/resources/Time_Sleep",
+        "protocolVersion": "2.0.0"
+    }
+}
diff --git a/resources/Time_Sleep/Makefile b/resources/Time_Sleep/Makefile
new file mode 100644
index 00000000..bc13ba53
--- /dev/null
+++ b/resources/Time_Sleep/Makefile
@@ -0,0 +1,11 @@
+.PHONY: build test clean
+
+build:
+	make -f makebuild  # this runs build steps required by the cfn cli
+
+test:
+	cfn generate
+	env GOOS=linux go build -ldflags="-s -w" -o bin/handler cmd/main.go
+
+clean:
+	rm -rf bin
diff --git a/resources/Time_Sleep/README.md b/resources/Time_Sleep/README.md
new file mode 100644
index 00000000..9734071f
--- /dev/null
+++ b/resources/Time_Sleep/README.md
@@ -0,0 +1,57 @@
+# AwsCommunity::Time::Sleep
+
+Put a sleep into your CloudFormation templates.
+
+## Example
+
+```yaml
+Resources:
+   Instance:
+      Type: AWS::EC2::Instance
+      ...
+   Sleep:
+      Type: AwsCommunity::Time::Sleep
+      Properties:
+         Seconds: 10
+         SleepOnCreate: true
+         SleepOnUpdate: true
+         SleepOnDelete: true
+         Triggers:
+         # will sleep any time the instance ID changes
+         - !Ref Instance
+```
+
+## Properties
+The following properties you can use in a `GetAtt`
+
+| Attribute  | Type | Description |
+| ------------- | ------------- | ------------- |
+| **Seconds**  | integer  | The number of seconds to sleep.
+| **SleepOnCreate**  | boolean  | If you want to sleep on creation. (default=true)
+| **SleepOnUpdate** | boolean  | If you want to sleep on update. (default=true)
+| **SleepOnDelete** | boolean  | If you want to sleep on delete. (default=true)
+| **Triggers** | array of strings  | A list of strings that represent when we want to do a sleep on update
+
+## Attributes
+None
+
+## Development
+
+Open two tabs in your terminal.
+
+Run SAM 
+```sh
+sam local start-lambda
+```
+
+In another tab, run cfn test:
+
+```sh
+cd resources/Time_Sleep
+cfn test"
+```
+
+## Notes
+
+### SSM Parameter
+Since this extension does not represent an actual resource, we have to create a Systems Manager Parameter Store entry to track its state for the purposes of satisfying the CloudFormation registry contract. We store a key in parameter store at `/CloudFormation/AwsCommunity/Time/Offset/unique-identifier`.
diff --git a/resources/Time_Sleep/awscommunity-time-sleep.json b/resources/Time_Sleep/awscommunity-time-sleep.json
new file mode 100644
index 00000000..ee71835a
--- /dev/null
+++ b/resources/Time_Sleep/awscommunity-time-sleep.json
@@ -0,0 +1,73 @@
+{
+    "typeName": "AwsCommunity::Time::Sleep",
+    "description": "An example resource schema demonstrating some basic constructs and validation rules.",
+    "sourceUrl": "https://github.com/aws-cloudformation/aws-cloudformation-rpdk.git",
+    "definitions": {},
+    "properties": {
+        "Id": {
+            "description": "Id is a unique identifier that is auto generated.",
+            "type": "string"
+        },
+        "Seconds": {
+            "description": "The number of seconds to sleep for.",
+            "type": "integer",
+            "exclusiveMinimum": 0,
+            "exclusiveMaximum": 600
+        },
+        "SleepOnCreate": {
+            "description": "If we should sleep on a create.",
+            "type": "boolean",
+            "default": true
+        },
+        "SleepOnUpdate": {
+            "description": "If we should sleep on an update.",
+            "type": "boolean",
+            "default": true
+        },
+        "SleepOnDelete": {
+            "description": "If we should sleep on a delete.",
+            "type": "boolean",
+            "default": true
+        },
+        "Triggers": {
+            "description": "A value to represent when a sleep should occur. Any time this is updated this resource will sleep.",
+            "type": "array",
+            "items": {
+                "type": "string"
+            },
+            "insertionOrder": true
+        }
+    },
+    "additionalProperties": false,
+    "required": [
+        "Seconds"
+    ],
+    "readOnlyProperties": [
+        "/properties/Id"
+    ],
+    "primaryIdentifier": [
+        "/properties/Id"
+    ],
+    "handlers": {
+        "create": {
+            "permissions": [
+                "ssm:PutParameter"
+            ]
+        },
+        "read": {
+            "permissions": [
+                "ssm:GetParameter"
+            ]
+        },
+        "update": {
+            "permissions": [
+                "ssm:PutParameter"
+            ]
+        },
+        "delete": {
+            "permissions": [
+                "ssm:DeleteParameter"
+            ]
+        }
+    }
+}
diff --git a/resources/Time_Sleep/cmd/main.go b/resources/Time_Sleep/cmd/main.go
new file mode 100644
index 00000000..d9d34d8f
--- /dev/null
+++ b/resources/Time_Sleep/cmd/main.go
@@ -0,0 +1,85 @@
+// Code generated by 'cfn generate', changes will be undone by the next invocation. DO NOT EDIT.
+package main
+
+import (
+	"errors"
+	"fmt"
+	"log"
+
+	"github.com/aws-cloudformation/awscommunity-registry-extensions/resources/Time_Sleep/cmd/resource"
+	"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn"
+	"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/handler"
+)
+
+// Handler is a container for the CRUDL actions exported by resources
+type Handler struct{}
+
+// Create wraps the related Create function exposed by the resource code
+func (r *Handler) Create(req handler.Request) handler.ProgressEvent {
+	return wrap(req, resource.Create)
+}
+
+// Read wraps the related Read function exposed by the resource code
+func (r *Handler) Read(req handler.Request) handler.ProgressEvent {
+	return wrap(req, resource.Read)
+}
+
+// Update wraps the related Update function exposed by the resource code
+func (r *Handler) Update(req handler.Request) handler.ProgressEvent {
+	return wrap(req, resource.Update)
+}
+
+// Delete wraps the related Delete function exposed by the resource code
+func (r *Handler) Delete(req handler.Request) handler.ProgressEvent {
+	return wrap(req, resource.Delete)
+}
+
+// List wraps the related List function exposed by the resource code
+func (r *Handler) List(req handler.Request) handler.ProgressEvent {
+	return wrap(req, resource.List)
+}
+
+// main is the entry point of the application.
+func main() {
+	cfn.Start(&Handler{})
+}
+
+type handlerFunc func(handler.Request, *resource.Model, *resource.Model) (handler.ProgressEvent, error)
+
+func wrap(req handler.Request, f handlerFunc) (response handler.ProgressEvent) {
+	defer func() {
+		// Catch any panics and return a failed ProgressEvent
+		if r := recover(); r != nil {
+			err, ok := r.(error)
+			if !ok {
+				err = errors.New(fmt.Sprint(r))
+			}
+
+			log.Printf("Trapped error in handler: %v", err)
+
+			response = handler.NewFailedEvent(err)
+		}
+	}()
+
+	// Populate the previous model
+	prevModel := &resource.Model{}
+	if err := req.UnmarshalPrevious(prevModel); err != nil {
+		log.Printf("Error unmarshaling prev model: %v", err)
+		return handler.NewFailedEvent(err)
+	}
+
+	// Populate the current model
+	currentModel := &resource.Model{}
+	if err := req.Unmarshal(currentModel); err != nil {
+		log.Printf("Error unmarshaling model: %v", err)
+		return handler.NewFailedEvent(err)
+	}
+
+	response, err := f(req, prevModel, currentModel)
+	if err != nil {
+		log.Printf("Error returned from handler function: %v", err)
+		return handler.NewFailedEvent(err)
+	}
+
+	return response
+}
diff --git a/resources/Time_Sleep/cmd/resource/config.go b/resources/Time_Sleep/cmd/resource/config.go
new file mode 100644
index 00000000..4d9eb783
--- /dev/null
+++ b/resources/Time_Sleep/cmd/resource/config.go
@@ -0,0 +1,19 @@
+// Code generated by 'cfn generate', changes will be undone by the next invocation. DO NOT EDIT.
+// Updates to this type are made my editing the schema file and executing the 'generate' command.
+package resource
+
+import "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/handler"
+
+// TypeConfiguration is autogenerated from the json schema
+type TypeConfiguration struct {
+}
+
+// Configuration returns a resource's configuration.
+func Configuration(req handler.Request) (*TypeConfiguration, error) {
+	// Populate the type configuration
+	typeConfig := &TypeConfiguration{}
+	if err := req.UnmarshalTypeConfig(typeConfig); err != nil {
+		return typeConfig, err
+	}
+	return typeConfig, nil
+}
diff --git a/resources/Time_Sleep/cmd/resource/model.go b/resources/Time_Sleep/cmd/resource/model.go
new file mode 100644
index 00000000..e0adcc22
--- /dev/null
+++ b/resources/Time_Sleep/cmd/resource/model.go
@@ -0,0 +1,13 @@
+// Code generated by 'cfn generate', changes will be undone by the next invocation. DO NOT EDIT.
+// Updates to this type are made my editing the schema file and executing the 'generate' command.
+package resource
+
+// Model is autogenerated from the json schema
+type Model struct {
+	Id            *string  `json:",omitempty"`
+	Seconds       *int     `json:",omitempty"`
+	SleepOnCreate *bool    `json:",omitempty"`
+	SleepOnUpdate *bool    `json:",omitempty"`
+	SleepOnDelete *bool    `json:",omitempty"`
+	Triggers      []string `json:",omitempty"`
+}
diff --git a/resources/Time_Sleep/cmd/resource/resource.go b/resources/Time_Sleep/cmd/resource/resource.go
new file mode 100644
index 00000000..a5bf1baa
--- /dev/null
+++ b/resources/Time_Sleep/cmd/resource/resource.go
@@ -0,0 +1,149 @@
+package resource
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/handler"
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/service/cloudformation"
+	"github.com/aws/aws-sdk-go/service/ssm"
+	"github.com/google/uuid"
+)
+
+func buildSsmParameterString(model *Model) string {
+	resourceType := "AwsCommunity::Time::Sleep"
+	resourceTypeSplits := strings.Split(resourceType, "::")
+	return fmt.Sprintf("/CloudFormation/%s/%s/%s/%s", resourceTypeSplits[0], resourceTypeSplits[1], resourceTypeSplits[2], *model.Id)
+}
+
+// Create handles the Create event from the Cloudformation service.
+func Create(req handler.Request, _ *Model, currentModel *Model) (handler.ProgressEvent, error) {
+
+	id := uuid.New().String()
+	currentModel.Id = &id
+
+	// Save the unique identifier in SSM if it exists its a duplicate
+	ssmParameter := buildSsmParameterString(currentModel)
+	svc := ssm.New(req.Session)
+	_, err := svc.PutParameter(&ssm.PutParameterInput{
+		Name:      &ssmParameter,
+		Value:     &id,
+		Overwrite: aws.Bool(false),
+		Tier:      aws.String("Standard"),
+		Type:      aws.String("String"),
+	})
+	if err != nil {
+		return handler.ProgressEvent{
+			OperationStatus:  handler.Failed,
+			Message:          err.Error(),
+			HandlerErrorCode: cloudformation.HandlerErrorCodeAlreadyExists,
+			ResourceModel:    nil,
+		}, nil
+	}
+
+	if currentModel.SleepOnCreate == nil || *currentModel.SleepOnCreate {
+		time.Sleep(time.Duration(*currentModel.Seconds * int(time.Second)))
+	}
+
+	response := handler.ProgressEvent{
+		OperationStatus: handler.Success,
+		Message:         "Create complete",
+		ResourceModel:   currentModel,
+	}
+	return response, nil
+}
+
+// Read handles the Read event from the Cloudformation service.
+func Read(req handler.Request, prevModel *Model, currentModel *Model) (handler.ProgressEvent, error) {
+
+	// See if the SSM parameter exists to determine if the resource exists
+	ssmParameter := buildSsmParameterString(currentModel)
+	svc := ssm.New(req.Session)
+	_, err := svc.GetParameter(&ssm.GetParameterInput{
+		Name: &ssmParameter,
+	})
+	if err != nil {
+		return handler.ProgressEvent{
+			OperationStatus:  handler.Failed,
+			HandlerErrorCode: cloudformation.HandlerErrorCodeNotFound,
+			Message:          "Resource not found",
+		}, nil
+	}
+
+	response := handler.ProgressEvent{
+		OperationStatus: handler.Success,
+		Message:         "Read complete",
+		ResourceModel:   currentModel,
+	}
+
+	return response, nil
+}
+
+// Update handles the Update event from the Cloudformation service.
+func Update(req handler.Request, prevModel *Model, currentModel *Model) (handler.ProgressEvent, error) {
+
+	// See if the SSM parameter exists to determine if the resource exists
+	ssmParameter := buildSsmParameterString(currentModel)
+	svc := ssm.New(req.Session)
+	_, err := svc.GetParameter(&ssm.GetParameterInput{
+		Name: &ssmParameter,
+	})
+	if err != nil {
+		return handler.ProgressEvent{
+			OperationStatus:  handler.Failed,
+			HandlerErrorCode: cloudformation.HandlerErrorCodeNotFound,
+			Message:          "Resource not found",
+		}, nil
+	}
+
+	if currentModel.SleepOnUpdate == nil || *currentModel.SleepOnUpdate {
+		time.Sleep(time.Duration(*currentModel.Seconds * int(time.Second)))
+	}
+	time.Sleep(time.Duration(*currentModel.Seconds * int(time.Second)))
+
+	response := handler.ProgressEvent{
+		OperationStatus: handler.Success,
+		Message:         "Update complete",
+		ResourceModel:   currentModel,
+	}
+
+	return response, nil
+}
+
+// Delete handles the Delete event from the Cloudformation service.
+func Delete(req handler.Request, prevModel *Model, currentModel *Model) (handler.ProgressEvent, error) {
+
+	// See if the SSM parameter exists to determine if the resource exists
+	ssmParameter := buildSsmParameterString(currentModel)
+	svc := ssm.New(req.Session)
+	_, err := svc.DeleteParameter(&ssm.DeleteParameterInput{
+		Name: &ssmParameter,
+	})
+	if err != nil {
+		return handler.ProgressEvent{
+			OperationStatus:  handler.Failed,
+			HandlerErrorCode: cloudformation.HandlerErrorCodeNotFound,
+			Message:          "Resource not found",
+		}, nil
+	}
+
+	if currentModel.SleepOnDelete == nil || *currentModel.SleepOnDelete {
+		time.Sleep(time.Duration(*currentModel.Seconds * int(time.Second)))
+	}
+
+	response := handler.ProgressEvent{
+		OperationStatus: handler.Success,
+		Message:         "Delete complete",
+		ResourceModel:   nil,
+	}
+
+	return response, nil
+}
+
+// List handles the List event from the Cloudformation service.
+func List(req handler.Request, prevModel *Model, currentModel *Model) (handler.ProgressEvent, error) {
+	return handler.ProgressEvent{}, errors.New("this resource type does not support list")
+}
diff --git a/resources/Time_Sleep/docs/README.md b/resources/Time_Sleep/docs/README.md
new file mode 100644
index 00000000..b6bea6e5
--- /dev/null
+++ b/resources/Time_Sleep/docs/README.md
@@ -0,0 +1,104 @@
+# AwsCommunity::Time::Sleep
+
+An example resource schema demonstrating some basic constructs and validation rules.
+
+## Syntax
+
+To declare this entity in your AWS CloudFormation template, use the following syntax:
+
+### JSON
+
+<pre>
+{
+    "Type" : "AwsCommunity::Time::Sleep",
+    "Properties" : {
+        "<a href="#seconds" title="Seconds">Seconds</a>" : <i>Integer</i>,
+        "<a href="#sleeponcreate" title="SleepOnCreate">SleepOnCreate</a>" : <i>Boolean</i>,
+        "<a href="#sleeponupdate" title="SleepOnUpdate">SleepOnUpdate</a>" : <i>Boolean</i>,
+        "<a href="#sleepondelete" title="SleepOnDelete">SleepOnDelete</a>" : <i>Boolean</i>,
+        "<a href="#triggers" title="Triggers">Triggers</a>" : <i>[ String, ... ]</i>
+    }
+}
+</pre>
+
+### YAML
+
+<pre>
+Type: AwsCommunity::Time::Sleep
+Properties:
+    <a href="#seconds" title="Seconds">Seconds</a>: <i>Integer</i>
+    <a href="#sleeponcreate" title="SleepOnCreate">SleepOnCreate</a>: <i>Boolean</i>
+    <a href="#sleeponupdate" title="SleepOnUpdate">SleepOnUpdate</a>: <i>Boolean</i>
+    <a href="#sleepondelete" title="SleepOnDelete">SleepOnDelete</a>: <i>Boolean</i>
+    <a href="#triggers" title="Triggers">Triggers</a>: <i>
+      - String</i>
+</pre>
+
+## Properties
+
+#### Seconds
+
+The number of seconds to sleep for.
+
+_Required_: Yes
+
+_Type_: Integer
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+#### SleepOnCreate
+
+If we should sleep on a create.
+
+_Required_: No
+
+_Type_: Boolean
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+#### SleepOnUpdate
+
+If we should sleep on an update.
+
+_Required_: No
+
+_Type_: Boolean
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+#### SleepOnDelete
+
+If we should sleep on a delete.
+
+_Required_: No
+
+_Type_: Boolean
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+#### Triggers
+
+A value to represent when a sleep should occur. Any time this is updated this resource will sleep.
+
+_Required_: No
+
+_Type_: List of String
+
+_Update requires_: [No interruption](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-no-interrupt)
+
+## Return Values
+
+### Ref
+
+When you pass the logical ID of this resource to the intrinsic `Ref` function, Ref returns the Id.
+
+### Fn::GetAtt
+
+The `Fn::GetAtt` intrinsic function returns a value for a specified attribute of this type. The following are the available attributes and sample return values.
+
+For more information about using the `Fn::GetAtt` intrinsic function, see [Fn::GetAtt](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html).
+
+#### Id
+
+Id is a unique identifier that is auto generated.
+
diff --git a/resources/Time_Sleep/go.mod b/resources/Time_Sleep/go.mod
new file mode 100644
index 00000000..beaa3081
--- /dev/null
+++ b/resources/Time_Sleep/go.mod
@@ -0,0 +1,16 @@
+module github.com/aws-cloudformation/awscommunity-registry-extensions/resources/Time_Sleep
+
+go 1.19
+
+require (
+	github.com/aws-cloudformation/cloudformation-cli-go-plugin v1.2.0
+	github.com/aws/aws-sdk-go v1.44.235
+	github.com/google/uuid v1.3.0
+)
+
+require (
+	github.com/aws/aws-lambda-go v1.37.0 // indirect
+	github.com/jmespath/go-jmespath v0.4.0 // indirect
+	github.com/segmentio/ksuid v1.0.4 // indirect
+	gopkg.in/validator.v2 v2.0.1 // indirect
+)
diff --git a/resources/Time_Sleep/go.sum b/resources/Time_Sleep/go.sum
new file mode 100644
index 00000000..99f83d04
--- /dev/null
+++ b/resources/Time_Sleep/go.sum
@@ -0,0 +1,58 @@
+github.com/aws-cloudformation/cloudformation-cli-go-plugin v1.2.0 h1:NHNKs4hOKBz9kufu2Ylce+P20x6mSxS2ryrYoW6AlX8=
+github.com/aws-cloudformation/cloudformation-cli-go-plugin v1.2.0/go.mod h1:u3nqs3hHrn8D51m7+N+6ya7Sksyd6OG3xK3RpXdRb1g=
+github.com/aws/aws-lambda-go v1.37.0 h1:WXkQ/xhIcXZZ2P5ZBEw+bbAKeCEcb5NtiYpSwVVzIXg=
+github.com/aws/aws-lambda-go v1.37.0/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM=
+github.com/aws/aws-sdk-go v1.44.235 h1:5MS1ZW1Pr27mmHFqqjuXYwGMlNTW/g6DqU5ekamPMeU=
+github.com/aws/aws-sdk-go v1.44.235/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
+github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
+github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
+github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY=
+gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/resources/Time_Sleep/inputs/inputs_1_create.json b/resources/Time_Sleep/inputs/inputs_1_create.json
new file mode 100644
index 00000000..4ac12ff7
--- /dev/null
+++ b/resources/Time_Sleep/inputs/inputs_1_create.json
@@ -0,0 +1,3 @@
+{
+    "Seconds": "10"
+}
diff --git a/resources/Time_Sleep/inputs/inputs_1_invalid.json b/resources/Time_Sleep/inputs/inputs_1_invalid.json
new file mode 100644
index 00000000..0ab0742a
--- /dev/null
+++ b/resources/Time_Sleep/inputs/inputs_1_invalid.json
@@ -0,0 +1,3 @@
+{
+    "Seconds": "0"
+}
diff --git a/resources/Time_Sleep/inputs/inputs_1_update.json b/resources/Time_Sleep/inputs/inputs_1_update.json
new file mode 100644
index 00000000..a71bcb36
--- /dev/null
+++ b/resources/Time_Sleep/inputs/inputs_1_update.json
@@ -0,0 +1,3 @@
+{
+    "Seconds": "5"
+}
diff --git a/resources/Time_Sleep/makebuild b/resources/Time_Sleep/makebuild
new file mode 100644
index 00000000..390b673b
--- /dev/null
+++ b/resources/Time_Sleep/makebuild
@@ -0,0 +1,7 @@
+# This file is autogenerated, do not edit;
+# changes will be undone by the next 'generate' command.
+
+.PHONY: build
+build:
+	cfn generate
+	env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -tags="$(TAGS)" -o bin/handler cmd/main.go
diff --git a/resources/Time_Sleep/resource-role-prod.yaml b/resources/Time_Sleep/resource-role-prod.yaml
new file mode 100644
index 00000000..90c2d7fd
--- /dev/null
+++ b/resources/Time_Sleep/resource-role-prod.yaml
@@ -0,0 +1,33 @@
+AWSTemplateFormatVersion: "2010-09-09"
+Description: >
+  This CloudFormation template creates a role assumed by CloudFormation
+  during CRUDL operations to mutate resources on behalf of the customer.
+
+Resources:
+  ExecutionRole:
+    Type: AWS::IAM::Role
+    Properties:
+      MaxSessionDuration: 8400
+      AssumeRolePolicyDocument:
+        Version: '2012-10-17'
+        Statement:
+          - Effect: Allow
+            Principal:
+              Service: resources.cloudformation.amazonaws.com
+            Action: sts:AssumeRole
+      Path: "/"
+      Policies:
+        - PolicyName: ResourceTypePolicy
+          PolicyDocument:
+            Version: '2012-10-17'
+            Statement:
+              - Effect: Allow
+                Action:
+                - "ssm:DeleteParameter"
+                - "ssm:GetParameter"
+                - "ssm:PutParameter"
+                Resource: !Sub "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/CloudFormation/AwsCommunity/Time/Sleep/*"
+Outputs:
+  ExecutionRoleArn:
+    Value:
+      Fn::GetAtt: ExecutionRole.Arn
diff --git a/resources/Time_Sleep/resource-role.yaml b/resources/Time_Sleep/resource-role.yaml
new file mode 100644
index 00000000..7d3286e9
--- /dev/null
+++ b/resources/Time_Sleep/resource-role.yaml
@@ -0,0 +1,40 @@
+AWSTemplateFormatVersion: "2010-09-09"
+Description: >
+  This CloudFormation template creates a role assumed by CloudFormation
+  during CRUDL operations to mutate resources on behalf of the customer.
+
+Resources:
+  ExecutionRole:
+    Type: AWS::IAM::Role
+    Properties:
+      MaxSessionDuration: 8400
+      AssumeRolePolicyDocument:
+        Version: '2012-10-17'
+        Statement:
+          - Effect: Allow
+            Principal:
+              Service: resources.cloudformation.amazonaws.com
+            Action: sts:AssumeRole
+            Condition:
+              StringEquals:
+                aws:SourceAccount:
+                  Ref: AWS::AccountId
+              StringLike:
+                aws:SourceArn:
+                  Fn::Sub: arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:type/resource/AwsCommunity-Time-Sleep/*
+      Path: "/"
+      Policies:
+        - PolicyName: ResourceTypePolicy
+          PolicyDocument:
+            Version: '2012-10-17'
+            Statement:
+              - Effect: Allow
+                Action:
+                - "ssm:DeleteParameter"
+                - "ssm:GetParameter"
+                - "ssm:PutParameter"
+                Resource: "*"
+Outputs:
+  ExecutionRoleArn:
+    Value:
+      Fn::GetAtt: ExecutionRole.Arn
diff --git a/resources/Time_Sleep/template.yml b/resources/Time_Sleep/template.yml
new file mode 100644
index 00000000..a6201843
--- /dev/null
+++ b/resources/Time_Sleep/template.yml
@@ -0,0 +1,27 @@
+AWSTemplateFormatVersion: "2010-09-09"
+Transform: AWS::Serverless-2016-10-31
+Description: AWS SAM template for the AwsCommunity::Time::Sleep resource type
+
+Globals:
+  Function:
+    Timeout: 180  # docker start-up times can be long for SAM CLI
+    MemorySize: 256
+
+Resources:
+  TypeFunction:
+    Type: AWS::Serverless::Function
+    Properties:
+      Handler: handler
+      Runtime: go1.x
+      CodeUri: bin/
+
+  TestEntrypoint:
+    Type: AWS::Serverless::Function
+    Properties:
+      Handler: handler
+      Runtime: go1.x
+      CodeUri: bin/
+      Environment: 
+        Variables: 
+          MODE: Test
+
diff --git a/resources/Time_Static/resource-role-prod.yaml b/resources/Time_Static/resource-role-prod.yaml
index 852f2007..b229a37c 100644
--- a/resources/Time_Static/resource-role-prod.yaml
+++ b/resources/Time_Static/resource-role-prod.yaml
@@ -26,7 +26,7 @@ Resources:
                 - "ssm:DeleteParameter"
                 - "ssm:GetParameter"
                 - "ssm:PutParameter"
-                Resource: "*"
+                Resource: !Sub "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/CloudFormation/AwsCommunity/Time/Static/*"
 Outputs:
   ExecutionRoleArn:
     Value:
diff --git a/resources/Time_Static/resource-role.yaml b/resources/Time_Static/resource-role.yaml
index 0c96ca5f..613458f1 100644
--- a/resources/Time_Static/resource-role.yaml
+++ b/resources/Time_Static/resource-role.yaml
@@ -33,7 +33,7 @@ Resources:
                 - "ssm:DeleteParameter"
                 - "ssm:GetParameter"
                 - "ssm:PutParameter"
-                Resource: "*"
+                Resource: !Sub "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/CloudFormation/AwsCommunity/Time/Static/*"
 Outputs:
   ExecutionRoleArn:
     Value:
diff --git a/resources/alpha-buildspec-go.yml b/resources/alpha-buildspec-go.yml
index c4fc8bc8..f68c7c97 100644
--- a/resources/alpha-buildspec-go.yml
+++ b/resources/alpha-buildspec-go.yml
@@ -8,7 +8,7 @@ phases:
       - echo Entered the install phase...
       - echo About to build $RESOURCE_PATH
       - export PATH="/usr/local/bin:$PATH"
-      - ENTRY_PATH=$(python scripts/entry.py $RESOURCE_PATH/.rpdk-config)
+      - ENTRY_PATH=$(python scripts/resourceName.py $RESOURCE_PATH/.rpdk-config)
       - /usr/local/bin/dockerd-entrypoint.sh
       - cat /var/log/docker.log
       - echo ENTRY_PATH is $ENTRY_PATH
diff --git a/resources/beta-buildspec-go.yml b/resources/beta-buildspec-go.yml
index 89fe1bc7..732052ac 100644
--- a/resources/beta-buildspec-go.yml
+++ b/resources/beta-buildspec-go.yml
@@ -8,7 +8,7 @@ phases:
       - echo Entered the install phase...
       - echo About to build $RESOURCE_PATH
       - export PATH="/usr/local/bin:$PATH"
-      - ENTRY_PATH=$(python scripts/entry.py $RESOURCE_PATH/.rpdk-config)
+      - ENTRY_PATH=$(python scripts/resourceName.py $RESOURCE_PATH/.rpdk-config)
       - echo ENTRY_PATH is $ENTRY_PATH
       - BUILD_FILE_NAME=$(cat ENTRY_PATH | sed s/_/-/g)
       - BUILD_FILE_NAME="${BUILD_FILE_NAME}.zip"
diff --git a/resources/prod-buildspec-go.yml b/resources/prod-buildspec-go.yml
index 353c7796..d4bd3720 100644
--- a/resources/prod-buildspec-go.yml
+++ b/resources/prod-buildspec-go.yml
@@ -10,7 +10,7 @@ phases:
       - export PATH="/usr/local/bin:$PATH"
       - /usr/local/bin/dockerd-entrypoint.sh
       - cat /var/log/docker.log
-      - ENTRY_PATH=$(python scripts/entry.py $RESOURCE_PATH/.rpdk-config)
+      - ENTRY_PATH=$(python scripts/resourceName.py $RESOURCE_PATH/.rpdk-config)
       - echo ENTRY_PATH is $ENTRY_PATH
       - TYPE_NAME=$(python scripts/type_name.py $RESOURCE_PATH/.rpdk-config)
       - echo TYPE_NAME is $TYPE_NAME
diff --git a/scripts/resourceName.py b/scripts/resourceName.py
new file mode 100644
index 00000000..d8155a0b
--- /dev/null
+++ b/scripts/resourceName.py
@@ -0,0 +1,15 @@
+"Gets the name of the folder with the handler code"
+
+import json
+import sys
+
+def main(rpdk_path):
+    "Get the source folder from rpdk config"
+
+    with open(rpdk_path) as f:
+        obj = json.load(f)
+        entrypoint = obj["typeName"]
+        print(entrypoint.replace("::", "_").lower())
+
+if __name__ == "__main__":
+    main(sys.argv[1])


Test patch:
diff --git a/resources/Time_Sleep/test/integ.yml b/resources/Time_Sleep/test/integ.yml
new file mode 100644
index 00000000..2fa53e09
--- /dev/null
+++ b/resources/Time_Sleep/test/integ.yml
@@ -0,0 +1,6 @@
+Resources:
+  Time:
+    Type: AwsCommunity::Time::Sleep
+    Properties:
+      Seconds: 10
+
diff --git a/resources/Time_Sleep/test/setup.yml b/resources/Time_Sleep/test/setup.yml
new file mode 100644
index 00000000..a33c0d47
--- /dev/null
+++ b/resources/Time_Sleep/test/setup.yml
@@ -0,0 +1,13 @@
+AWSTemplateFormatVersion: '2010-09-09'
+Description: 'CloudFormation exports'
+ 
+Conditions:
+  HasNot: !Equals [ 'true', 'false' ]
+ 
+# dummy (null) resource, never created
+Resources:
+  NullResource:
+    Type: 'AWS::CloudFormation::CustomResource'
+    Condition: HasNot
+    Properties:
+      ServiceToken: "null"