github.com/joshgarnett/terraform@v0.5.4-0.20160219181435-92dc20bb3594/builtin/providers/aws/resource_aws_lambda_function.go (about) 1 package aws 2 3 import ( 4 "crypto/sha256" 5 "fmt" 6 "io/ioutil" 7 "log" 8 "time" 9 10 "github.com/aws/aws-sdk-go/aws" 11 "github.com/aws/aws-sdk-go/aws/awserr" 12 "github.com/aws/aws-sdk-go/service/lambda" 13 "github.com/mitchellh/go-homedir" 14 15 "errors" 16 17 "github.com/hashicorp/terraform/helper/resource" 18 "github.com/hashicorp/terraform/helper/schema" 19 ) 20 21 func resourceAwsLambdaFunction() *schema.Resource { 22 return &schema.Resource{ 23 Create: resourceAwsLambdaFunctionCreate, 24 Read: resourceAwsLambdaFunctionRead, 25 Update: resourceAwsLambdaFunctionUpdate, 26 Delete: resourceAwsLambdaFunctionDelete, 27 28 Schema: map[string]*schema.Schema{ 29 "filename": &schema.Schema{ 30 Type: schema.TypeString, 31 Optional: true, 32 ConflictsWith: []string{"s3_bucket", "s3_key", "s3_object_version"}, 33 }, 34 "s3_bucket": &schema.Schema{ 35 Type: schema.TypeString, 36 Optional: true, 37 ConflictsWith: []string{"filename"}, 38 }, 39 "s3_key": &schema.Schema{ 40 Type: schema.TypeString, 41 Optional: true, 42 ConflictsWith: []string{"filename"}, 43 }, 44 "s3_object_version": &schema.Schema{ 45 Type: schema.TypeString, 46 Optional: true, 47 ConflictsWith: []string{"filename"}, 48 }, 49 "description": &schema.Schema{ 50 Type: schema.TypeString, 51 Optional: true, 52 }, 53 "function_name": &schema.Schema{ 54 Type: schema.TypeString, 55 Required: true, 56 ForceNew: true, 57 }, 58 "handler": &schema.Schema{ 59 Type: schema.TypeString, 60 Required: true, 61 ForceNew: true, // TODO make this editable 62 }, 63 "memory_size": &schema.Schema{ 64 Type: schema.TypeInt, 65 Optional: true, 66 Default: 128, 67 ForceNew: true, // TODO make this editable 68 }, 69 "role": &schema.Schema{ 70 Type: schema.TypeString, 71 Required: true, 72 ForceNew: true, // TODO make this editable 73 }, 74 "runtime": &schema.Schema{ 75 Type: schema.TypeString, 76 Optional: true, 77 ForceNew: true, 78 Default: "nodejs", 79 }, 80 "timeout": &schema.Schema{ 81 Type: schema.TypeInt, 82 Optional: true, 83 Default: 3, 84 ForceNew: true, // TODO make this editable 85 }, 86 "vpc_config": &schema.Schema{ 87 Type: schema.TypeList, 88 Optional: true, 89 ForceNew: true, 90 Elem: &schema.Resource{ 91 Schema: map[string]*schema.Schema{ 92 "subnet_ids": &schema.Schema{ 93 Type: schema.TypeSet, 94 Required: true, 95 ForceNew: true, 96 Elem: &schema.Schema{Type: schema.TypeString}, 97 Set: schema.HashString, 98 }, 99 "security_group_ids": &schema.Schema{ 100 Type: schema.TypeSet, 101 Required: true, 102 ForceNew: true, 103 Elem: &schema.Schema{Type: schema.TypeString}, 104 Set: schema.HashString, 105 }, 106 }, 107 }, 108 }, 109 "arn": &schema.Schema{ 110 Type: schema.TypeString, 111 Computed: true, 112 }, 113 "last_modified": &schema.Schema{ 114 Type: schema.TypeString, 115 Computed: true, 116 }, 117 "source_code_hash": &schema.Schema{ 118 Type: schema.TypeString, 119 Computed: true, 120 ForceNew: true, 121 }, 122 }, 123 } 124 } 125 126 // resourceAwsLambdaFunction maps to: 127 // CreateFunction in the API / SDK 128 func resourceAwsLambdaFunctionCreate(d *schema.ResourceData, meta interface{}) error { 129 conn := meta.(*AWSClient).lambdaconn 130 131 functionName := d.Get("function_name").(string) 132 iamRole := d.Get("role").(string) 133 134 log.Printf("[DEBUG] Creating Lambda Function %s with role %s", functionName, iamRole) 135 136 var functionCode *lambda.FunctionCode 137 if v, ok := d.GetOk("filename"); ok { 138 filename, err := homedir.Expand(v.(string)) 139 if err != nil { 140 return err 141 } 142 zipfile, err := ioutil.ReadFile(filename) 143 if err != nil { 144 return err 145 } 146 d.Set("source_code_hash", sha256.Sum256(zipfile)) 147 functionCode = &lambda.FunctionCode{ 148 ZipFile: zipfile, 149 } 150 } else { 151 s3Bucket, bucketOk := d.GetOk("s3_bucket") 152 s3Key, keyOk := d.GetOk("s3_key") 153 s3ObjectVersion, versionOk := d.GetOk("s3_object_version") 154 if !bucketOk || !keyOk || !versionOk { 155 return errors.New("s3_bucket, s3_key and s3_object_version must all be set while using S3 code source") 156 } 157 functionCode = &lambda.FunctionCode{ 158 S3Bucket: aws.String(s3Bucket.(string)), 159 S3Key: aws.String(s3Key.(string)), 160 S3ObjectVersion: aws.String(s3ObjectVersion.(string)), 161 } 162 } 163 164 params := &lambda.CreateFunctionInput{ 165 Code: functionCode, 166 Description: aws.String(d.Get("description").(string)), 167 FunctionName: aws.String(functionName), 168 Handler: aws.String(d.Get("handler").(string)), 169 MemorySize: aws.Int64(int64(d.Get("memory_size").(int))), 170 Role: aws.String(iamRole), 171 Runtime: aws.String(d.Get("runtime").(string)), 172 Timeout: aws.Int64(int64(d.Get("timeout").(int))), 173 } 174 175 if v, ok := d.GetOk("vpc_config"); ok { 176 configs := v.([]interface{}) 177 178 if len(configs) > 1 { 179 return errors.New("Only a single vpc_config block is expected") 180 } else if len(configs) == 1 { 181 config := configs[0].(map[string]interface{}) 182 var subnetIds []*string 183 for _, id := range config["subnet_ids"].(*schema.Set).List() { 184 subnetIds = append(subnetIds, aws.String(id.(string))) 185 } 186 187 var securityGroupIds []*string 188 for _, id := range config["security_group_ids"].(*schema.Set).List() { 189 securityGroupIds = append(securityGroupIds, aws.String(id.(string))) 190 } 191 192 var vpcConfig = &lambda.VpcConfig{ 193 SubnetIds: subnetIds, 194 SecurityGroupIds: securityGroupIds, 195 } 196 params.VpcConfig = vpcConfig 197 } 198 } 199 200 // IAM profiles can take ~10 seconds to propagate in AWS: 201 // http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role-console 202 // Error creating Lambda function: InvalidParameterValueException: The role defined for the task cannot be assumed by Lambda. 203 err := resource.Retry(1*time.Minute, func() error { 204 _, err := conn.CreateFunction(params) 205 if err != nil { 206 if awserr, ok := err.(awserr.Error); ok { 207 if awserr.Code() == "InvalidParameterValueException" { 208 log.Printf("[DEBUG] InvalidParameterValueException creating Lambda Function: %s", awserr) 209 // Retryable 210 return awserr 211 } 212 } 213 log.Printf("[DEBUG] Error creating Lambda Function: %s", err) 214 // Not retryable 215 return resource.RetryError{Err: err} 216 } 217 // No error 218 return nil 219 }) 220 if err != nil { 221 return fmt.Errorf("Error creating Lambda function: %s", err) 222 } 223 224 d.SetId(d.Get("function_name").(string)) 225 226 return resourceAwsLambdaFunctionRead(d, meta) 227 } 228 229 // resourceAwsLambdaFunctionRead maps to: 230 // GetFunction in the API / SDK 231 func resourceAwsLambdaFunctionRead(d *schema.ResourceData, meta interface{}) error { 232 conn := meta.(*AWSClient).lambdaconn 233 234 log.Printf("[DEBUG] Fetching Lambda Function: %s", d.Id()) 235 236 params := &lambda.GetFunctionInput{ 237 FunctionName: aws.String(d.Get("function_name").(string)), 238 } 239 240 getFunctionOutput, err := conn.GetFunction(params) 241 if err != nil { 242 return err 243 } 244 245 // getFunctionOutput.Code.Location is a pre-signed URL pointing at the zip 246 // file that we uploaded when we created the resource. You can use it to 247 // download the code from AWS. The other part is 248 // getFunctionOutput.Configuration which holds metadata. 249 250 function := getFunctionOutput.Configuration 251 // TODO error checking / handling on the Set() calls. 252 d.Set("arn", function.FunctionArn) 253 d.Set("description", function.Description) 254 d.Set("handler", function.Handler) 255 d.Set("memory_size", function.MemorySize) 256 d.Set("last_modified", function.LastModified) 257 d.Set("role", function.Role) 258 d.Set("runtime", function.Runtime) 259 d.Set("timeout", function.Timeout) 260 d.Set("vpc_config", flattenLambdaVpcConfigResponse(function.VpcConfig)) 261 262 return nil 263 } 264 265 // resourceAwsLambdaFunction maps to: 266 // DeleteFunction in the API / SDK 267 func resourceAwsLambdaFunctionDelete(d *schema.ResourceData, meta interface{}) error { 268 conn := meta.(*AWSClient).lambdaconn 269 270 log.Printf("[INFO] Deleting Lambda Function: %s", d.Id()) 271 272 params := &lambda.DeleteFunctionInput{ 273 FunctionName: aws.String(d.Get("function_name").(string)), 274 } 275 276 _, err := conn.DeleteFunction(params) 277 if err != nil { 278 return fmt.Errorf("Error deleting Lambda Function: %s", err) 279 } 280 281 d.SetId("") 282 283 return nil 284 } 285 286 // resourceAwsLambdaFunctionUpdate maps to: 287 // UpdateFunctionCode in the API / SDK 288 func resourceAwsLambdaFunctionUpdate(d *schema.ResourceData, meta interface{}) error { 289 // conn := meta.(*AWSClient).lambdaconn 290 291 return nil 292 }