Merge pull request #4826 from TimeIncOSS/f-aws-lambda-permission

[WIP] provider/aws: Add aws_lambda_permission
This commit is contained in:
Paul Stack 2016-02-17 13:31:29 +00:00
commit cd28433c2a
8 changed files with 1474 additions and 1 deletions

View File

@ -174,6 +174,7 @@ func Provider() terraform.ResourceProvider {
"aws_lambda_function": resourceAwsLambdaFunction(),
"aws_lambda_event_source_mapping": resourceAwsLambdaEventSourceMapping(),
"aws_lambda_alias": resourceAwsLambdaAlias(),
"aws_lambda_permission": resourceAwsLambdaPermission(),
"aws_launch_configuration": resourceAwsLaunchConfiguration(),
"aws_lb_cookie_stickiness_policy": resourceAwsLBCookieStickinessPolicy(),
"aws_main_route_table_association": resourceAwsMainRouteTableAssociation(),

View File

@ -0,0 +1,343 @@
package aws
import (
"encoding/json"
"fmt"
"log"
"regexp"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/lambda"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)
var LambdaFunctionRegexp = `^(arn:aws:lambda:)?([a-z]{2}-[a-z]+-\d{1}:)?(\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\$LATEST|[a-zA-Z0-9-_]+))?$`
func resourceAwsLambdaPermission() *schema.Resource {
return &schema.Resource{
Create: resourceAwsLambdaPermissionCreate,
Read: resourceAwsLambdaPermissionRead,
Delete: resourceAwsLambdaPermissionDelete,
Schema: map[string]*schema.Schema{
"action": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateLambdaPermissionAction,
},
"function_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateLambdaFunctionName,
},
"principal": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"qualifier": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validateLambdaQualifier,
},
"source_account": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validateAwsAccountId,
},
"source_arn": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validateArn,
},
"statement_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validatePolicyStatementId,
},
},
}
}
func resourceAwsLambdaPermissionCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lambdaconn
functionName := d.Get("function_name").(string)
// There is a bug in the API (reported and acknowledged by AWS)
// which causes some permissions to be ignored when API calls are sent in parallel
// We work around this bug via mutex
awsMutexKV.Lock(functionName)
defer awsMutexKV.Unlock(functionName)
input := lambda.AddPermissionInput{
Action: aws.String(d.Get("action").(string)),
FunctionName: aws.String(functionName),
Principal: aws.String(d.Get("principal").(string)),
StatementId: aws.String(d.Get("statement_id").(string)),
}
if v, ok := d.GetOk("qualifier"); ok {
input.Qualifier = aws.String(v.(string))
}
if v, ok := d.GetOk("source_account"); ok {
input.SourceAccount = aws.String(v.(string))
}
if v, ok := d.GetOk("source_arn"); ok {
input.SourceArn = aws.String(v.(string))
}
log.Printf("[DEBUG] Adding new Lambda permission: %s", input)
var out *lambda.AddPermissionOutput
err := resource.Retry(1*time.Minute, func() error {
var err error
out, err = conn.AddPermission(&input)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
// IAM is eventually consistent :/
if awsErr.Code() == "ResourceConflictException" {
return fmt.Errorf("[WARN] Error adding new Lambda Permission for %s, retrying: %s",
*input.FunctionName, err)
}
}
return resource.RetryError{Err: err}
}
return nil
})
if err != nil {
return err
}
log.Printf("[DEBUG] Created new Lambda permission: %s", *out.Statement)
d.SetId(d.Get("statement_id").(string))
err = resource.Retry(5*time.Minute, func() error {
// IAM is eventually cosistent :/
err := resourceAwsLambdaPermissionRead(d, meta)
if err != nil {
if strings.HasPrefix(err.Error(), "Error reading Lambda policy: ResourceNotFoundException") {
return fmt.Errorf("[WARN] Error reading newly created Lambda Permission for %s, retrying: %s",
*input.FunctionName, err)
}
if strings.HasPrefix(err.Error(), "Failed to find statement \""+d.Id()) {
return fmt.Errorf("[WARN] Error reading newly created Lambda Permission statement for %s, retrying: %s",
*input.FunctionName, err)
}
log.Printf("[ERROR] An actual error occured when expecting Lambda policy to be there: %s", err)
return resource.RetryError{Err: err}
}
return nil
})
return err
}
func resourceAwsLambdaPermissionRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lambdaconn
input := lambda.GetPolicyInput{
FunctionName: aws.String(d.Get("function_name").(string)),
}
if v, ok := d.GetOk("qualifier"); ok {
input.Qualifier = aws.String(v.(string))
}
log.Printf("[DEBUG] Looking for Lambda permission: %s", input)
var out *lambda.GetPolicyOutput
var statement *LambdaPolicyStatement
err := resource.Retry(1*time.Minute, func() error {
// IAM is eventually cosistent :/
var err error
out, err = conn.GetPolicy(&input)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "ResourceNotFoundException" {
return err
}
}
return resource.RetryError{Err: err}
}
policyInBytes := []byte(*out.Policy)
policy := LambdaPolicy{}
err = json.Unmarshal(policyInBytes, &policy)
if err != nil {
return resource.RetryError{Err: fmt.Errorf("Error unmarshalling Lambda policy: %s", err)}
}
statement, err = findLambdaPolicyStatementById(&policy, d.Id())
return err
})
if err != nil {
return err
}
qualifier, err := getQualifierFromLambdaAliasOrVersionArn(statement.Resource)
if err == nil {
d.Set("qualifier", qualifier)
}
// Save Lambda function name in the same format
if strings.HasPrefix(d.Get("function_name").(string), "arn:aws:lambda:") {
// Strip qualifier off
trimmedArn := strings.TrimSuffix(statement.Resource, ":"+qualifier)
d.Set("function_name", trimmedArn)
} else {
functionName, err := getFunctionNameFromLambdaArn(statement.Resource)
if err != nil {
return err
}
d.Set("function_name", functionName)
}
d.Set("action", statement.Action)
d.Set("principal", statement.Principal["Service"])
if stringEquals, ok := statement.Condition["StringEquals"]; ok {
d.Set("source_account", stringEquals["AWS:SourceAccount"])
}
if arnLike, ok := statement.Condition["ArnLike"]; ok {
d.Set("source_arn", arnLike["AWS:SourceArn"])
}
return nil
}
func resourceAwsLambdaPermissionDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lambdaconn
functionName := d.Get("function_name").(string)
// There is a bug in the API (reported and acknowledged by AWS)
// which causes some permissions to be ignored when API calls are sent in parallel
// We work around this bug via mutex
awsMutexKV.Lock(functionName)
defer awsMutexKV.Unlock(functionName)
input := lambda.RemovePermissionInput{
FunctionName: aws.String(functionName),
StatementId: aws.String(d.Id()),
}
if v, ok := d.GetOk("qualifier"); ok {
input.Qualifier = aws.String(v.(string))
}
log.Printf("[DEBUG] Removing Lambda permission: %s", input)
_, err := conn.RemovePermission(&input)
if err != nil {
return err
}
err = resource.Retry(5*time.Minute, func() error {
log.Printf("[DEBUG] Checking if Lambda permission %q is deleted", d.Id())
params := &lambda.GetPolicyInput{
FunctionName: aws.String(d.Get("function_name").(string)),
}
if v, ok := d.GetOk("qualifier"); ok {
params.Qualifier = aws.String(v.(string))
}
log.Printf("[DEBUG] Looking for Lambda permission: %s", *params)
resp, err := conn.GetPolicy(params)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "ResourceNotFoundException" {
return nil
}
}
return resource.RetryError{Err: err}
}
if resp.Policy == nil {
return nil
}
policyInBytes := []byte(*resp.Policy)
policy := LambdaPolicy{}
err = json.Unmarshal(policyInBytes, &policy)
if err != nil {
return fmt.Errorf("Error unmarshalling Lambda policy: %s", err)
}
_, err = findLambdaPolicyStatementById(&policy, d.Id())
if err != nil {
return nil
}
log.Printf("[DEBUG] No error when checking if Lambda permission %s is deleted", d.Id())
return nil
})
if err != nil {
return fmt.Errorf("Failed removing Lambda permission: %s", err)
}
log.Printf("[DEBUG] Lambda permission with ID %q removed", d.Id())
d.SetId("")
return nil
}
func findLambdaPolicyStatementById(policy *LambdaPolicy, id string) (
*LambdaPolicyStatement, error) {
log.Printf("[DEBUG] Received %d statements in Lambda policy: %s", len(policy.Statement), policy.Statement)
for _, statement := range policy.Statement {
if statement.Sid == id {
return &statement, nil
}
}
return nil, fmt.Errorf("Failed to find statement %q in Lambda policy:\n%s", id, policy.Statement)
}
func getQualifierFromLambdaAliasOrVersionArn(arn string) (string, error) {
matches := regexp.MustCompile(LambdaFunctionRegexp).FindStringSubmatch(arn)
if len(matches) < 8 || matches[7] == "" {
return "", fmt.Errorf("Invalid ARN or otherwise unable to get qualifier from ARN (%q)",
arn)
}
return matches[7], nil
}
func getFunctionNameFromLambdaArn(arn string) (string, error) {
matches := regexp.MustCompile(LambdaFunctionRegexp).FindStringSubmatch(arn)
if len(matches) < 6 || matches[5] == "" {
return "", fmt.Errorf("Invalid ARN or otherwise unable to get qualifier from ARN (%q)",
arn)
}
return matches[5], nil
}
type LambdaPolicy struct {
Version string
Statement []LambdaPolicyStatement
Id string
}
type LambdaPolicyStatement struct {
Condition map[string]map[string]string
Action string
Resource string
Effect string
Principal map[string]string
Sid string
}

View File

@ -0,0 +1,722 @@
package aws
import (
"encoding/json"
"fmt"
"regexp"
"strings"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/lambda"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestLambdaPermissionUnmarshalling(t *testing.T) {
v := LambdaPolicy{}
err := json.Unmarshal(testLambdaPolicy, &v)
if err != nil {
t.Fatalf("Expected no error when unmarshalling: %s", err)
}
expectedSid := "36fe77d9-a4ae-13fb-8beb-5dc6821d5291"
if v.Statement[0].Sid != expectedSid {
t.Fatalf("Expected Sid to match (%q != %q)", v.Statement[0].Sid, expectedSid)
}
expectedFunctionName := "arn:aws:lambda:eu-west-1:319201112229:function:myCustomFunction"
if v.Statement[0].Resource != expectedFunctionName {
t.Fatalf("Expected function name to match (%q != %q)", v.Statement[0].Resource, expectedFunctionName)
}
expectedAction := "lambda:InvokeFunction"
if v.Statement[0].Action != expectedAction {
t.Fatalf("Expected Action to match (%q != %q)", v.Statement[0].Action, expectedAction)
}
expectedPrincipal := "events.amazonaws.com"
if v.Statement[0].Principal["Service"] != expectedPrincipal {
t.Fatalf("Expected Principal to match (%q != %q)", v.Statement[0].Principal["Service"], expectedPrincipal)
}
expectedSourceAccount := "319201112229"
if v.Statement[0].Condition["StringEquals"]["AWS:SourceAccount"] != expectedSourceAccount {
t.Fatalf("Expected Source Account to match (%q != %q)",
v.Statement[0].Condition["StringEquals"]["AWS:SourceAccount"],
expectedSourceAccount)
}
}
func TestLambdaPermissionGetQualifierFromLambdaAliasOrVersionArn_alias(t *testing.T) {
arnWithAlias := "arn:aws:lambda:us-west-2:187636751137:function:lambda_function_name:testalias"
expectedQualifier := "testalias"
qualifier, err := getQualifierFromLambdaAliasOrVersionArn(arnWithAlias)
if err != nil {
t.Fatalf("Expected no error when getting qualifier: %s", err)
}
if qualifier != expectedQualifier {
t.Fatalf("Expected qualifier to match (%q != %q)", qualifier, expectedQualifier)
}
}
func TestLambdaPermissionGetQualifierFromLambdaAliasOrVersionArn_version(t *testing.T) {
arnWithVersion := "arn:aws:lambda:us-west-2:187636751137:function:lambda_function_name:223"
expectedQualifier := "223"
qualifier, err := getQualifierFromLambdaAliasOrVersionArn(arnWithVersion)
if err != nil {
t.Fatalf("Expected no error when getting qualifier: %s", err)
}
if qualifier != expectedQualifier {
t.Fatalf("Expected qualifier to match (%q != %q)", qualifier, expectedQualifier)
}
}
func TestLambdaPermissionGetQualifierFromLambdaAliasOrVersionArn_invalid(t *testing.T) {
invalidArn := "arn:aws:lambda:us-west-2:187636751137:function:lambda_function_name"
qualifier, err := getQualifierFromLambdaAliasOrVersionArn(invalidArn)
if err == nil {
t.Fatalf("Expected error when getting qualifier")
}
if qualifier != "" {
t.Fatalf("Expected qualifier to be empty (%q)", qualifier)
}
// with trailing colon
invalidArn = "arn:aws:lambda:us-west-2:187636751137:function:lambda_function_name:"
qualifier, err = getQualifierFromLambdaAliasOrVersionArn(invalidArn)
if err == nil {
t.Fatalf("Expected error when getting qualifier")
}
if qualifier != "" {
t.Fatalf("Expected qualifier to be empty (%q)", qualifier)
}
}
func TestLambdaPermissionGetFunctionNameFromLambdaArn_invalid(t *testing.T) {
invalidArn := "arn:aws:lambda:us-west-2:187636751137:function:"
fn, err := getFunctionNameFromLambdaArn(invalidArn)
if err == nil {
t.Fatalf("Expected error when parsing invalid ARN (%q)", invalidArn)
}
if fn != "" {
t.Fatalf("Expected empty string when parsing invalid ARN (%q)", invalidArn)
}
}
func TestLambdaPermissionGetFunctionNameFromLambdaArn_valid(t *testing.T) {
validArn := "arn:aws:lambda:us-west-2:187636751137:function:lambda_function_name"
fn, err := getFunctionNameFromLambdaArn(validArn)
if err != nil {
t.Fatalf("Expected no error (%q): %q", validArn, err)
}
expectedFunctionname := "lambda_function_name"
if fn != expectedFunctionname {
t.Fatalf("Expected Lambda function name to match (%q != %q)",
validArn, expectedFunctionname)
}
// With qualifier
validArn = "arn:aws:lambda:us-west-2:187636751137:function:lambda_function_name:12"
fn, err = getFunctionNameFromLambdaArn(validArn)
if err != nil {
t.Fatalf("Expected no error (%q): %q", validArn, err)
}
expectedFunctionname = "lambda_function_name"
if fn != expectedFunctionname {
t.Fatalf("Expected Lambda function name to match (%q != %q)",
validArn, expectedFunctionname)
}
}
func TestAccAWSLambdaPermission_basic(t *testing.T) {
var statement LambdaPolicyStatement
endsWithFuncName := regexp.MustCompile(":function:lambda_function_name_perm$")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLambdaPermissionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSLambdaPermissionConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckLambdaPermissionExists("aws_lambda_permission.allow_cloudwatch", &statement),
resource.TestCheckResourceAttr("aws_lambda_permission.allow_cloudwatch", "action", "lambda:InvokeFunction"),
resource.TestCheckResourceAttr("aws_lambda_permission.allow_cloudwatch", "principal", "events.amazonaws.com"),
resource.TestCheckResourceAttr("aws_lambda_permission.allow_cloudwatch", "statement_id", "AllowExecutionFromCloudWatch"),
resource.TestCheckResourceAttr("aws_lambda_permission.allow_cloudwatch", "qualifier", ""),
resource.TestMatchResourceAttr("aws_lambda_permission.allow_cloudwatch", "function_name", endsWithFuncName),
),
},
},
})
}
func TestAccAWSLambdaPermission_withRawFunctionName(t *testing.T) {
var statement LambdaPolicyStatement
endsWithFuncName := regexp.MustCompile(":function:lambda_function_name_perm_raw_func_name$")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLambdaPermissionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSLambdaPermissionConfig_withRawFunctionName,
Check: resource.ComposeTestCheckFunc(
testAccCheckLambdaPermissionExists("aws_lambda_permission.with_raw_func_name", &statement),
resource.TestCheckResourceAttr("aws_lambda_permission.with_raw_func_name", "action", "lambda:InvokeFunction"),
resource.TestCheckResourceAttr("aws_lambda_permission.with_raw_func_name", "principal", "events.amazonaws.com"),
resource.TestCheckResourceAttr("aws_lambda_permission.with_raw_func_name", "statement_id", "AllowExecutionWithRawFuncName"),
resource.TestMatchResourceAttr("aws_lambda_permission.with_raw_func_name", "function_name", endsWithFuncName),
),
},
},
})
}
func TestAccAWSLambdaPermission_withQualifier(t *testing.T) {
var statement LambdaPolicyStatement
endsWithFuncName := regexp.MustCompile(":function:lambda_function_name_perm_qualifier$")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLambdaPermissionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSLambdaPermissionConfig_withQualifier,
Check: resource.ComposeTestCheckFunc(
testAccCheckLambdaPermissionExists("aws_lambda_permission.with_qualifier", &statement),
resource.TestCheckResourceAttr("aws_lambda_permission.with_qualifier", "action", "lambda:InvokeFunction"),
resource.TestCheckResourceAttr("aws_lambda_permission.with_qualifier", "principal", "events.amazonaws.com"),
resource.TestCheckResourceAttr("aws_lambda_permission.with_qualifier", "statement_id", "AllowExecutionWithQualifier"),
resource.TestMatchResourceAttr("aws_lambda_permission.with_qualifier", "function_name", endsWithFuncName),
resource.TestCheckResourceAttr("aws_lambda_permission.with_qualifier", "qualifier", "testalias_perm_qualifier"),
),
},
},
})
}
func TestAccAWSLambdaPermission_multiplePerms(t *testing.T) {
var firstStatement LambdaPolicyStatement
var firstStatementModified LambdaPolicyStatement
var secondStatement LambdaPolicyStatement
var secondStatementModified LambdaPolicyStatement
var thirdStatement LambdaPolicyStatement
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLambdaPermissionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSLambdaPermissionConfig_multiplePerms,
Check: resource.ComposeTestCheckFunc(
// 1st
testAccCheckLambdaPermissionExists("aws_lambda_permission.first", &firstStatement),
resource.TestCheckResourceAttr("aws_lambda_permission.first", "action", "lambda:InvokeFunction"),
resource.TestCheckResourceAttr("aws_lambda_permission.first", "principal", "events.amazonaws.com"),
resource.TestCheckResourceAttr("aws_lambda_permission.first", "statement_id", "AllowExecutionFirst"),
resource.TestMatchResourceAttr("aws_lambda_permission.first", "function_name",
regexp.MustCompile(":function:lambda_function_name_perm_multiperms$")),
// 2nd
testAccCheckLambdaPermissionExists("aws_lambda_permission.second", &firstStatementModified),
resource.TestCheckResourceAttr("aws_lambda_permission.second", "action", "lambda:*"),
resource.TestCheckResourceAttr("aws_lambda_permission.second", "principal", "events.amazonaws.com"),
resource.TestCheckResourceAttr("aws_lambda_permission.second", "statement_id", "AllowExecutionSecond"),
resource.TestMatchResourceAttr("aws_lambda_permission.second", "function_name",
regexp.MustCompile(":function:lambda_function_name_perm_multiperms$")),
),
},
resource.TestStep{
Config: testAccAWSLambdaPermissionConfig_multiplePermsModified,
Check: resource.ComposeTestCheckFunc(
// 1st
testAccCheckLambdaPermissionExists("aws_lambda_permission.first", &secondStatement),
resource.TestCheckResourceAttr("aws_lambda_permission.first", "action", "lambda:InvokeFunction"),
resource.TestCheckResourceAttr("aws_lambda_permission.first", "principal", "events.amazonaws.com"),
resource.TestCheckResourceAttr("aws_lambda_permission.first", "statement_id", "AllowExecutionFirst"),
resource.TestMatchResourceAttr("aws_lambda_permission.first", "function_name",
regexp.MustCompile(":function:lambda_function_name_perm_multiperms$")),
// 2nd
testAccCheckLambdaPermissionExists("aws_lambda_permission.sec0nd", &secondStatementModified),
resource.TestCheckResourceAttr("aws_lambda_permission.sec0nd", "action", "lambda:*"),
resource.TestCheckResourceAttr("aws_lambda_permission.sec0nd", "principal", "events.amazonaws.com"),
resource.TestCheckResourceAttr("aws_lambda_permission.sec0nd", "statement_id", "AllowExecutionSec0nd"),
resource.TestMatchResourceAttr("aws_lambda_permission.sec0nd", "function_name",
regexp.MustCompile(":function:lambda_function_name_perm_multiperms$")),
// 3rd
testAccCheckLambdaPermissionExists("aws_lambda_permission.third", &thirdStatement),
resource.TestCheckResourceAttr("aws_lambda_permission.third", "action", "lambda:*"),
resource.TestCheckResourceAttr("aws_lambda_permission.third", "principal", "events.amazonaws.com"),
resource.TestCheckResourceAttr("aws_lambda_permission.third", "statement_id", "AllowExecutionThird"),
resource.TestMatchResourceAttr("aws_lambda_permission.third", "function_name",
regexp.MustCompile(":function:lambda_function_name_perm_multiperms$")),
),
},
},
})
}
func TestAccAWSLambdaPermission_withS3(t *testing.T) {
rInt := acctest.RandInt()
var statement LambdaPolicyStatement
endsWithFuncName := regexp.MustCompile(":function:lambda_function_name_perm_s3$")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLambdaPermissionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(testAccAWSLambdaPermissionConfig_withS3_tpl, rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckLambdaPermissionExists("aws_lambda_permission.with_s3", &statement),
resource.TestCheckResourceAttr("aws_lambda_permission.with_s3", "action", "lambda:InvokeFunction"),
resource.TestCheckResourceAttr("aws_lambda_permission.with_s3", "principal", "s3.amazonaws.com"),
resource.TestCheckResourceAttr("aws_lambda_permission.with_s3", "statement_id", "AllowExecutionFromS3"),
resource.TestMatchResourceAttr("aws_lambda_permission.with_s3", "function_name", endsWithFuncName),
resource.TestCheckResourceAttr("aws_lambda_permission.with_s3", "source_arn",
fmt.Sprintf("arn:aws:s3:::tf-acc-towards-lambda-%d", rInt)),
),
},
},
})
}
func TestAccAWSLambdaPermission_withSNS(t *testing.T) {
var statement LambdaPolicyStatement
endsWithFuncName := regexp.MustCompile(":function:lambda_function_name_perm_sns$")
endsWithTopicName := regexp.MustCompile(":tf-acc-user-updates-topic$")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLambdaPermissionDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSLambdaPermissionConfig_withSNS,
Check: resource.ComposeTestCheckFunc(
testAccCheckLambdaPermissionExists("aws_lambda_permission.with_sns", &statement),
resource.TestCheckResourceAttr("aws_lambda_permission.with_sns", "action", "lambda:InvokeFunction"),
resource.TestCheckResourceAttr("aws_lambda_permission.with_sns", "principal", "sns.amazonaws.com"),
resource.TestCheckResourceAttr("aws_lambda_permission.with_sns", "statement_id", "AllowExecutionFromSNS"),
resource.TestMatchResourceAttr("aws_lambda_permission.with_sns", "function_name", endsWithFuncName),
resource.TestMatchResourceAttr("aws_lambda_permission.with_sns", "source_arn", endsWithTopicName),
),
},
},
})
}
func testAccCheckLambdaPermissionExists(n string, statement *LambdaPolicyStatement) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
conn := testAccProvider.Meta().(*AWSClient).lambdaconn
// IAM is eventually consistent
var foundStatement *LambdaPolicyStatement
err := resource.Retry(5*time.Minute, func() error {
var err error
foundStatement, err = lambdaPermissionExists(rs, conn)
if err != nil {
if strings.HasPrefix(err.Error(), "ResourceNotFoundException") {
return err
}
if strings.HasPrefix(err.Error(), "Lambda policy not found") {
return err
}
if strings.HasPrefix(err.Error(), "Failed to find statement") {
return err
}
return resource.RetryError{Err: err}
}
return nil
})
if err != nil {
return err
}
*statement = *foundStatement
return nil
}
}
func testAccCheckAWSLambdaPermissionDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).lambdaconn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_lambda_permission" {
continue
}
// IAM is eventually consistent
err := resource.Retry(5*time.Minute, func() error {
err := isLambdaPermissionGone(rs, conn)
if err != nil {
if !strings.HasPrefix(err.Error(), "Error unmarshalling Lambda policy") {
return err
}
return resource.RetryError{Err: err}
}
return nil
})
if err != nil {
return err
}
}
return nil
}
func isLambdaPermissionGone(rs *terraform.ResourceState, conn *lambda.Lambda) error {
params := &lambda.GetPolicyInput{
FunctionName: aws.String(rs.Primary.Attributes["function_name"]),
}
if v, ok := rs.Primary.Attributes["qualifier"]; ok {
params.Qualifier = aws.String(v)
}
resp, err := conn.GetPolicy(params)
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "ResourceNotFoundException" {
// no policy found => all statements deleted
return nil
}
}
if err != nil {
return fmt.Errorf("Unexpected error when checking existence of Lambda permission: %s\n%s",
rs.Primary.ID, err)
}
policyInBytes := []byte(*resp.Policy)
policy := LambdaPolicy{}
err = json.Unmarshal(policyInBytes, &policy)
if err != nil {
return fmt.Errorf("Error unmarshalling Lambda policy (%s): %s", *resp.Policy, err)
}
state, err := findLambdaPolicyStatementById(&policy, rs.Primary.ID)
if err != nil {
// statement not found => deleted
return nil
}
return fmt.Errorf("Policy statement expected to be gone (%s):\n%s",
rs.Primary.ID, *state)
}
func lambdaPermissionExists(rs *terraform.ResourceState, conn *lambda.Lambda) (*LambdaPolicyStatement, error) {
params := &lambda.GetPolicyInput{
FunctionName: aws.String(rs.Primary.Attributes["function_name"]),
}
if v, ok := rs.Primary.Attributes["qualifier"]; ok {
params.Qualifier = aws.String(v)
}
resp, err := conn.GetPolicy(params)
if err != nil {
return nil, fmt.Errorf("Lambda policy not found: %q", err)
}
if resp.Policy == nil {
return nil, fmt.Errorf("Received Lambda policy is empty")
}
policyInBytes := []byte(*resp.Policy)
policy := LambdaPolicy{}
err = json.Unmarshal(policyInBytes, &policy)
if err != nil {
return nil, fmt.Errorf("Error unmarshalling Lambda policy: %s", err)
}
return findLambdaPolicyStatementById(&policy, rs.Primary.ID)
}
var testAccAWSLambdaPermissionConfig = `
resource "aws_lambda_permission" "allow_cloudwatch" {
statement_id = "AllowExecutionFromCloudWatch"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.test_lambda.arn}"
principal = "events.amazonaws.com"
}
resource "aws_lambda_function" "test_lambda" {
filename = "test-fixtures/lambdatest.zip"
function_name = "lambda_function_name_perm"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "exports.handler"
}
resource "aws_iam_role" "iam_for_lambda" {
name = "iam_for_lambda_perm"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
`
var testAccAWSLambdaPermissionConfig_withRawFunctionName = `
resource "aws_lambda_permission" "with_raw_func_name" {
statement_id = "AllowExecutionWithRawFuncName"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.test_lambda.arn}"
principal = "events.amazonaws.com"
}
resource "aws_lambda_function" "test_lambda" {
filename = "test-fixtures/lambdatest.zip"
function_name = "lambda_function_name_perm_raw_func_name"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "exports.handler"
}
resource "aws_iam_role" "iam_for_lambda" {
name = "iam_for_lambda_perm_raw_func_name"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
`
var testAccAWSLambdaPermissionConfig_withQualifier = `
resource "aws_lambda_permission" "with_qualifier" {
statement_id = "AllowExecutionWithQualifier"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.test_lambda.arn}"
principal = "events.amazonaws.com"
source_account = "111122223333"
source_arn = "arn:aws:events:eu-west-1:111122223333:rule/RunDaily"
qualifier = "${aws_lambda_alias.test_alias.name}"
}
resource "aws_lambda_alias" "test_alias" {
name = "testalias_perm_qualifier"
description = "a sample description"
function_name = "${aws_lambda_function.test_lambda.arn}"
function_version = "$LATEST"
}
resource "aws_lambda_function" "test_lambda" {
filename = "test-fixtures/lambdatest.zip"
function_name = "lambda_function_name_perm_qualifier"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "exports.handler"
}
resource "aws_iam_role" "iam_for_lambda" {
name = "iam_for_lambda_perm_qualifier"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
`
var testAccAWSLambdaPermissionConfig_multiplePerms_tpl = `
resource "aws_lambda_permission" "first" {
statement_id = "AllowExecutionFirst"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.test_lambda.arn}"
principal = "events.amazonaws.com"
}
resource "aws_lambda_permission" "%s" {
statement_id = "%s"
action = "lambda:*"
function_name = "${aws_lambda_function.test_lambda.arn}"
principal = "events.amazonaws.com"
}
%s
resource "aws_lambda_function" "test_lambda" {
filename = "test-fixtures/lambdatest.zip"
function_name = "lambda_function_name_perm_multiperms"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "exports.handler"
}
resource "aws_iam_role" "iam_for_lambda" {
name = "iam_for_lambda_perm_multi_perms"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
`
var testAccAWSLambdaPermissionConfig_multiplePerms = fmt.Sprintf(
testAccAWSLambdaPermissionConfig_multiplePerms_tpl, "second", "AllowExecutionSecond", "")
var testAccAWSLambdaPermissionConfig_multiplePermsModified = fmt.Sprintf(
testAccAWSLambdaPermissionConfig_multiplePerms_tpl, "sec0nd", "AllowExecutionSec0nd", `
resource "aws_lambda_permission" "third" {
statement_id = "AllowExecutionThird"
action = "lambda:*"
function_name = "${aws_lambda_function.test_lambda.arn}"
principal = "events.amazonaws.com"
}`)
var testAccAWSLambdaPermissionConfig_withS3_tpl = `
resource "aws_lambda_permission" "with_s3" {
statement_id = "AllowExecutionFromS3"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.my-func.arn}"
principal = "s3.amazonaws.com"
source_arn = "${aws_s3_bucket.default.arn}"
}
resource "aws_s3_bucket" "default" {
bucket = "tf-acc-towards-lambda-%d"
acl = "private"
}
resource "aws_lambda_function" "my-func" {
filename = "test-fixtures/lambdatest.zip"
function_name = "lambda_function_name_perm_s3"
role = "${aws_iam_role.police.arn}"
handler = "exports.handler"
}
resource "aws_iam_role" "police" {
name = "iam_for_lambda_perm_with_s3"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
`
var testAccAWSLambdaPermissionConfig_withSNS = `
resource "aws_lambda_permission" "with_sns" {
statement_id = "AllowExecutionFromSNS"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.my-func.arn}"
principal = "sns.amazonaws.com"
source_arn = "${aws_sns_topic.default.arn}"
}
resource "aws_sns_topic" "default" {
name = "tf-acc-user-updates-topic"
}
resource "aws_sns_topic_subscription" "lambda" {
topic_arn = "${aws_sns_topic.default.arn}"
protocol = "lambda"
endpoint = "${aws_lambda_function.my-func.arn}"
}
resource "aws_lambda_function" "my-func" {
filename = "test-fixtures/lambdatest.zip"
function_name = "lambda_function_name_perm_sns"
role = "${aws_iam_role.police.arn}"
handler = "exports.handler"
}
resource "aws_iam_role" "police" {
name = "iam_for_lambda_perm_with_sns"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
`
var testLambdaPolicy = []byte(`{
"Version": "2012-10-17",
"Statement": [
{
"Condition": {
"StringEquals": {"AWS:SourceAccount": "319201112229"},
"ArnLike":{"AWS:SourceArn":"arn:aws:events:eu-west-1:319201112229:rule/RunDaily"}
},
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:eu-west-1:319201112229:function:myCustomFunction",
"Effect": "Allow",
"Principal": {"Service":"events.amazonaws.com"},
"Sid": "36fe77d9-a4ae-13fb-8beb-5dc6821d5291"
}
],
"Id":"default"
}`)

View File

@ -210,7 +210,20 @@ func resourceAwsS3BucketCreate(d *schema.ResourceData, meta interface{}) error {
}
}
_, err := s3conn.CreateBucket(req)
err := resource.Retry(5*time.Minute, func() error {
log.Printf("[DEBUG] Trying to create new S3 bucket: %q", bucket)
_, err := s3conn.CreateBucket(req)
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "OperationAborted" {
log.Printf("[WARN] Got an error while trying to create S3 bucket %s: %s", bucket, err)
return fmt.Errorf("[WARN] Error creating S3 bucket %s, retrying: %s",
bucket, err)
}
return resource.RetryError{Err: err}
}
return nil
})
if err != nil {
return fmt.Errorf("Error creating S3 bucket: %s", err)
}

View File

@ -183,3 +183,98 @@ func validateCloudWatchEventTargetId(v interface{}, k string) (ws []string, erro
return
}
func validateLambdaFunctionName(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if len(value) > 140 {
errors = append(errors, fmt.Errorf(
"%q cannot be longer than 140 characters: %q", k, value))
}
// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
pattern := `^(arn:aws:lambda:)?([a-z]{2}-[a-z]+-\d{1}:)?(\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\$LATEST|[a-zA-Z0-9-_]+))?$`
if !regexp.MustCompile(pattern).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q doesn't comply with restrictions (%q): %q",
k, pattern, value))
}
return
}
func validateLambdaQualifier(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if len(value) > 128 {
errors = append(errors, fmt.Errorf(
"%q cannot be longer than 128 characters: %q", k, value))
}
// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
pattern := `^[a-zA-Z0-9$_]+$`
if !regexp.MustCompile(pattern).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q doesn't comply with restrictions (%q): %q",
k, pattern, value))
}
return
}
func validateLambdaPermissionAction(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
pattern := `^(lambda:[*]|lambda:[a-zA-Z]+|[*])$`
if !regexp.MustCompile(pattern).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q doesn't comply with restrictions (%q): %q",
k, pattern, value))
}
return
}
func validateAwsAccountId(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
pattern := `^\d{12}$`
if !regexp.MustCompile(pattern).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q doesn't look like AWS Account ID (exactly 12 digits): %q",
k, value))
}
return
}
func validateArn(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
pattern := `^arn:aws:([a-zA-Z0-9\-])+:([a-z]{2}-[a-z]+-\d{1})?:(\d{12})?:(.*)$`
if !regexp.MustCompile(pattern).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q doesn't look like a valid ARN (%q): %q",
k, pattern, value))
}
return
}
func validatePolicyStatementId(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if len(value) > 100 {
errors = append(errors, fmt.Errorf(
"%q cannot be longer than 100 characters: %q", k, value))
}
// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
pattern := `^[a-zA-Z0-9-_]+$`
if !regexp.MustCompile(pattern).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q doesn't look like a valid statement ID (%q): %q",
k, pattern, value))
}
return
}

View File

@ -70,3 +70,176 @@ func TestValidateCloudWatchEventRuleName(t *testing.T) {
}
}
}
func TestValidateLambdaFunctionName(t *testing.T) {
validNames := []string{
"arn:aws:lambda:us-west-2:123456789012:function:ThumbNail",
"FunctionName",
"function-name",
}
for _, v := range validNames {
_, errors := validateLambdaFunctionName(v, "name")
if len(errors) != 0 {
t.Fatalf("%q should be a valid Lambda function name: %q", v, errors)
}
}
invalidNames := []string{
"/FunctionNameWithSlash",
"function.name.with.dots",
// lenght > 140
"arn:aws:lambda:us-west-2:123456789012:function:TooLoooooo" +
"ooooooooooooooooooooooooooooooooooooooooooooooooooooooo" +
"ooooooooooooooooongFunctionName",
}
for _, v := range invalidNames {
_, errors := validateLambdaFunctionName(v, "name")
if len(errors) == 0 {
t.Fatalf("%q should be an invalid Lambda function name", v)
}
}
}
func TestValidateLambdaQualifier(t *testing.T) {
validNames := []string{
"123",
"prod",
"PROD",
"MyTestEnv",
"$LATEST",
}
for _, v := range validNames {
_, errors := validateLambdaQualifier(v, "name")
if len(errors) != 0 {
t.Fatalf("%q should be a valid Lambda function qualifier: %q", v, errors)
}
}
invalidNames := []string{
// No ARNs allowed
"arn:aws:lambda:us-west-2:123456789012:function:prod",
// lenght > 128
"TooLooooooooooooooooooooooooooooooooooooooooooooooooooo" +
"ooooooooooooooooooooooooooooooooooooooooooooooooooo" +
"oooooooooooongQualifier",
}
for _, v := range invalidNames {
_, errors := validateLambdaQualifier(v, "name")
if len(errors) == 0 {
t.Fatalf("%q should be an invalid Lambda function qualifier", v)
}
}
}
func TestValidateLambdaPermissionAction(t *testing.T) {
validNames := []string{
"lambda:*",
"lambda:InvokeFunction",
"*",
}
for _, v := range validNames {
_, errors := validateLambdaPermissionAction(v, "action")
if len(errors) != 0 {
t.Fatalf("%q should be a valid Lambda permission action: %q", v, errors)
}
}
invalidNames := []string{
"yada",
"lambda:123",
"*:*",
"lambda:Invoke*",
}
for _, v := range invalidNames {
_, errors := validateLambdaPermissionAction(v, "action")
if len(errors) == 0 {
t.Fatalf("%q should be an invalid Lambda permission action", v)
}
}
}
func TestValidateAwsAccountId(t *testing.T) {
validNames := []string{
"123456789012",
"999999999999",
}
for _, v := range validNames {
_, errors := validateAwsAccountId(v, "account_id")
if len(errors) != 0 {
t.Fatalf("%q should be a valid AWS Account ID: %q", v, errors)
}
}
invalidNames := []string{
"12345678901", // too short
"1234567890123", // too long
"invalid",
"x123456789012",
}
for _, v := range invalidNames {
_, errors := validateAwsAccountId(v, "account_id")
if len(errors) == 0 {
t.Fatalf("%q should be an invalid AWS Account ID", v)
}
}
}
func TestValidateArn(t *testing.T) {
validNames := []string{
"arn:aws:elasticbeanstalk:us-east-1:123456789012:environment/My App/MyEnvironment", // Beanstalk
"arn:aws:iam::123456789012:user/David", // IAM User
"arn:aws:rds:eu-west-1:123456789012:db:mysql-db", // RDS
"arn:aws:s3:::my_corporate_bucket/exampleobject.png", // S3 object
"arn:aws:events:us-east-1:319201112229:rule/rule_name", // CloudWatch Rule
"arn:aws:lambda:eu-west-1:319201112229:function:myCustomFunction", // Lambda function
"arn:aws:lambda:eu-west-1:319201112229:function:myCustomFunction:Qualifier", // Lambda func qualifier
}
for _, v := range validNames {
_, errors := validateArn(v, "arn")
if len(errors) != 0 {
t.Fatalf("%q should be a valid ARN: %q", v, errors)
}
}
invalidNames := []string{
"arn",
"123456789012",
"arn:aws",
"arn:aws:logs",
"arn:aws:logs:region:*:*",
}
for _, v := range invalidNames {
_, errors := validateArn(v, "arn")
if len(errors) == 0 {
t.Fatalf("%q should be an invalid ARN", v)
}
}
}
func TestValidatePolicyStatementId(t *testing.T) {
validNames := []string{
"YadaHereAndThere",
"Valid-5tatement_Id",
"1234",
}
for _, v := range validNames {
_, errors := validatePolicyStatementId(v, "statement_id")
if len(errors) != 0 {
t.Fatalf("%q should be a valid Statement ID: %q", v, errors)
}
}
invalidNames := []string{
"Invalid/StatementId/with/slashes",
"InvalidStatementId.with.dots",
// length > 100
"TooooLoooooooooooooooooooooooooooooooooooooooooooo" +
"ooooooooooooooooooooooooooooooooooooooooStatementId",
}
for _, v := range invalidNames {
_, errors := validatePolicyStatementId(v, "statement_id")
if len(errors) == 0 {
t.Fatalf("%q should be an invalid Statement ID", v)
}
}
}

View File

@ -0,0 +1,123 @@
---
layout: "aws"
page_title: "AWS: aws_lambda_permission"
sidebar_current: "docs-aws-resource-aws-lambda-permission"
description: |-
Creates a Lambda function permission.
---
# aws\_lambda\_permission
Creates a Lambda permission to allow external sources invoking the Lambda function
(e.g. CloudWatch Event Rule, SNS or S3).
## Example Usage
```
resource "aws_lambda_permission" "allow_cloudwatch" {
statement_id = "AllowExecutionFromCloudWatch"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.test_lambda.arn}"
principal = "events.amazonaws.com"
source_account = "111122223333"
source_arn = "arn:aws:events:eu-west-1:111122223333:rule/RunDaily"
qualifier = "${aws_lambda_alias.test_alias.name}"
}
resource "aws_lambda_alias" "test_alias" {
name = "testalias"
description = "a sample description"
function_name = "${aws_lambda_function.test_lambda.arn}"
function_version = "$LATEST"
}
resource "aws_lambda_function" "test_lambda" {
filename = "lambdatest.zip"
function_name = "lambda_function_name"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "exports.handler"
}
resource "aws_iam_role" "iam_for_lambda" {
name = "iam_for_lambda"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
```
## Usage with SNS
```
resource "aws_lambda_permission" "with_sns" {
statement_id = "AllowExecutionFromSNS"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.my-func.arn}"
principal = "sns.amazonaws.com"
source_arn = "${aws_sns_topic.default.arn}"
}
resource "aws_sns_topic" "default" {
name = "call-lambda-maybe"
}
resource "aws_sns_topic_subscription" "lambda" {
topic_arn = "${aws_sns_topic.default.arn}"
protocol = "lambda"
endpoint = "${aws_lambda_function.func.arn}"
}
resource "aws_lambda_function" "func" {
filename = "lambdatest.zip"
function_name = "lambda_called_from_sns"
role = "${aws_iam_role.default.arn}"
handler = "exports.handler"
}
resource "aws_iam_role" "default" {
name = "iam_for_lambda_with_sns"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
```
## Argument Reference
* `action` - (Required) The AWS Lambda action you want to allow in this statement. (e.g. `lambda:InvokeFunction`)
* `function_name` - (Required) Name of the Lambda function whose resource policy you are updating
* `principal` - (Required) The principal who is getting this permission.
e.g. `s3.amazonaws.com`, an AWS account ID, or any valid AWS service principal
such as `events.amazonaws.com` or `sns.amazonaws.com`.
* `statement_id` - (Required) A unique statement identifier.
* `qualifier` - (Optional) Query parameter to specify function version or alias name.
The permission will then apply to the specific qualified ARN.
e.g. `arn:aws:lambda:aws-region:acct-id:function:function-name:2`
* `source_account` - (Optional) The AWS account ID (without a hyphen) of the source owner.
* `source_arn` - (Optional) When granting Amazon S3 permission to invoke your function,
you should specify this field with the bucket Amazon Resource Name (ARN) as its value.
This ensures that only events generated from the specified bucket can invoke the function.

View File

@ -367,6 +367,9 @@
<li<%= sidebar_current("docs-aws-resource-aws-lambda-event-source-mapping") %>>
<a href="/docs/providers/aws/r/lambda_event_source_mapping.html">aws_lambda_event_source_mapping</a>
</li>
<li<%= sidebar_current("docs-aws-resource-lambda-permission") %>>
<a href="/docs/providers/aws/r/lambda_permission.html">aws_lambda_permission</a>
</li>
</ul>
</li>