Cfn.response in lambda AWS


(steven) #1

Hello,

I am trying to get a lambda function to send a message back to a cloudformation deployment inside AWS by using custom resources. The flow of execution should be:

  1. cloudformation starts deploying a cloudformation and executes it
  2. lambda performs a task and then sends a response to cloudformation to indicate the task is completed
  3. cloudformation deploys remaining resources

I am trying to leverage these package(s) to do the job:

my code looks like this:

package main

import (
	"context"
	"fmt"

	"github.com/aws/aws-lambda-go/cfn"

	"github.com/aws/aws-lambda-go/lambda"
)

// Event is the struct that the function will receive from cloudformation
// The structure of this should mimic that the of accompanying event (sampleevent.json)
type Event struct {
	StackID            string `json:"StackId"`
	ResponseURL        string `json:"ResponseURL"`
	ResourceProperties struct {
		SourceBucket string   `json:"SourceBucket"`
		SourcePrefix string   `json:"SourcePrefix"`
		Bucket       string   `json:"Bucket"`
		List         []string `json:"List"`
	} `json:"ResourceProperties"`
	RequestType       string `json:"RequestType"`
	ResourceType      string `json:"ResourceType"`
	RequestID         string `json:"RequestId"`
	LogicalResourceID string `json:"LogicalResourceId"`
}

func handler(ctx context.Context, event Event) {

	r := cfn.NewResponse(&cfn.Event{
		RequestID:         event.RequestID,
		ResponseURL:       event.ResponseURL,
		LogicalResourceID: event.LogicalResourceID,
		StackID:           event.StackID,
	})

	const StatusSuccess cfn.StatusType = "SUCCESS"

	er := r.Send
	if er != nil {
		fmt.Println(er)
	}

}

func main() {
	lambda.Start(handler)
}

and the event json looks like this:

{
    "StackId": "arn:aws:cloudformation:us-west-2:EXAMPLE/stack-name/guid",
    "ResponseURL": "http://pre-signed-S3-url-for-response",
    "ResourceProperties": {
      "SourceBucket": "iamyourfather22",
      "SourcePrefix": "WebApplication/1_StaticWebHosting",
      "Bucket": "wildrydes-ap-southeast-2",
      "List": [
        "1",
        "2",
        "3"
      ]
    },
    "RequestType": "Create",
    "ResourceType": "Custom::TestResource",
    "RequestId": "unique id for this create request",
    "LogicalResourceId": "MyTestResource"
  } 

when the .send runs i can see it gets the correct URL but keeps on missing the success message. When lambda executes it logs invalid response object “” is not a valid ResponseStatus.

How can i inject the ResponseStatus into the receiver type so it gets sent with the .send method?

If terminology is terribly wrong i apologise i am 1 month into GO. Loving it, but it is a steep learning curve.


(Johan Dahl) #2

The signature of your handler is not correct. It should be one of:

  • func ()

  • func () error

  • func (TIn), error

  • func () (TOut, error)

  • func (context.Context) error

  • func (context.Context, TIn) error

  • func (context.Context) (TOut, error)

  • func (context.Context, TIn) (TOut, error)

See https://docs.aws.amazon.com/lambda/latest/dg/go-programming-model-handler-types.html


(steven) #3

Thanks for your help johandalabacka. Managed to get it working by setting my handler too:
func handler(ctx context.Context, event cfn.Event)

forgot the todd mcleod number 1 lesson. go is all about type. I was referencing the wrong event type and it threw me off.

Cheers,
Steve