mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Merge pull request #4826 from TimeIncOSS/f-aws-lambda-permission
[WIP] provider/aws: Add aws_lambda_permission
This commit is contained in:
commit
cd28433c2a
@ -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(),
|
||||
|
343
builtin/providers/aws/resource_aws_lambda_permission.go
Normal file
343
builtin/providers/aws/resource_aws_lambda_permission.go
Normal 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
|
||||
}
|
722
builtin/providers/aws/resource_aws_lambda_permission_test.go
Normal file
722
builtin/providers/aws/resource_aws_lambda_permission_test.go
Normal 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"
|
||||
}`)
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
@ -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>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user