github.com/andresvia/terraform@v0.6.15-0.20160412045437-d51c75946785/builtin/providers/aws/resource_aws_lambda_permission.go (about) 1 package aws 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "log" 7 "regexp" 8 "strings" 9 "time" 10 11 "github.com/aws/aws-sdk-go/aws" 12 "github.com/aws/aws-sdk-go/aws/awserr" 13 "github.com/aws/aws-sdk-go/service/lambda" 14 "github.com/hashicorp/terraform/helper/resource" 15 "github.com/hashicorp/terraform/helper/schema" 16 ) 17 18 var LambdaFunctionRegexp = `^(arn:aws:lambda:)?([a-z]{2}-[a-z]+-\d{1}:)?(\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\$LATEST|[a-zA-Z0-9-_]+))?$` 19 20 func resourceAwsLambdaPermission() *schema.Resource { 21 return &schema.Resource{ 22 Create: resourceAwsLambdaPermissionCreate, 23 Read: resourceAwsLambdaPermissionRead, 24 Delete: resourceAwsLambdaPermissionDelete, 25 26 Schema: map[string]*schema.Schema{ 27 "action": &schema.Schema{ 28 Type: schema.TypeString, 29 Required: true, 30 ForceNew: true, 31 ValidateFunc: validateLambdaPermissionAction, 32 }, 33 "function_name": &schema.Schema{ 34 Type: schema.TypeString, 35 Required: true, 36 ForceNew: true, 37 ValidateFunc: validateLambdaFunctionName, 38 }, 39 "principal": &schema.Schema{ 40 Type: schema.TypeString, 41 Required: true, 42 ForceNew: true, 43 }, 44 "qualifier": &schema.Schema{ 45 Type: schema.TypeString, 46 Optional: true, 47 ForceNew: true, 48 ValidateFunc: validateLambdaQualifier, 49 }, 50 "source_account": &schema.Schema{ 51 Type: schema.TypeString, 52 Optional: true, 53 ForceNew: true, 54 ValidateFunc: validateAwsAccountId, 55 }, 56 "source_arn": &schema.Schema{ 57 Type: schema.TypeString, 58 Optional: true, 59 ForceNew: true, 60 ValidateFunc: validateArn, 61 }, 62 "statement_id": &schema.Schema{ 63 Type: schema.TypeString, 64 Required: true, 65 ForceNew: true, 66 ValidateFunc: validatePolicyStatementId, 67 }, 68 }, 69 } 70 } 71 72 func resourceAwsLambdaPermissionCreate(d *schema.ResourceData, meta interface{}) error { 73 conn := meta.(*AWSClient).lambdaconn 74 75 functionName := d.Get("function_name").(string) 76 77 // There is a bug in the API (reported and acknowledged by AWS) 78 // which causes some permissions to be ignored when API calls are sent in parallel 79 // We work around this bug via mutex 80 awsMutexKV.Lock(functionName) 81 defer awsMutexKV.Unlock(functionName) 82 83 input := lambda.AddPermissionInput{ 84 Action: aws.String(d.Get("action").(string)), 85 FunctionName: aws.String(functionName), 86 Principal: aws.String(d.Get("principal").(string)), 87 StatementId: aws.String(d.Get("statement_id").(string)), 88 } 89 90 if v, ok := d.GetOk("qualifier"); ok { 91 input.Qualifier = aws.String(v.(string)) 92 } 93 if v, ok := d.GetOk("source_account"); ok { 94 input.SourceAccount = aws.String(v.(string)) 95 } 96 if v, ok := d.GetOk("source_arn"); ok { 97 input.SourceArn = aws.String(v.(string)) 98 } 99 100 log.Printf("[DEBUG] Adding new Lambda permission: %s", input) 101 var out *lambda.AddPermissionOutput 102 err := resource.Retry(1*time.Minute, func() *resource.RetryError { 103 var err error 104 out, err = conn.AddPermission(&input) 105 106 if err != nil { 107 if awsErr, ok := err.(awserr.Error); ok { 108 // IAM is eventually consistent :/ 109 if awsErr.Code() == "ResourceConflictException" { 110 return resource.RetryableError( 111 fmt.Errorf("[WARN] Error adding new Lambda Permission for %s, retrying: %s", 112 *input.FunctionName, err)) 113 } 114 } 115 return resource.NonRetryableError(err) 116 } 117 return nil 118 }) 119 120 if err != nil { 121 return err 122 } 123 124 if out != nil && out.Statement != nil { 125 log.Printf("[DEBUG] Created new Lambda permission: %s", *out.Statement) 126 } else { 127 log.Printf("[DEBUG] Created new Lambda permission, but no Statement was included") 128 } 129 130 d.SetId(d.Get("statement_id").(string)) 131 132 err = resource.Retry(5*time.Minute, func() *resource.RetryError { 133 // IAM is eventually cosistent :/ 134 err := resourceAwsLambdaPermissionRead(d, meta) 135 if err != nil { 136 if strings.HasPrefix(err.Error(), "Error reading Lambda policy: ResourceNotFoundException") { 137 return resource.RetryableError( 138 fmt.Errorf("[WARN] Error reading newly created Lambda Permission for %s, retrying: %s", 139 *input.FunctionName, err)) 140 } 141 if strings.HasPrefix(err.Error(), "Failed to find statement \""+d.Id()) { 142 return resource.RetryableError( 143 fmt.Errorf("[WARN] Error reading newly created Lambda Permission statement for %s, retrying: %s", 144 *input.FunctionName, err)) 145 } 146 147 log.Printf("[ERROR] An actual error occured when expecting Lambda policy to be there: %s", err) 148 return resource.NonRetryableError(err) 149 } 150 return nil 151 }) 152 153 return err 154 } 155 156 func resourceAwsLambdaPermissionRead(d *schema.ResourceData, meta interface{}) error { 157 conn := meta.(*AWSClient).lambdaconn 158 159 input := lambda.GetPolicyInput{ 160 FunctionName: aws.String(d.Get("function_name").(string)), 161 } 162 if v, ok := d.GetOk("qualifier"); ok { 163 input.Qualifier = aws.String(v.(string)) 164 } 165 166 log.Printf("[DEBUG] Looking for Lambda permission: %s", input) 167 var out *lambda.GetPolicyOutput 168 var statement *LambdaPolicyStatement 169 err := resource.Retry(1*time.Minute, func() *resource.RetryError { 170 // IAM is eventually cosistent :/ 171 var err error 172 out, err = conn.GetPolicy(&input) 173 if err != nil { 174 if awsErr, ok := err.(awserr.Error); ok { 175 if awsErr.Code() == "ResourceNotFoundException" { 176 return resource.RetryableError(err) 177 } 178 } 179 return resource.NonRetryableError(err) 180 } 181 182 policyInBytes := []byte(*out.Policy) 183 policy := LambdaPolicy{} 184 err = json.Unmarshal(policyInBytes, &policy) 185 if err != nil { 186 return resource.NonRetryableError(err) 187 } 188 189 statement, err = findLambdaPolicyStatementById(&policy, d.Id()) 190 return resource.RetryableError(err) 191 }) 192 if err != nil { 193 return err 194 } 195 196 qualifier, err := getQualifierFromLambdaAliasOrVersionArn(statement.Resource) 197 if err == nil { 198 d.Set("qualifier", qualifier) 199 } 200 201 // Save Lambda function name in the same format 202 if strings.HasPrefix(d.Get("function_name").(string), "arn:aws:lambda:") { 203 // Strip qualifier off 204 trimmedArn := strings.TrimSuffix(statement.Resource, ":"+qualifier) 205 d.Set("function_name", trimmedArn) 206 } else { 207 functionName, err := getFunctionNameFromLambdaArn(statement.Resource) 208 if err != nil { 209 return err 210 } 211 d.Set("function_name", functionName) 212 } 213 214 d.Set("action", statement.Action) 215 d.Set("principal", statement.Principal["Service"]) 216 217 if stringEquals, ok := statement.Condition["StringEquals"]; ok { 218 d.Set("source_account", stringEquals["AWS:SourceAccount"]) 219 } 220 221 if arnLike, ok := statement.Condition["ArnLike"]; ok { 222 d.Set("source_arn", arnLike["AWS:SourceArn"]) 223 } 224 225 return nil 226 } 227 228 func resourceAwsLambdaPermissionDelete(d *schema.ResourceData, meta interface{}) error { 229 conn := meta.(*AWSClient).lambdaconn 230 231 functionName := d.Get("function_name").(string) 232 233 // There is a bug in the API (reported and acknowledged by AWS) 234 // which causes some permissions to be ignored when API calls are sent in parallel 235 // We work around this bug via mutex 236 awsMutexKV.Lock(functionName) 237 defer awsMutexKV.Unlock(functionName) 238 239 input := lambda.RemovePermissionInput{ 240 FunctionName: aws.String(functionName), 241 StatementId: aws.String(d.Id()), 242 } 243 244 if v, ok := d.GetOk("qualifier"); ok { 245 input.Qualifier = aws.String(v.(string)) 246 } 247 248 log.Printf("[DEBUG] Removing Lambda permission: %s", input) 249 _, err := conn.RemovePermission(&input) 250 if err != nil { 251 return err 252 } 253 254 err = resource.Retry(5*time.Minute, func() *resource.RetryError { 255 log.Printf("[DEBUG] Checking if Lambda permission %q is deleted", d.Id()) 256 257 params := &lambda.GetPolicyInput{ 258 FunctionName: aws.String(d.Get("function_name").(string)), 259 } 260 if v, ok := d.GetOk("qualifier"); ok { 261 params.Qualifier = aws.String(v.(string)) 262 } 263 264 log.Printf("[DEBUG] Looking for Lambda permission: %s", *params) 265 resp, err := conn.GetPolicy(params) 266 if err != nil { 267 if awsErr, ok := err.(awserr.Error); ok { 268 if awsErr.Code() == "ResourceNotFoundException" { 269 return nil 270 } 271 } 272 return resource.NonRetryableError(err) 273 } 274 275 if resp.Policy == nil { 276 return nil 277 } 278 279 policyInBytes := []byte(*resp.Policy) 280 policy := LambdaPolicy{} 281 err = json.Unmarshal(policyInBytes, &policy) 282 if err != nil { 283 return resource.RetryableError( 284 fmt.Errorf("Error unmarshalling Lambda policy: %s", err)) 285 } 286 287 _, err = findLambdaPolicyStatementById(&policy, d.Id()) 288 if err != nil { 289 return nil 290 } 291 292 log.Printf("[DEBUG] No error when checking if Lambda permission %s is deleted", d.Id()) 293 return nil 294 }) 295 296 if err != nil { 297 return fmt.Errorf("Failed removing Lambda permission: %s", err) 298 } 299 300 log.Printf("[DEBUG] Lambda permission with ID %q removed", d.Id()) 301 d.SetId("") 302 303 return nil 304 } 305 306 func findLambdaPolicyStatementById(policy *LambdaPolicy, id string) ( 307 *LambdaPolicyStatement, error) { 308 309 log.Printf("[DEBUG] Received %d statements in Lambda policy: %s", len(policy.Statement), policy.Statement) 310 for _, statement := range policy.Statement { 311 if statement.Sid == id { 312 return &statement, nil 313 } 314 } 315 316 return nil, fmt.Errorf("Failed to find statement %q in Lambda policy:\n%s", id, policy.Statement) 317 } 318 319 func getQualifierFromLambdaAliasOrVersionArn(arn string) (string, error) { 320 matches := regexp.MustCompile(LambdaFunctionRegexp).FindStringSubmatch(arn) 321 if len(matches) < 8 || matches[7] == "" { 322 return "", fmt.Errorf("Invalid ARN or otherwise unable to get qualifier from ARN (%q)", 323 arn) 324 } 325 326 return matches[7], nil 327 } 328 329 func getFunctionNameFromLambdaArn(arn string) (string, error) { 330 matches := regexp.MustCompile(LambdaFunctionRegexp).FindStringSubmatch(arn) 331 if len(matches) < 6 || matches[5] == "" { 332 return "", fmt.Errorf("Invalid ARN or otherwise unable to get qualifier from ARN (%q)", 333 arn) 334 } 335 return matches[5], nil 336 } 337 338 type LambdaPolicy struct { 339 Version string 340 Statement []LambdaPolicyStatement 341 Id string 342 } 343 344 type LambdaPolicyStatement struct { 345 Condition map[string]map[string]string 346 Action string 347 Resource string 348 Effect string 349 Principal map[string]string 350 Sid string 351 }