diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 7439ff2..4097414 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -46,6 +46,8 @@ jobs: --src test/src \ --endpoint http://127.0.0.1:4566 \ --tfstate test/terraform.tfstate \ + --prefixed-tfstate "prefix1_=test/terraform_1.tfstate" \ + --prefixed-tfstate "prefix2_=test/terraform_2.tfstate" \ --log-level debug env: FUNCTION_NAME: hello @@ -62,6 +64,8 @@ jobs: --src test/src \ --endpoint http://127.0.0.1:4566 \ --tfstate test/terraform.tfstate \ + --prefixed-tfstate "prefix1_=test/terraform_1.tfstate" \ + --prefixed-tfstate "prefix2_=test/terraform_2.tfstate" \ --log-level debug env: FUNCTION_NAME: hello diff --git a/README.md b/README.md index 3be7eba..459f25b 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,8 @@ Flags: --profile="" AWS credential profile name --region="" AWS region --tfstate="" URL to terraform.tfstate + --prefixed-tfstate=PREFIX=URL ... + key value pair of the prefix for template function name and URL to terraform.tfstate --endpoint="" AWS API Lambda Endpoint --envfile=ENVFILE ... environment files --ext-str=EXT-STR ... external string values for Jsonnet @@ -200,10 +202,12 @@ Flags: --profile="" AWS credential profile name --region="" AWS region --tfstate="" URL to terraform.tfstate + --prefixed-tfstate=PREFIX=URL ... + key value pair of the prefix for template function name and URL to terraform.tfstate --endpoint="" AWS API Lambda Endpoint --envfile=ENVFILE ... environment files --src="." function zip archive or src dir - --exclude-file=".lambdaignore" + --exclude-file=".lambdaignore" exclude file --dry-run dry run --publish publish function @@ -435,6 +439,28 @@ data "aws_iam_role" "lambda" { } ``` +Likewise, if you have AWS resource definitions spread across multiple tfstate files, you can utilize `--prefixed-tfstate` option: + +e.g. +```shell +lambroll --prefixed-tfstate="my_first_=s3://my-bucket/first.tfstate" --prefixed-tfstate="my_second_=s3://my-bucket/second.tfstate" ... +``` + +which then exposes additional template functions available like: + +```json +{ + "Description": "hello function", + "Environment": { + "Variables": { + "FIRST_VALUE": "{{ my_first_tfstate `data.aws_iam_role.lambda.arn` }}", + "SECOND_VALUE": "{{ my_second_tfstate `data.aws_iam_role.lambda.arn` }}" + } + }, + "rest of the parameters": "..." +} +``` + ### Jsonnet support for function configuration lambroll also can read function.jsonnet as [Jsonnet](https://jsonnet.org/) format. diff --git a/cmd/lambroll/main.go b/cmd/lambroll/main.go index bdc69c3..a7ee5ae 100644 --- a/cmd/lambroll/main.go +++ b/cmd/lambroll/main.go @@ -30,13 +30,14 @@ func _main() int { colorOpt := kingpin.Flag("color", "enable colored output").Default(colorDefault).Bool() opt := lambroll.Option{ - Profile: kingpin.Flag("profile", "AWS credential profile name").Default(os.Getenv("AWS_PROFILE")).String(), - Region: kingpin.Flag("region", "AWS region").Default(os.Getenv("AWS_REGION")).String(), - TFState: kingpin.Flag("tfstate", "URL to terraform.tfstate").Default("").String(), - Endpoint: kingpin.Flag("endpoint", "AWS API Lambda Endpoint").Default("").String(), - Envfile: kingpin.Flag("envfile", "environment files").Strings(), - ExtStr: kingpin.Flag("ext-str", "external string values for Jsonnet").StringMap(), - ExtCode: kingpin.Flag("ext-code", "external code values for Jsonnet").StringMap(), + Profile: kingpin.Flag("profile", "AWS credential profile name").Default(os.Getenv("AWS_PROFILE")).String(), + Region: kingpin.Flag("region", "AWS region").Default(os.Getenv("AWS_REGION")).String(), + TFState: kingpin.Flag("tfstate", "URL to terraform.tfstate").Default("").String(), + PrefixedTFState: kingpin.Flag("prefixed-tfstate", "key value pair of the prefix for template function name and URL to terraform.tfstate").PlaceHolder("PREFIX=URL").StringMap(), + Endpoint: kingpin.Flag("endpoint", "AWS API Lambda Endpoint").Default("").String(), + Envfile: kingpin.Flag("envfile", "environment files").Strings(), + ExtStr: kingpin.Flag("ext-str", "external string values for Jsonnet").StringMap(), + ExtCode: kingpin.Flag("ext-code", "external code values for Jsonnet").StringMap(), } init := kingpin.Command("init", "init function.json") diff --git a/function_test.go b/function_test.go index dc62bb3..8df00db 100644 --- a/function_test.go +++ b/function_test.go @@ -11,6 +11,10 @@ func TestLoadFunction(t *testing.T) { path := "test/terraform.tfstate" app, err := New(&Option{ TFState: &path, + PrefixedTFState: &map[string]string{ + "prefix1_": "test/terraform_1.tfstate", + "prefix2_": "test/terraform_2.tfstate", + }, Envfile: &envfiles, ExtStr: &map[string]string{ "Description": "hello function", @@ -39,6 +43,12 @@ func TestLoadFunction(t *testing.T) { if *fn.Environment.Variables["JSON"] != `{"foo":"bar"}` { t.Errorf("unexpected environment %v", fn.Environment.Variables) } + if *fn.Environment.Variables["PREFIXED_TFSTATE_1"] != "arn:aws:iam::123456789012:role/test_lambda_role_1" { + t.Errorf("unexpected environment %v", fn.Environment.Variables) + } + if *fn.Environment.Variables["PREFIXED_TFSTATE_2"] != "arn:aws:iam::123456789012:role/test_lambda_role_2" { + t.Errorf("unexpected environment %v", fn.Environment.Variables) + } if *fn.VpcConfig.SecurityGroupIds[0] != "sg-01a9b01eab0a3c154" { t.Errorf("unexpected SecurityGroupIds %v", fn.VpcConfig.SecurityGroupIds) } diff --git a/lambroll.go b/lambroll.go index 5c52327..095da9c 100644 --- a/lambroll.go +++ b/lambroll.go @@ -5,6 +5,7 @@ import ( "log" "os" "path/filepath" + "text/template" "time" "github.com/aws/aws-sdk-go/aws" @@ -128,6 +129,24 @@ func New(opt *Option) (*App, error) { } loader.Funcs(funcs) } + if opt.PrefixedTFState != nil { + prefixedFuncs := make(template.FuncMap) + for prefix, path := range *opt.PrefixedTFState { + if prefix == "" { + return nil, errors.New("--prefixed-tfstate option cannot have empty key") + } + + funcs, err := tfstate.FuncMap(path) + if err != nil { + return nil, err + } + + for name, f := range funcs { + prefixedFuncs[prefix+name] = f + } + } + loader.Funcs(prefixedFuncs) + } app := &App{ sess: sess, @@ -210,6 +229,11 @@ func fillDefaultValues(fn *Function) { Mode: aws.String("PassThrough"), } } + if fn.EphemeralStorage == nil { + fn.EphemeralStorage = &lambda.EphemeralStorage{ + Size: aws.Int64(512), + } + } if fn.Timeout == nil { fn.Timeout = aws.Int64(3) } diff --git a/option.go b/option.go index cf2795b..462db9a 100644 --- a/option.go +++ b/option.go @@ -2,11 +2,12 @@ package lambroll // Option represents common option. type Option struct { - Region *string - Profile *string - TFState *string - Endpoint *string - Envfile *[]string - ExtStr *map[string]string - ExtCode *map[string]string + Region *string + Profile *string + TFState *string + PrefixedTFState *map[string]string + Endpoint *string + Envfile *[]string + ExtStr *map[string]string + ExtCode *map[string]string } diff --git a/test/function.json b/test/function.json index c15f0e7..33e4c10 100644 --- a/test/function.json +++ b/test/function.json @@ -9,7 +9,9 @@ }, "Environment": { "Variables": { - "JSON": "{{ env `JSON` | json_escape }}" + "JSON": "{{ env `JSON` | json_escape }}", + "PREFIXED_TFSTATE_1": "{{ prefix1_tfstate `data.aws_iam_role.lambda.arn` }}", + "PREFIXED_TFSTATE_2": "{{ prefix2_tfstate `data.aws_iam_role.lambda.arn` }}" } }, "FunctionName": "{{ must_env `FUNCTION_NAME` }}", diff --git a/test/function.jsonnet b/test/function.jsonnet index 6c788a7..8d27dd5 100644 --- a/test/function.jsonnet +++ b/test/function.jsonnet @@ -10,6 +10,8 @@ Environment: { Variables: { JSON: '{{ env `JSON` | json_escape }}', + PREFIXED_TFSTATE_1: '{{ prefix1_tfstate `data.aws_iam_role.lambda.arn` }}', + PREFIXED_TFSTATE_2: '{{ prefix2_tfstate `data.aws_iam_role.lambda.arn` }}' }, }, FunctionName: '{{ must_env `FUNCTION_NAME` }}', diff --git a/test/terraform_1.tfstate b/test/terraform_1.tfstate new file mode 100644 index 0000000..504409f --- /dev/null +++ b/test/terraform_1.tfstate @@ -0,0 +1,34 @@ +{ + "version": 4, + "terraform_version": "0.12.12", + "serial": 1, + "outputs": {}, + "resources": [ + { + "mode": "data", + "type": "aws_iam_role", + "name": "lambda", + "provider": "provider.aws", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws:iam::123456789012:role/test_lambda_role_1", + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}", + "assume_role_policy_document": null, + "create_date": "2019-11-06T03:22:03Z", + "description": "Allows Lambda functions to call AWS services on your behalf.", + "id": "test_lambda_role", + "max_session_duration": 3600, + "name": "test_lambda_role", + "path": "/", + "permissions_boundary": "", + "role_id": null, + "role_name": null, + "tags": {} + } + } + ] + } + ] +} diff --git a/test/terraform_2.tfstate b/test/terraform_2.tfstate new file mode 100644 index 0000000..151f2c2 --- /dev/null +++ b/test/terraform_2.tfstate @@ -0,0 +1,34 @@ +{ + "version": 4, + "terraform_version": "0.12.12", + "serial": 1, + "outputs": {}, + "resources": [ + { + "mode": "data", + "type": "aws_iam_role", + "name": "lambda", + "provider": "provider.aws", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws:iam::123456789012:role/test_lambda_role_2", + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}", + "assume_role_policy_document": null, + "create_date": "2019-11-06T03:22:03Z", + "description": "Allows Lambda functions to call AWS services on your behalf.", + "id": "test_lambda_role", + "max_session_duration": 3600, + "name": "test_lambda_role", + "path": "/", + "permissions_boundary": "", + "role_id": null, + "role_name": null, + "tags": {} + } + } + ] + } + ] +}