provider/aws: Added Lambda Environment configuration

This commit is contained in:
Ninir 2016-11-19 00:48:18 +01:00
parent b1f974be39
commit ab9059564c
3 changed files with 270 additions and 36 deletions

View File

@ -34,112 +34,132 @@ func resourceAwsLambdaFunction() *schema.Resource {
},
Schema: map[string]*schema.Schema{
"filename": &schema.Schema{
"filename": {
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"s3_bucket", "s3_key", "s3_object_version"},
},
"s3_bucket": &schema.Schema{
"s3_bucket": {
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"filename"},
},
"s3_key": &schema.Schema{
"s3_key": {
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"filename"},
},
"s3_object_version": &schema.Schema{
"s3_object_version": {
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"filename"},
},
"description": &schema.Schema{
"description": {
Type: schema.TypeString,
Optional: true,
},
"function_name": &schema.Schema{
"function_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"handler": &schema.Schema{
"handler": {
Type: schema.TypeString,
Required: true,
},
"memory_size": &schema.Schema{
"memory_size": {
Type: schema.TypeInt,
Optional: true,
Default: 128,
},
"role": &schema.Schema{
"role": {
Type: schema.TypeString,
Required: true,
},
"runtime": &schema.Schema{
"runtime": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: "nodejs",
},
"timeout": &schema.Schema{
"timeout": {
Type: schema.TypeInt,
Optional: true,
Default: 3,
},
"publish": &schema.Schema{
"publish": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"version": &schema.Schema{
"version": {
Type: schema.TypeString,
Computed: true,
},
"vpc_config": &schema.Schema{
"vpc_config": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"subnet_ids": &schema.Schema{
"subnet_ids": {
Type: schema.TypeSet,
Required: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"security_group_ids": &schema.Schema{
"security_group_ids": {
Type: schema.TypeSet,
Required: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"vpc_id": &schema.Schema{
"vpc_id": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"arn": &schema.Schema{
"arn": {
Type: schema.TypeString,
Computed: true,
},
"qualified_arn": &schema.Schema{
"qualified_arn": {
Type: schema.TypeString,
Computed: true,
},
"last_modified": &schema.Schema{
"last_modified": {
Type: schema.TypeString,
Computed: true,
},
"source_code_hash": &schema.Schema{
"source_code_hash": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"environment": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"variables": {
Type: schema.TypeMap,
Optional: true,
Elem: schema.TypeString,
},
},
},
},
"kms_key_arn": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validateArn,
},
},
}
}
@ -220,6 +240,26 @@ func resourceAwsLambdaFunctionCreate(d *schema.ResourceData, meta interface{}) e
}
}
if v, ok := d.GetOk("environment"); ok {
environments := v.([]interface{})
environment, ok := environments[0].(map[string]interface{})
if !ok {
return errors.New("At least one field is expected inside environment")
}
if environmentVariables, ok := environment["variables"]; ok {
variables := readEnvironmentVariables(environmentVariables.(map[string]interface{}))
params.Environment = &lambda.Environment{
Variables: aws.StringMap(variables),
}
}
}
if v, ok := d.GetOk("kms_key_arn"); ok {
params.KMSKeyArn = aws.String(v.(string))
}
// IAM profiles can take ~10 seconds to propagate in AWS:
// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role-console
// Error creating Lambda function: InvalidParameterValueException: The role defined for the task cannot be assumed by Lambda.
@ -282,6 +322,7 @@ func resourceAwsLambdaFunctionRead(d *schema.ResourceData, meta interface{}) err
d.Set("role", function.Role)
d.Set("runtime", function.Runtime)
d.Set("timeout", function.Timeout)
d.Set("kms_key_arn", function.KMSKeyArn)
if config := flattenLambdaVpcConfigResponse(function.VpcConfig); len(config) > 0 {
log.Printf("[INFO] Setting Lambda %s VPC config %#v from API", d.Id(), config)
err := d.Set("vpc_config", config)
@ -429,6 +470,28 @@ func resourceAwsLambdaFunctionUpdate(d *schema.ResourceData, meta interface{}) e
configReq.Timeout = aws.Int64(int64(d.Get("timeout").(int)))
configUpdate = true
}
if d.HasChange("kms_key_arn") {
configReq.KMSKeyArn = aws.String(d.Get("kms_key_arn").(string))
configUpdate = true
}
if d.HasChange("environment") {
if v, ok := d.GetOk("environment"); ok {
environments := v.([]interface{})
environment, ok := environments[0].(map[string]interface{})
if !ok {
return errors.New("At least one field is expected inside environment")
}
if environmentVariables, ok := environment["variables"]; ok {
variables := readEnvironmentVariables(environmentVariables.(map[string]interface{}))
configReq.Environment = &lambda.Environment{
Variables: aws.StringMap(variables),
}
configUpdate = true
}
}
}
if configUpdate {
log.Printf("[DEBUG] Send Update Lambda Function Configuration request: %#v", configReq)
@ -460,6 +523,15 @@ func loadFileContent(v string) ([]byte, error) {
return fileContent, nil
}
func readEnvironmentVariables(ev map[string]interface{}) map[string]string {
variables := make(map[string]string)
for k, v := range ev {
variables[k] = v.(string)
}
return variables
}
func validateVPCConfig(v interface{}) (map[string]interface{}, error) {
configs := v.([]interface{})
if len(configs) > 1 {

View File

@ -27,7 +27,7 @@ func TestAccAWSLambdaFunction_basic(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckLambdaFunctionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccAWSLambdaConfigBasic(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_test", rName, &conf),
@ -39,6 +39,74 @@ func TestAccAWSLambdaFunction_basic(t *testing.T) {
})
}
func TestAccAWSLambdaFunction_envVariables(t *testing.T) {
var conf lambda.GetFunctionOutput
rName := fmt.Sprintf("tf_test_%s", acctest.RandString(5))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLambdaFunctionDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLambdaConfigEnvVariables(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_test", rName, &conf),
testAccCheckAwsLambdaFunctionName(&conf, rName),
testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, ":"+rName),
resource.TestCheckResourceAttr("aws_lambda_function.lambda_function_test", "environment.0.variables.foo", "bar"),
),
},
{
Config: testAccAWSLambdaConfigEnvVariablesModified(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_test", rName, &conf),
testAccCheckAwsLambdaFunctionName(&conf, rName),
testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, ":"+rName),
resource.TestCheckResourceAttr("aws_lambda_function.lambda_function_test", "environment.0.variables.foo", "baz"),
resource.TestCheckResourceAttr("aws_lambda_function.lambda_function_test", "environment.0.variables.foo1", "bar1"),
),
},
},
})
}
func TestAccAWSLambdaFunction_encryptedEnvVariables(t *testing.T) {
var conf lambda.GetFunctionOutput
rName := fmt.Sprintf("tf_test_%s", acctest.RandString(5))
keyRegex := regexp.MustCompile("^arn:aws:kms:")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLambdaFunctionDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLambdaConfigEncryptedEnvVariables(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_test", rName, &conf),
testAccCheckAwsLambdaFunctionName(&conf, rName),
testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, ":"+rName),
resource.TestCheckResourceAttr("aws_lambda_function.lambda_function_test", "environment.0.variables.foo", "bar"),
resource.TestMatchResourceAttr("aws_lambda_function.lambda_function_test", "kms_key_arn", keyRegex),
),
},
{
Config: testAccAWSLambdaConfigEncryptedEnvVariablesModified(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_test", rName, &conf),
testAccCheckAwsLambdaFunctionName(&conf, rName),
testAccCheckAwsLambdaFunctionArnHasSuffix(&conf, ":"+rName),
resource.TestCheckResourceAttr("aws_lambda_function.lambda_function_test", "environment.0.variables.foo", "bar"),
resource.TestCheckResourceAttr("aws_lambda_function.lambda_function_test", "kms_key_arn", ""),
),
},
},
})
}
func TestAccAWSLambdaFunction_versioned(t *testing.T) {
var conf lambda.GetFunctionOutput
@ -49,7 +117,7 @@ func TestAccAWSLambdaFunction_versioned(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckLambdaFunctionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccAWSLambdaConfigVersioned(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_test", rName, &conf),
@ -74,7 +142,7 @@ func TestAccAWSLambdaFunction_VPC(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckLambdaFunctionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccAWSLambdaConfigWithVPC(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_test", rName, &conf),
@ -100,7 +168,7 @@ func TestAccAWSLambdaFunction_s3(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckLambdaFunctionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
Config: testAccAWSLambdaConfigS3(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsLambdaFunctionExists("aws_lambda_function.lambda_function_s3test", rName, &conf),
@ -127,7 +195,7 @@ func TestAccAWSLambdaFunction_localUpdate(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckLambdaFunctionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
PreConfig: func() {
testAccCreateZipFromFiles(map[string]string{"test-fixtures/lambda_func.js": "lambda.js"}, zipFile)
},
@ -139,7 +207,7 @@ func TestAccAWSLambdaFunction_localUpdate(t *testing.T) {
testAccCheckAwsLambdaSourceCodeHash(&conf, "8DPiX+G1l2LQ8hjBkwRchQFf1TSCEvPrYGRKlM9UoyY="),
),
},
resource.TestStep{
{
PreConfig: func() {
testAccCreateZipFromFiles(map[string]string{"test-fixtures/lambda_func_modified.js": "lambda.js"}, zipFile)
},
@ -175,7 +243,7 @@ func TestAccAWSLambdaFunction_localUpdate_nameOnly(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckLambdaFunctionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
PreConfig: func() {
testAccCreateZipFromFiles(map[string]string{"test-fixtures/lambda_func.js": "lambda.js"}, zipFile)
},
@ -187,7 +255,7 @@ func TestAccAWSLambdaFunction_localUpdate_nameOnly(t *testing.T) {
testAccCheckAwsLambdaSourceCodeHash(&conf, "8DPiX+G1l2LQ8hjBkwRchQFf1TSCEvPrYGRKlM9UoyY="),
),
},
resource.TestStep{
{
PreConfig: func() {
testAccCreateZipFromFiles(map[string]string{"test-fixtures/lambda_func_modified.js": "lambda.js"}, updatedZipFile)
},
@ -220,7 +288,7 @@ func TestAccAWSLambdaFunction_s3Update(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckLambdaFunctionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
PreConfig: func() {
// Upload 1st version
testAccCreateZipFromFiles(map[string]string{"test-fixtures/lambda_func.js": "lambda.js"}, zipFile)
@ -233,7 +301,7 @@ func TestAccAWSLambdaFunction_s3Update(t *testing.T) {
testAccCheckAwsLambdaSourceCodeHash(&conf, "8DPiX+G1l2LQ8hjBkwRchQFf1TSCEvPrYGRKlM9UoyY="),
),
},
resource.TestStep{
{
ExpectNonEmptyPlan: true,
PreConfig: func() {
// Upload 2nd version
@ -274,7 +342,7 @@ func TestAccAWSLambdaFunction_s3Update_unversioned(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckLambdaFunctionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
{
PreConfig: func() {
// Upload 1st version
testAccCreateZipFromFiles(map[string]string{"test-fixtures/lambda_func.js": "lambda.js"}, zipFile)
@ -287,7 +355,7 @@ func TestAccAWSLambdaFunction_s3Update_unversioned(t *testing.T) {
testAccCheckAwsLambdaSourceCodeHash(&conf, "8DPiX+G1l2LQ8hjBkwRchQFf1TSCEvPrYGRKlM9UoyY="),
),
},
resource.TestStep{
{
PreConfig: func() {
// Upload 2nd version
testAccCreateZipFromFiles(map[string]string{"test-fixtures/lambda_func_modified.js": "lambda.js"}, zipFile)
@ -538,6 +606,93 @@ resource "aws_lambda_function" "lambda_function_test" {
`, rName)
}
func testAccAWSLambdaConfigEnvVariables(rName string) string {
return fmt.Sprintf(baseAccAWSLambdaConfig+`
resource "aws_lambda_function" "lambda_function_test" {
filename = "test-fixtures/lambdatest.zip"
function_name = "%s"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "exports.example"
environment {
variables = {
foo = "bar"
}
}
}
`, rName)
}
func testAccAWSLambdaConfigEnvVariablesModified(rName string) string {
return fmt.Sprintf(baseAccAWSLambdaConfig+`
resource "aws_lambda_function" "lambda_function_test" {
filename = "test-fixtures/lambdatest.zip"
function_name = "%s"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "exports.example"
environment {
variables = {
foo = "baz"
foo1 = "bar1"
}
}
}
`, rName)
}
func testAccAWSLambdaConfigEncryptedEnvVariables(rName string) string {
return fmt.Sprintf(baseAccAWSLambdaConfig+`
resource "aws_kms_key" "foo" {
description = "Terraform acc test %s"
policy = <<POLICY
{
"Version": "2012-10-17",
"Id": "kms-tf-1",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "kms:*",
"Resource": "*"
}
]
}
POLICY
}
resource "aws_lambda_function" "lambda_function_test" {
filename = "test-fixtures/lambdatest.zip"
function_name = "%s"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "exports.example"
kms_key_arn = "${aws_kms_key.foo.arn}"
environment {
variables = {
foo = "bar"
}
}
}
`, rName, rName)
}
func testAccAWSLambdaConfigEncryptedEnvVariablesModified(rName string) string {
return fmt.Sprintf(baseAccAWSLambdaConfig+`
resource "aws_lambda_function" "lambda_function_test" {
filename = "test-fixtures/lambdatest.zip"
function_name = "%s"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "exports.example"
environment {
variables = {
foo = "bar"
}
}
}
`, rName)
}
func testAccAWSLambdaConfigVersioned(rName string) string {
return fmt.Sprintf(baseAccAWSLambdaConfig+`
resource "aws_lambda_function" "lambda_function_test" {

View File

@ -58,6 +58,8 @@ resource "aws_lambda_function" "test_lambda" {
* `timeout` - (Optional) The amount of time your Lambda Function has to run in seconds. Defaults to `3`. See [Limits][5]
* `publish` - (Optional) Whether to publish creation/change as new Lambda Function Version. Defaults to `false`.
* `vpc_config` - (Optional) Provide this to allow your function to access your VPC. Fields documented below. See [Lambda in VPC][7]
* `environment` - (Optional) The Lambda environment's configuration settings. Fields documented below.
* `kms_key_arn` - (Optional) The ARN for the KMS encryption key.
* `source_code_hash` - (Optional) Used to trigger updates. This is only useful in conjunction with `filename`.
The only useful value is `${base64sha256(file("file.zip"))}`.
@ -68,15 +70,20 @@ resource "aws_lambda_function" "test_lambda" {
~> **NOTE:** if both `subnet_ids` and `security_group_ids` are empty then vpc_config is considered to be empty or unset.
For **environment** the following attributes are supported:
* `variables` - (Optional) A map that defines environment variables for the Lambda function.
## Attributes Reference
* `arn` - The Amazon Resource Name (ARN) identifying your Lambda Function.
* `qualified_arn` - The Amazon Resource Name (ARN) identifying your Lambda Function Version
(if versioning is enabled via `publish = true`).
* `version` - Latest published version of your Lambda Function
* `version` - Latest published version of your Lambda Function.
* `last_modified` - The date this resource was last modified.
* `kms_key_arn` - (Optional) The ARN for the KMS encryption key.
* `source_code_hash` - Base64-encoded representation of raw SHA-256 sum of the zip file
provided either via `filename` or `s3_*` parameters
provided either via `filename` or `s3_*` parameters.
[1]: https://docs.aws.amazon.com/lambda/latest/dg/welcome.html
[2]: https://docs.aws.amazon.com/lambda/latest/dg/walkthrough-s3-events-adminuser-create-test-function-create-function.html
@ -92,4 +99,4 @@ Lambda Functions can be imported using the `function_name`, e.g.
```
$ terraform import aws_lambda_function.tesr_lambda my_test_lambda_function
```
```