github.com/subuk/terraform@v0.6.14-0.20160317140351-de1567c2e732/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 log.Printf("[DEBUG] Created new Lambda permission: %s", *out.Statement) 125 126 d.SetId(d.Get("statement_id").(string)) 127 128 err = resource.Retry(5*time.Minute, func() *resource.RetryError { 129 // IAM is eventually cosistent :/ 130 err := resourceAwsLambdaPermissionRead(d, meta) 131 if err != nil { 132 if strings.HasPrefix(err.Error(), "Error reading Lambda policy: ResourceNotFoundException") { 133 return resource.RetryableError( 134 fmt.Errorf("[WARN] Error reading newly created Lambda Permission for %s, retrying: %s", 135 *input.FunctionName, err)) 136 } 137 if strings.HasPrefix(err.Error(), "Failed to find statement \""+d.Id()) { 138 return resource.RetryableError( 139 fmt.Errorf("[WARN] Error reading newly created Lambda Permission statement for %s, retrying: %s", 140 *input.FunctionName, err)) 141 } 142 143 log.Printf("[ERROR] An actual error occured when expecting Lambda policy to be there: %s", err) 144 return resource.NonRetryableError(err) 145 } 146 return nil 147 }) 148 149 return err 150 } 151 152 func resourceAwsLambdaPermissionRead(d *schema.ResourceData, meta interface{}) error { 153 conn := meta.(*AWSClient).lambdaconn 154 155 input := lambda.GetPolicyInput{ 156 FunctionName: aws.String(d.Get("function_name").(string)), 157 } 158 if v, ok := d.GetOk("qualifier"); ok { 159 input.Qualifier = aws.String(v.(string)) 160 } 161 162 log.Printf("[DEBUG] Looking for Lambda permission: %s", input) 163 var out *lambda.GetPolicyOutput 164 var statement *LambdaPolicyStatement 165 err := resource.Retry(1*time.Minute, func() *resource.RetryError { 166 // IAM is eventually cosistent :/ 167 var err error 168 out, err = conn.GetPolicy(&input) 169 if err != nil { 170 if awsErr, ok := err.(awserr.Error); ok { 171 if awsErr.Code() == "ResourceNotFoundException" { 172 return resource.RetryableError(err) 173 } 174 } 175 return resource.NonRetryableError(err) 176 } 177 178 policyInBytes := []byte(*out.Policy) 179 policy := LambdaPolicy{} 180 err = json.Unmarshal(policyInBytes, &policy) 181 if err != nil { 182 return resource.NonRetryableError(err) 183 } 184 185 statement, err = findLambdaPolicyStatementById(&policy, d.Id()) 186 return resource.RetryableError(err) 187 }) 188 if err != nil { 189 return err 190 } 191 192 qualifier, err := getQualifierFromLambdaAliasOrVersionArn(statement.Resource) 193 if err == nil { 194 d.Set("qualifier", qualifier) 195 } 196 197 // Save Lambda function name in the same format 198 if strings.HasPrefix(d.Get("function_name").(string), "arn:aws:lambda:") { 199 // Strip qualifier off 200 trimmedArn := strings.TrimSuffix(statement.Resource, ":"+qualifier) 201 d.Set("function_name", trimmedArn) 202 } else { 203 functionName, err := getFunctionNameFromLambdaArn(statement.Resource) 204 if err != nil { 205 return err 206 } 207 d.Set("function_name", functionName) 208 } 209 210 d.Set("action", statement.Action) 211 d.Set("principal", statement.Principal["Service"]) 212 213 if stringEquals, ok := statement.Condition["StringEquals"]; ok { 214 d.Set("source_account", stringEquals["AWS:SourceAccount"]) 215 } 216 217 if arnLike, ok := statement.Condition["ArnLike"]; ok { 218 d.Set("source_arn", arnLike["AWS:SourceArn"]) 219 } 220 221 return nil 222 } 223 224 func resourceAwsLambdaPermissionDelete(d *schema.ResourceData, meta interface{}) error { 225 conn := meta.(*AWSClient).lambdaconn 226 227 functionName := d.Get("function_name").(string) 228 229 // There is a bug in the API (reported and acknowledged by AWS) 230 // which causes some permissions to be ignored when API calls are sent in parallel 231 // We work around this bug via mutex 232 awsMutexKV.Lock(functionName) 233 defer awsMutexKV.Unlock(functionName) 234 235 input := lambda.RemovePermissionInput{ 236 FunctionName: aws.String(functionName), 237 StatementId: aws.String(d.Id()), 238 } 239 240 if v, ok := d.GetOk("qualifier"); ok { 241 input.Qualifier = aws.String(v.(string)) 242 } 243 244 log.Printf("[DEBUG] Removing Lambda permission: %s", input) 245 _, err := conn.RemovePermission(&input) 246 if err != nil { 247 return err 248 } 249 250 err = resource.Retry(5*time.Minute, func() *resource.RetryError { 251 log.Printf("[DEBUG] Checking if Lambda permission %q is deleted", d.Id()) 252 253 params := &lambda.GetPolicyInput{ 254 FunctionName: aws.String(d.Get("function_name").(string)), 255 } 256 if v, ok := d.GetOk("qualifier"); ok { 257 params.Qualifier = aws.String(v.(string)) 258 } 259 260 log.Printf("[DEBUG] Looking for Lambda permission: %s", *params) 261 resp, err := conn.GetPolicy(params) 262 if err != nil { 263 if awsErr, ok := err.(awserr.Error); ok { 264 if awsErr.Code() == "ResourceNotFoundException" { 265 return nil 266 } 267 } 268 return resource.NonRetryableError(err) 269 } 270 271 if resp.Policy == nil { 272 return nil 273 } 274 275 policyInBytes := []byte(*resp.Policy) 276 policy := LambdaPolicy{} 277 err = json.Unmarshal(policyInBytes, &policy) 278 if err != nil { 279 return resource.RetryableError( 280 fmt.Errorf("Error unmarshalling Lambda policy: %s", err)) 281 } 282 283 _, err = findLambdaPolicyStatementById(&policy, d.Id()) 284 if err != nil { 285 return nil 286 } 287 288 log.Printf("[DEBUG] No error when checking if Lambda permission %s is deleted", d.Id()) 289 return nil 290 }) 291 292 if err != nil { 293 return fmt.Errorf("Failed removing Lambda permission: %s", err) 294 } 295 296 log.Printf("[DEBUG] Lambda permission with ID %q removed", d.Id()) 297 d.SetId("") 298 299 return nil 300 } 301 302 func findLambdaPolicyStatementById(policy *LambdaPolicy, id string) ( 303 *LambdaPolicyStatement, error) { 304 305 log.Printf("[DEBUG] Received %d statements in Lambda policy: %s", len(policy.Statement), policy.Statement) 306 for _, statement := range policy.Statement { 307 if statement.Sid == id { 308 return &statement, nil 309 } 310 } 311 312 return nil, fmt.Errorf("Failed to find statement %q in Lambda policy:\n%s", id, policy.Statement) 313 } 314 315 func getQualifierFromLambdaAliasOrVersionArn(arn string) (string, error) { 316 matches := regexp.MustCompile(LambdaFunctionRegexp).FindStringSubmatch(arn) 317 if len(matches) < 8 || matches[7] == "" { 318 return "", fmt.Errorf("Invalid ARN or otherwise unable to get qualifier from ARN (%q)", 319 arn) 320 } 321 322 return matches[7], nil 323 } 324 325 func getFunctionNameFromLambdaArn(arn string) (string, error) { 326 matches := regexp.MustCompile(LambdaFunctionRegexp).FindStringSubmatch(arn) 327 if len(matches) < 6 || matches[5] == "" { 328 return "", fmt.Errorf("Invalid ARN or otherwise unable to get qualifier from ARN (%q)", 329 arn) 330 } 331 return matches[5], nil 332 } 333 334 type LambdaPolicy struct { 335 Version string 336 Statement []LambdaPolicyStatement 337 Id string 338 } 339 340 type LambdaPolicyStatement struct { 341 Condition map[string]map[string]string 342 Action string 343 Resource string 344 Effect string 345 Principal map[string]string 346 Sid string 347 }