├── .gitignore ├── LICENSE ├── README.md ├── archive.tf ├── aws.tf ├── build.py ├── builds └── .gitignore ├── built.py ├── hash.py ├── iam.tf ├── lambda.tf ├── outputs.tf ├── terraform.tf ├── tests ├── .tool-versions ├── assume-roles │ ├── lambda.py │ └── main.tf ├── build-command │ ├── lambda │ │ ├── build.sh │ │ └── src │ │ │ ├── main.py │ │ │ └── requirements.txt │ └── main.tf ├── dead-letter-queue │ ├── lambda.py │ └── main.tf ├── environment-variables │ ├── lambda.py │ └── main.tf ├── layers │ ├── lambda.py │ └── main.tf ├── nested-modules │ ├── main.tf │ ├── nested1 │ │ ├── lambda1.py │ │ └── main.tf │ └── nested2 │ │ ├── lambda2 │ │ ├── __init__.py │ │ ├── main.py │ │ └── requirements.txt │ │ └── main.tf ├── policy │ ├── lambda.py │ └── main.tf ├── python-versions │ ├── lambda │ │ ├── __init__.py │ │ ├── main.py │ │ ├── requirements.txt │ │ └── test_result │ │ │ └── __init__.py │ ├── python2 │ │ ├── .envrc │ │ └── main.tf │ └── python3 │ │ ├── .envrc │ │ └── main.tf └── vpc-config │ ├── lambda.py │ └── main.tf └── variables.tf /.gitignore: -------------------------------------------------------------------------------- 1 | .terraform/ 2 | *.tfstate* 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | Copyright (c) 2017 Claranet 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # _DEPRECATION WARNING_ 2 | 3 | This module is lacking active maintainance and is being deprecated. Users are encouraged to migrate to the actively maintained https://github.com/terraform-aws-modules/terraform-aws-lambda community module. This repository will be marked as _archived_ but will stay online for the foreseeable future. 4 | 5 | # terraform-aws-lambda 6 | 7 | This Terraform module creates and uploads an AWS Lambda function and hides the ugly parts from you. 8 | 9 | ## Features 10 | 11 | * Only appears in the Terraform plan when there are legitimate changes. 12 | * Creates a standard IAM role and policy for CloudWatch Logs. 13 | * You can add additional policies if required. 14 | * Zips up a source file or directory. 15 | * Installs dependencies from `requirements.txt` for Python functions. 16 | * It only does this when necessary, not every time. 17 | 18 | ## Requirements 19 | 20 | * Python 2.7 or higher 21 | * Linux/Unix/Windows 22 | 23 | ## Terraform version compatibility 24 | 25 | | Module version | Terraform version | 26 | |----------------|-------------------| 27 | | 1.x.x | 0.12.x | 28 | | 0.x.x | 0.11.x | 29 | 30 | ## Usage 31 | 32 | ```js 33 | module "lambda" { 34 | source = "github.com/claranet/terraform-aws-lambda" 35 | 36 | function_name = "deployment-deploy-status" 37 | description = "Deployment deploy status task" 38 | handler = "main.lambda_handler" 39 | runtime = "python3.6" 40 | timeout = 300 41 | 42 | // Specify a file or directory for the source code. 43 | source_path = "${path.module}/lambda.py" 44 | 45 | // Add additional trusted entities for assuming roles (trust relationships). 46 | trusted_entities = ["events.amazonaws.com", "s3.amazonaws.com"] 47 | 48 | // Attach a policy. 49 | policy = { 50 | json = data.aws_iam_policy_document.lambda.json 51 | } 52 | 53 | // Add a dead letter queue. 54 | dead_letter_config = { 55 | target_arn = aws_sqs_queue.dlq.arn 56 | } 57 | 58 | // Add environment variables. 59 | environment = { 60 | variables = { 61 | SLACK_URL = var.slack_url 62 | } 63 | } 64 | 65 | // Deploy into a VPC. 66 | vpc_config = { 67 | subnet_ids = [aws_subnet.test.id] 68 | security_group_ids = [aws_security_group.test.id] 69 | } 70 | } 71 | ``` 72 | 73 | ## Inputs 74 | 75 | Inputs for this module are the same as the [aws_lambda_function](https://www.terraform.io/docs/providers/aws/r/lambda_function.html) resource with the following additional arguments: 76 | 77 | | Name | Description | Type | Default | Required | 78 | |------|-------------|------|---------|----------| 79 | | **source\_path** | The absolute path to a local file or directory containing your Lambda source code | `string` | | yes | 80 | | build\_command | The command to run to create the Lambda package zip file | `string` | `"python build.py '$filename' '$runtime' '$source'"` | no | 81 | | build\_paths | The files or directories used by the build command, to trigger new Lambda package builds whenever build scripts change | `list(string)` | `["build.py"]` | no | 82 | | cloudwatch\_logs | Set this to false to disable logging your Lambda output to CloudWatch Logs | `bool` | `true` | no | 83 | | lambda\_at\_edge | Set this to true if using Lambda@Edge, to enable publishing, limit the timeout, and allow edgelambda.amazonaws.com to invoke the function | `bool` | `false` | no | 84 | | policy | An additional policy to attach to the Lambda function role | `object({json=string})` | | no | 85 | | trusted\_entities | Additional trusted entities for the Lambda function. The lambda.amazonaws.com (and edgelambda.amazonaws.com if lambda\_at\_edge is true) is always set | `list(string)` | | no | 86 | | enabled | Enabling and disaling of resources | `bool` | `true` | no | 87 | 88 | The following arguments from the [aws_lambda_function](https://www.terraform.io/docs/providers/aws/r/lambda_function.html) resource are not supported: 89 | 90 | * filename (use source\_path instead) 91 | * role (one is automatically created) 92 | * s3_bucket 93 | * s3_key 94 | * s3_object_version 95 | * source_code_hash (changes are handled automatically) 96 | 97 | ## Outputs 98 | 99 | | Name | Description | 100 | |------|-------------| 101 | | function\_arn | The ARN of the Lambda function | 102 | | function\_invoke\_arn | The Invoke ARN of the Lambda function | 103 | | function\_name | The name of the Lambda function | 104 | | function\_qualified\_arn | The qualified ARN of the Lambda function | 105 | | role\_arn | The ARN of the IAM role created for the Lambda function | 106 | | role\_name | The name of the IAM role created for the Lambda function | 107 | -------------------------------------------------------------------------------- /archive.tf: -------------------------------------------------------------------------------- 1 | # Generates a filename for the zip archive based on the contents of the files 2 | # in source_path. The filename will change when the source code changes. 3 | data "external" "archive" { 4 | count = var.enabled ? 1 : 0 5 | 6 | program = ["python", "${path.module}/hash.py"] 7 | 8 | query = { 9 | build_command = var.build_command 10 | build_paths = jsonencode(var.build_paths) 11 | module_relpath = path.module 12 | runtime = var.runtime 13 | source_path = var.source_path 14 | } 15 | } 16 | 17 | # Build the zip archive whenever the filename changes. 18 | resource "null_resource" "archive" { 19 | count = var.enabled ? 1 : 0 20 | 21 | triggers = { 22 | filename = lookup(data.external.archive[0].result, "filename") 23 | } 24 | 25 | provisioner "local-exec" { 26 | command = lookup(data.external.archive[0].result, "build_command") 27 | working_dir = path.module 28 | } 29 | } 30 | 31 | # Check that the null_resource.archive file has been built. This will rebuild 32 | # it if missing. This is used to catch situations where the Terraform state 33 | # does not match the Lambda function in AWS, e.g. after someone manually 34 | # deletes the Lambda function. If the file is rebuilt here, the build 35 | # output is unfortunately invisible. 36 | data "external" "built" { 37 | count = var.enabled ? 1 : 0 38 | 39 | program = ["python", "${path.module}/built.py"] 40 | 41 | query = { 42 | build_command = lookup(data.external.archive[0].result, "build_command") 43 | filename_old = lookup(null_resource.archive[0].triggers, "filename") 44 | filename_new = lookup(data.external.archive[0].result, "filename") 45 | module_relpath = path.module 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /aws.tf: -------------------------------------------------------------------------------- 1 | data "aws_caller_identity" "current" {} 2 | 3 | data "aws_partition" "current" {} 4 | 5 | data "aws_region" "current" {} 6 | -------------------------------------------------------------------------------- /build.py: -------------------------------------------------------------------------------- 1 | # Builds a zip file from the source_dir or source_file. 2 | # Installs dependencies with pip automatically. 3 | 4 | import os 5 | import shlex 6 | import shutil 7 | import subprocess 8 | import sys 9 | import tempfile 10 | 11 | from contextlib import contextmanager 12 | 13 | 14 | @contextmanager 15 | def cd(path): 16 | """ 17 | Changes the working directory. 18 | 19 | """ 20 | 21 | cwd = os.getcwd() 22 | print('cd', path) 23 | try: 24 | os.chdir(path) 25 | yield 26 | finally: 27 | os.chdir(cwd) 28 | 29 | 30 | def format_command(command): 31 | """ 32 | Formats a command for displaying on screen. 33 | 34 | """ 35 | 36 | args = [] 37 | for arg in command: 38 | if ' ' in arg: 39 | args.append('"' + arg + '"') 40 | else: 41 | args.append(arg) 42 | return ' '.join(args) 43 | 44 | 45 | def list_files(top_path): 46 | """ 47 | Returns a sorted list of all files in a directory. 48 | 49 | """ 50 | 51 | results = [] 52 | 53 | for root, dirs, files in os.walk(top_path): 54 | if root.endswith("__pycache__"): 55 | continue 56 | for file_name in files: 57 | file_path = os.path.join(root, file_name) 58 | relative_path = os.path.relpath(file_path, top_path) 59 | results.append(relative_path) 60 | 61 | results.sort() 62 | return results 63 | 64 | 65 | def run(*args, **kwargs): 66 | """ 67 | Runs a command. 68 | 69 | """ 70 | 71 | print(format(format_command(args))) 72 | sys.stdout.flush() 73 | subprocess.check_call(args, **kwargs) 74 | 75 | 76 | @contextmanager 77 | def tempdir(): 78 | """ 79 | Creates a temporary directory and then deletes it afterwards. 80 | 81 | """ 82 | 83 | print('mktemp -d') 84 | path = tempfile.mkdtemp(prefix='terraform-aws-lambda-') 85 | print(path) 86 | try: 87 | yield path 88 | finally: 89 | shutil.rmtree(path) 90 | 91 | 92 | def create_zip_file(source_dir, target_file): 93 | """ 94 | Creates a zip file from a directory. 95 | 96 | """ 97 | 98 | target_file = os.path.abspath(target_file) 99 | target_dir = os.path.dirname(target_file) 100 | if not os.path.exists(target_dir): 101 | os.makedirs(target_dir) 102 | target_base, _ = os.path.splitext(target_file) 103 | shutil.make_archive( 104 | target_base, 105 | format='zip', 106 | root_dir=source_dir, 107 | ) 108 | 109 | 110 | def dequote(value): 111 | """ 112 | Handles quotes around values in a shell-compatible fashion. 113 | 114 | """ 115 | return ' '.join(shlex.split(value)) 116 | 117 | 118 | filename = dequote(sys.argv[1]) 119 | runtime = dequote(sys.argv[2]) 120 | source_path = dequote(sys.argv[3]) 121 | 122 | absolute_filename = os.path.abspath(filename) 123 | 124 | # Create a temporary directory for building the archive, 125 | # so no changes will be made to the source directory. 126 | with tempdir() as temp_dir: 127 | 128 | # Find all source files. 129 | if os.path.isdir(source_path): 130 | source_dir = source_path 131 | source_files = list_files(source_path) 132 | else: 133 | source_dir = os.path.dirname(source_path) 134 | source_files = [os.path.basename(source_path)] 135 | 136 | # Copy them into the temporary directory. 137 | with cd(source_dir): 138 | for file_name in source_files: 139 | target_path = os.path.join(temp_dir, file_name) 140 | target_dir = os.path.dirname(target_path) 141 | if not os.path.exists(target_dir): 142 | print('mkdir -p {}'.format(target_dir)) 143 | os.makedirs(target_dir) 144 | print('cp {} {}'.format(file_name, target_path)) 145 | shutil.copyfile(file_name, target_path) 146 | shutil.copymode(file_name, target_path) 147 | 148 | # Install dependencies into the temporary directory. 149 | if runtime.startswith('python'): 150 | requirements = os.path.join(temp_dir, 'requirements.txt') 151 | if os.path.exists(requirements): 152 | with cd(temp_dir): 153 | if runtime.startswith('python3'): 154 | pip_command = 'pip3' 155 | else: 156 | pip_command = 'pip2' 157 | run( 158 | pip_command, 159 | 'install', 160 | '--prefix=', 161 | '--target=.', 162 | '--requirement=requirements.txt', 163 | ) 164 | 165 | # Zip up the temporary directory and write it to the target filename. 166 | # This will be used by the Lambda function as the source code package. 167 | create_zip_file(temp_dir, absolute_filename) 168 | print('Created {}'.format(filename)) 169 | -------------------------------------------------------------------------------- /builds/.gitignore: -------------------------------------------------------------------------------- 1 | *.zip 2 | -------------------------------------------------------------------------------- /built.py: -------------------------------------------------------------------------------- 1 | # Builds missing zip files included in the 2 | # Terraform state but missing from the disk. 3 | 4 | import json 5 | import os 6 | import subprocess 7 | import sys 8 | 9 | 10 | # Parse the query. 11 | query = json.load(sys.stdin) 12 | build_command = query['build_command'] 13 | filename_old = query['filename_old'] 14 | filename_new = query['filename_new'] 15 | module_relpath = query['module_relpath'] 16 | 17 | # If the old filename (from the Terraform state) matches the new filename 18 | # (from hash.py) then the source code has not changed and thus the zip file 19 | # should not have changed. 20 | if filename_old == filename_new: 21 | if os.path.exists(filename_new): 22 | # Update the file time so it doesn't get cleaned up, 23 | # which would result in an unnecessary rebuild. 24 | os.utime(filename_new, None) 25 | else: 26 | # If the file is missing, then it was probably generated on another 27 | # machine, or it was created a long time ago and cleaned up. This is 28 | # expected behaviour. However if Terraform needs to upload the file 29 | # (e.g. someone manually deleted the Lambda function via the AWS 30 | # console) then it is possible that Terraform will try to upload 31 | # the missing file. I don't know how to tell if Terraform is going 32 | # to try to upload the file or not, so always ensure the file exists. 33 | subprocess.check_output(build_command, shell=True, cwd=module_relpath) 34 | 35 | # Output the filename to Terraform. 36 | json.dump({ 37 | 'filename': module_relpath + '/' + filename_new, 38 | }, sys.stdout, indent=2) 39 | sys.stdout.write('\n') 40 | -------------------------------------------------------------------------------- /hash.py: -------------------------------------------------------------------------------- 1 | # Generates a content hash of the source_path, which is used to determine if 2 | # the Lambda code has changed, ignoring file modification and access times. 3 | # 4 | # Outputs a filename and a command to run if the archive needs to be built. 5 | 6 | import datetime 7 | import errno 8 | import hashlib 9 | import json 10 | import os 11 | import sys 12 | 13 | 14 | def abort(message): 15 | """ 16 | Exits with an error message. 17 | 18 | """ 19 | 20 | sys.stderr.write(message + '\n') 21 | sys.exit(1) 22 | 23 | 24 | def delete_old_archives(): 25 | """ 26 | Deletes previously created archives. 27 | 28 | """ 29 | 30 | now = datetime.datetime.now() 31 | delete_older_than = now - datetime.timedelta(days=7) 32 | 33 | for name in os.listdir('builds'): 34 | if name.endswith('.zip'): 35 | try: 36 | file_modified = datetime.datetime.fromtimestamp( 37 | os.path.getmtime(name) 38 | ) 39 | if file_modified < delete_older_than: 40 | os.remove(name) 41 | except OSError as error: 42 | if error.errno == errno.ENOENT: 43 | # Ignore "not found" errors as they are probably race 44 | # conditions between multiple usages of this module. 45 | pass 46 | else: 47 | raise 48 | 49 | 50 | def list_files(top_path): 51 | """ 52 | Returns a sorted list of all files in a directory. 53 | 54 | """ 55 | 56 | results = [] 57 | 58 | for root, dirs, files in os.walk(top_path): 59 | if root.endswith("__pycache__"): 60 | continue 61 | for file_name in files: 62 | results.append(os.path.join(root, file_name)) 63 | 64 | results.sort() 65 | return results 66 | 67 | 68 | def generate_content_hash(source_paths): 69 | """ 70 | Generate a content hash of the source paths. 71 | 72 | """ 73 | 74 | sha256 = hashlib.sha256() 75 | 76 | for source_path in source_paths: 77 | if os.path.isdir(source_path): 78 | source_dir = source_path 79 | for source_file in list_files(source_dir): 80 | update_hash(sha256, source_dir, source_file) 81 | else: 82 | source_dir = os.path.dirname(source_path) 83 | source_file = source_path 84 | update_hash(sha256, source_dir, source_file) 85 | 86 | return sha256 87 | 88 | 89 | def update_hash(hash_obj, file_root, file_path): 90 | """ 91 | Update a hashlib object with the relative path and contents of a file. 92 | 93 | """ 94 | 95 | relative_path = os.path.relpath(file_path, file_root) 96 | hash_obj.update(relative_path.encode()) 97 | 98 | with open(file_path, 'rb') as open_file: 99 | while True: 100 | data = open_file.read(1024) 101 | if not data: 102 | break 103 | hash_obj.update(data) 104 | 105 | 106 | # Parse the query. 107 | query = json.load(sys.stdin) 108 | build_command = query['build_command'] 109 | build_paths = json.loads(query['build_paths']) 110 | module_relpath = query['module_relpath'] 111 | runtime = query['runtime'] 112 | source_path = os.path.abspath(query['source_path']) 113 | 114 | # Validate the query. 115 | if not source_path: 116 | abort('source_path must be set.') 117 | 118 | # Change working directory to the module path 119 | # so references to build.py will work. 120 | os.chdir(module_relpath) 121 | 122 | # Generate a hash based on file names and content. Also use the 123 | # runtime value, build command, and content of the build paths 124 | # because they can have an effect on the resulting archive. 125 | content_hash = generate_content_hash([source_path] + build_paths) 126 | content_hash.update(runtime.encode()) 127 | content_hash.update(build_command.encode()) 128 | 129 | # Generate a unique filename based on the hash. 130 | filename = 'builds/{content_hash}.zip'.format( 131 | content_hash=content_hash.hexdigest(), 132 | ) 133 | 134 | # Replace variables in the build command with calculated values. 135 | replacements = { 136 | '$filename': filename, 137 | '$runtime': runtime, 138 | '$source': source_path, 139 | } 140 | for old, new in replacements.items(): 141 | build_command = build_command.replace(old, new) 142 | 143 | # Delete previous archives. 144 | delete_old_archives() 145 | 146 | # Output the result to Terraform. 147 | json.dump({ 148 | 'filename': filename, 149 | 'build_command': build_command, 150 | }, sys.stdout, indent=2) 151 | sys.stdout.write('\n') 152 | -------------------------------------------------------------------------------- /iam.tf: -------------------------------------------------------------------------------- 1 | # Create the role. 2 | 3 | data "aws_iam_policy_document" "assume_role" { 4 | count = var.enabled ? 1 : 0 5 | 6 | statement { 7 | effect = "Allow" 8 | actions = ["sts:AssumeRole"] 9 | 10 | principals { 11 | type = "Service" 12 | identifiers = concat(slice(["lambda.amazonaws.com", "edgelambda.amazonaws.com"], 0, var.lambda_at_edge ? 2 : 1), var.trusted_entities) 13 | } 14 | } 15 | } 16 | 17 | resource "aws_iam_role" "lambda" { 18 | count = var.enabled ? 1 : 0 19 | 20 | name = var.function_name 21 | assume_role_policy = data.aws_iam_policy_document.assume_role[0].json 22 | tags = var.tags 23 | } 24 | 25 | # Attach a policy for logs. 26 | 27 | locals { 28 | lambda_log_group_arn = "arn:${data.aws_partition.current.partition}:logs:*:${data.aws_caller_identity.current.account_id}:log-group:/aws/lambda/${var.function_name}" 29 | lambda_edge_log_group_arn = "arn:${data.aws_partition.current.partition}:logs:*:${data.aws_caller_identity.current.account_id}:log-group:/aws/lambda/us-east-1.${var.function_name}" 30 | log_group_arns = slice([local.lambda_log_group_arn, local.lambda_edge_log_group_arn], 0, var.lambda_at_edge ? 2 : 1) 31 | } 32 | 33 | data "aws_iam_policy_document" "logs" { 34 | count = var.enabled && var.cloudwatch_logs ? 1 : 0 35 | 36 | statement { 37 | effect = "Allow" 38 | 39 | actions = [ 40 | "logs:CreateLogGroup", 41 | ] 42 | 43 | resources = [ 44 | "*", 45 | ] 46 | } 47 | 48 | statement { 49 | effect = "Allow" 50 | 51 | actions = [ 52 | "logs:CreateLogStream", 53 | "logs:PutLogEvents", 54 | ] 55 | 56 | resources = concat(formatlist("%v:*", local.log_group_arns), formatlist("%v:*:*", local.log_group_arns)) 57 | } 58 | } 59 | 60 | resource "aws_iam_policy" "logs" { 61 | count = var.enabled && var.cloudwatch_logs ? 1 : 0 62 | 63 | name = "${var.function_name}-logs" 64 | policy = data.aws_iam_policy_document.logs[0].json 65 | } 66 | 67 | resource "aws_iam_policy_attachment" "logs" { 68 | count = var.enabled && var.cloudwatch_logs ? 1 : 0 69 | 70 | name = "${var.function_name}-logs" 71 | roles = [aws_iam_role.lambda[0].name] 72 | policy_arn = aws_iam_policy.logs[0].arn 73 | } 74 | 75 | # Attach an additional policy required for the dead letter config. 76 | 77 | data "aws_iam_policy_document" "dead_letter" { 78 | count = var.dead_letter_config == null ? 0 : var.enabled ? 1 : 0 79 | 80 | statement { 81 | effect = "Allow" 82 | 83 | actions = [ 84 | "sns:Publish", 85 | "sqs:SendMessage", 86 | ] 87 | 88 | resources = [ 89 | var.dead_letter_config.target_arn, 90 | ] 91 | } 92 | } 93 | 94 | resource "aws_iam_policy" "dead_letter" { 95 | count = var.dead_letter_config == null ? 0 : var.enabled ? 1 : 0 96 | 97 | name = "${var.function_name}-dl" 98 | policy = data.aws_iam_policy_document.dead_letter[0].json 99 | } 100 | 101 | resource "aws_iam_policy_attachment" "dead_letter" { 102 | count = var.dead_letter_config == null ? 0 : var.enabled ? 1 : 0 103 | 104 | name = "${var.function_name}-dl" 105 | roles = [aws_iam_role.lambda[0].name] 106 | policy_arn = aws_iam_policy.dead_letter[0].arn 107 | } 108 | 109 | # Attach an additional policy required for the VPC config 110 | 111 | data "aws_iam_policy_document" "network" { 112 | count = var.vpc_config == null ? 0 : var.enabled ? 1 : 0 113 | 114 | statement { 115 | effect = "Allow" 116 | 117 | actions = [ 118 | "ec2:CreateNetworkInterface", 119 | "ec2:DescribeNetworkInterfaces", 120 | "ec2:DeleteNetworkInterface", 121 | ] 122 | 123 | resources = [ 124 | "*", 125 | ] 126 | } 127 | } 128 | 129 | resource "aws_iam_policy" "network" { 130 | count = var.vpc_config == null ? 0 : var.enabled ? 1 : 0 131 | 132 | name = "${var.function_name}-network" 133 | policy = data.aws_iam_policy_document.network[0].json 134 | } 135 | 136 | resource "aws_iam_policy_attachment" "network" { 137 | count = var.vpc_config == null ? 0 : var.enabled ? 1 : 0 138 | 139 | name = "${var.function_name}-network" 140 | roles = [aws_iam_role.lambda[0].name] 141 | policy_arn = aws_iam_policy.network[0].arn 142 | } 143 | 144 | # Attach an additional policy if provided. 145 | 146 | resource "aws_iam_policy" "additional" { 147 | count = var.policy == null ? 0 : var.enabled ? 1 : 0 148 | 149 | name = var.function_name 150 | policy = var.policy.json 151 | } 152 | 153 | resource "aws_iam_policy_attachment" "additional" { 154 | count = var.policy == null ? 0 : var.enabled ? 1 : 0 155 | 156 | name = var.function_name 157 | roles = [aws_iam_role.lambda[0].name] 158 | policy_arn = aws_iam_policy.additional[0].arn 159 | } 160 | -------------------------------------------------------------------------------- /lambda.tf: -------------------------------------------------------------------------------- 1 | resource "aws_lambda_function" "lambda" { 2 | count = var.enabled ? 1 : 0 3 | function_name = var.function_name 4 | description = var.description 5 | role = aws_iam_role.lambda[0].arn 6 | handler = var.handler 7 | memory_size = var.memory_size 8 | reserved_concurrent_executions = var.reserved_concurrent_executions 9 | runtime = var.runtime 10 | layers = var.layers 11 | timeout = local.timeout 12 | publish = local.publish 13 | tags = var.tags 14 | 15 | # Use a generated filename to determine when the source code has changed. 16 | 17 | filename = data.external.built[0].result.filename 18 | depends_on = [null_resource.archive] 19 | 20 | # Add dynamic blocks based on variables. 21 | 22 | dynamic "dead_letter_config" { 23 | for_each = var.dead_letter_config == null ? [] : [var.dead_letter_config] 24 | content { 25 | target_arn = dead_letter_config.value.target_arn 26 | } 27 | } 28 | 29 | dynamic "environment" { 30 | for_each = var.environment == null ? [] : [var.environment] 31 | content { 32 | variables = environment.value.variables 33 | } 34 | } 35 | 36 | dynamic "tracing_config" { 37 | for_each = var.tracing_config == null ? [] : [var.tracing_config] 38 | content { 39 | mode = tracing_config.value.mode 40 | } 41 | } 42 | 43 | dynamic "vpc_config" { 44 | for_each = var.vpc_config == null ? [] : [var.vpc_config] 45 | content { 46 | security_group_ids = vpc_config.value.security_group_ids 47 | subnet_ids = vpc_config.value.subnet_ids 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "function_arn" { 2 | description = "The ARN of the Lambda function" 3 | value = join("", aws_lambda_function.lambda.*.arn) 4 | } 5 | 6 | output "function_invoke_arn" { 7 | description = "The Invoke ARN of the Lambda function" 8 | value = join("", aws_lambda_function.lambda.*.invoke_arn) 9 | } 10 | 11 | output "function_name" { 12 | description = "The name of the Lambda function" 13 | value = join("", aws_lambda_function.lambda.*.function_name) 14 | } 15 | 16 | output "function_qualified_arn" { 17 | description = "The qualified ARN of the Lambda function" 18 | value = join("", aws_lambda_function.lambda.*.qualified_arn) 19 | } 20 | 21 | output "role_arn" { 22 | description = "The ARN of the IAM role created for the Lambda function" 23 | value = join("", aws_iam_role.lambda.*.arn) 24 | } 25 | 26 | output "role_name" { 27 | description = "The name of the IAM role created for the Lambda function" 28 | value = join("", aws_iam_role.lambda.*.name) 29 | } 30 | -------------------------------------------------------------------------------- /terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.12.0" 3 | } 4 | -------------------------------------------------------------------------------- /tests/.tool-versions: -------------------------------------------------------------------------------- 1 | terraform 0.12.1 2 | -------------------------------------------------------------------------------- /tests/assume-roles/lambda.py: -------------------------------------------------------------------------------- 1 | def lambda_handler(event, context): 2 | if event['pass']: 3 | return True 4 | else: 5 | raise Exception('oh no') 6 | -------------------------------------------------------------------------------- /tests/assume-roles/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "local" { 3 | path = "terraform.tfstate" 4 | } 5 | } 6 | 7 | provider "aws" { 8 | region = "eu-west-1" 9 | } 10 | 11 | resource "random_id" "name" { 12 | byte_length = 6 13 | prefix = "terraform-aws-lambda-policy-" 14 | } 15 | 16 | resource "aws_sqs_queue" "test" { 17 | name = random_id.name.hex 18 | } 19 | 20 | data "aws_iam_policy_document" "computed" { 21 | statement { 22 | effect = "Allow" 23 | 24 | actions = [ 25 | "sqs:SendMessage", 26 | ] 27 | 28 | resources = [ 29 | aws_sqs_queue.test.arn, 30 | ] 31 | } 32 | } 33 | 34 | data "aws_iam_policy_document" "known" { 35 | statement { 36 | effect = "Deny" 37 | 38 | actions = [ 39 | "sqs:SendMessage", 40 | ] 41 | 42 | resources = [ 43 | "*", 44 | ] 45 | } 46 | } 47 | 48 | module "lambda_with_computed_policy_add_trust_relationships" { 49 | source = "../../" 50 | 51 | function_name = "${random_id.name.hex}-computed" 52 | description = "Test attaching policy with additional trust relationships in terraform-aws-lambda" 53 | handler = "lambda.lambda_handler" 54 | runtime = "python3.6" 55 | 56 | source_path = "${path.module}/lambda.py" 57 | 58 | trusted_entities = ["events.amazonaws.com"] 59 | 60 | policy = { 61 | json = data.aws_iam_policy_document.computed.json 62 | } 63 | } 64 | 65 | 66 | module "lambda_with_known_policy_add_trust_relationships" { 67 | source = "../../" 68 | 69 | function_name = "${random_id.name.hex}-known" 70 | description = "Test attaching policy with additional trust relationships in terraform-aws-lambda" 71 | handler = "lambda.lambda_handler" 72 | runtime = "python3.6" 73 | 74 | source_path = "${path.module}/lambda.py" 75 | 76 | trusted_entities = ["events.amazonaws.com"] 77 | 78 | policy = { 79 | json = data.aws_iam_policy_document.known.json 80 | } 81 | } 82 | 83 | 84 | module "lambda_without_policy_add_trust_relationships" { 85 | source = "../../" 86 | 87 | function_name = "${random_id.name.hex}-without" 88 | description = "Test attaching policy with additional trust relationships in terraform-aws-lambda" 89 | handler = "lambda.lambda_handler" 90 | runtime = "python3.6" 91 | 92 | source_path = "${path.module}/lambda.py" 93 | 94 | trusted_entities = ["events.amazonaws.com"] 95 | } 96 | 97 | module "lambda_without_policy_without_added_trust_relationships" { 98 | source = "../../" 99 | 100 | function_name = "${random_id.name.hex}-without" 101 | description = "Test attaching policy with additional trust relationships in terraform-aws-lambda" 102 | handler = "lambda.lambda_handler" 103 | runtime = "python3.6" 104 | 105 | source_path = "${path.module}/lambda.py" 106 | } 107 | -------------------------------------------------------------------------------- /tests/build-command/lambda/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Compiles a Python package into a zip deployable on AWS Lambda. 4 | # 5 | # - Builds Python dependencies into the package, using a Docker image to 6 | # correctly build native extensions 7 | # - Able to be used with the terraform-aws-lambda module 8 | # 9 | # Dependencies: 10 | # 11 | # - Docker 12 | # 13 | # Usage: 14 | # 15 | # $ ./build.sh 16 | 17 | set -euo pipefail 18 | 19 | # Read variables from command line arguments 20 | FILENAME=$1 21 | RUNTIME=$2 22 | SOURCE_PATH=$3 23 | 24 | # Convert to absolute paths 25 | SOURCE_DIR=$(cd "$SOURCE_PATH" && pwd) 26 | ZIP_DIR=$(cd "$(dirname "$FILENAME")" && pwd) 27 | ZIP_NAME=$(basename "$FILENAME") 28 | 29 | # Install dependencies, using a Docker image to correctly build native extensions 30 | docker run --rm -t -v "$SOURCE_DIR:/src" -v "$ZIP_DIR:/out" lambci/lambda:build-$RUNTIME sh -c " 31 | cp -r /src /build && 32 | cd /build && 33 | pip install --progress-bar off -r requirements.txt -t . && 34 | chmod -R 755 . && 35 | zip -r /out/$ZIP_NAME . && 36 | chown \$(stat -c '%u:%g' /out) /out/$ZIP_NAME 37 | " 38 | 39 | echo "Created $FILENAME from $SOURCE_PATH" 40 | -------------------------------------------------------------------------------- /tests/build-command/lambda/src/main.py: -------------------------------------------------------------------------------- 1 | def lambda_handler(event, context): 2 | print('importing numpy package') 3 | import numpy as np 4 | print('checking numpy works correctly') 5 | assert np.array_equal(np.array([1, 2]) + 3, np.array([4, 5])) 6 | return 'test passed' 7 | -------------------------------------------------------------------------------- /tests/build-command/lambda/src/requirements.txt: -------------------------------------------------------------------------------- 1 | # numpy has native extensions, needs a custom build script to 2 | # install correctly if your host OS differs to Lambda OS 3 | numpy 4 | -------------------------------------------------------------------------------- /tests/build-command/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "local" { 3 | path = "terraform.tfstate" 4 | } 5 | } 6 | 7 | provider "aws" { 8 | region = "eu-west-1" 9 | } 10 | 11 | module "lambda" { 12 | source = "../../" 13 | 14 | function_name = "terraform-aws-lambda-test-build-command" 15 | description = "Test custom build command functionality in terraform-aws-lambda" 16 | handler = "main.lambda_handler" 17 | runtime = "python3.7" 18 | 19 | source_path = "${path.cwd}/lambda/src" 20 | 21 | build_command = "${path.cwd}/lambda/build.sh '$filename' '$runtime' '$source'" 22 | build_paths = ["${path.cwd}/lambda/build.sh"] 23 | } 24 | -------------------------------------------------------------------------------- /tests/dead-letter-queue/lambda.py: -------------------------------------------------------------------------------- 1 | def lambda_handler(event, context): 2 | if event['pass']: 3 | return True 4 | else: 5 | raise Exception('oh no') 6 | -------------------------------------------------------------------------------- /tests/dead-letter-queue/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "local" { 3 | path = "terraform.tfstate" 4 | } 5 | } 6 | 7 | provider "aws" { 8 | region = "eu-west-1" 9 | } 10 | 11 | resource "random_id" "name" { 12 | byte_length = 6 13 | prefix = "terraform-aws-lambda-dlq-" 14 | } 15 | 16 | resource "aws_sqs_queue" "dlq" { 17 | name = random_id.name.hex 18 | } 19 | 20 | module "lambda" { 21 | source = "../../" 22 | 23 | function_name = random_id.name.hex 24 | description = "Test dead letter queue in terraform-aws-lambda" 25 | handler = "lambda.lambda_handler" 26 | runtime = "python3.6" 27 | timeout = 30 28 | 29 | source_path = "${path.module}/lambda.py" 30 | 31 | dead_letter_config = { 32 | target_arn = aws_sqs_queue.dlq.arn 33 | } 34 | 35 | enabled = true 36 | } 37 | -------------------------------------------------------------------------------- /tests/environment-variables/lambda.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def lambda_handler(event, context): 5 | print(os.environ['ARN']) 6 | return 'test passed' 7 | -------------------------------------------------------------------------------- /tests/environment-variables/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "local" { 3 | path = "terraform.tfstate" 4 | } 5 | } 6 | 7 | provider "aws" { 8 | region = "eu-west-1" 9 | } 10 | 11 | resource "aws_iam_user" "test" { 12 | name = "terraform-aws-lambda-test-environment-variables" 13 | } 14 | 15 | module "lambda" { 16 | source = "../../" 17 | 18 | function_name = "terraform-aws-lambda-test-environment-variables" 19 | description = "Test environment variables in terraform-aws-lambda" 20 | handler = "lambda.lambda_handler" 21 | memory_size = 256 22 | reserved_concurrent_executions = 3 23 | runtime = "python3.6" 24 | timeout = 30 25 | 26 | source_path = "${path.module}/lambda.py" 27 | 28 | environment = { 29 | variables = { 30 | ARN = aws_iam_user.test.arn 31 | } 32 | } 33 | 34 | enabled = true 35 | } 36 | -------------------------------------------------------------------------------- /tests/layers/lambda.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | 4 | def lambda_handler(event, context): 5 | 6 | subprocess.run(['git']) 7 | 8 | return 'test passed' 9 | -------------------------------------------------------------------------------- /tests/layers/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "local" { 3 | path = "terraform.tfstate" 4 | } 5 | } 6 | 7 | provider "aws" { 8 | region = "eu-west-1" 9 | } 10 | 11 | module "lambda" { 12 | source = "../../" 13 | 14 | function_name = "terraform-aws-lambda-test-layers" 15 | description = "Test layers in terraform-aws-lambda" 16 | handler = "lambda.lambda_handler" 17 | runtime = "python3.7" 18 | 19 | layers = [ 20 | "arn:aws:lambda:::awslayer:AmazonLinux1803", # https://aws.amazon.com/blogs/compute/upcoming-updates-to-the-aws-lambda-execution-environment/ 21 | "arn:aws:lambda:eu-west-1:553035198032:layer:git:5", # https://github.com/lambci/git-lambda-layer 22 | ] 23 | 24 | source_path = "${path.module}/lambda.py" 25 | } 26 | -------------------------------------------------------------------------------- /tests/nested-modules/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "local" { 3 | path = "terraform.tfstate" 4 | } 5 | } 6 | 7 | provider "aws" { 8 | region = "eu-west-1" 9 | } 10 | 11 | module "nested1" { 12 | source = "./nested1" 13 | } 14 | 15 | module "nested2" { 16 | source = "./nested2" 17 | } 18 | -------------------------------------------------------------------------------- /tests/nested-modules/nested1/lambda1.py: -------------------------------------------------------------------------------- 1 | def lambda_handler(event, context): 2 | return 'test passed' 3 | -------------------------------------------------------------------------------- /tests/nested-modules/nested1/main.tf: -------------------------------------------------------------------------------- 1 | module "lambda" { 2 | source = "../../../" 3 | 4 | function_name = "terraform-aws-lambda-test-nested-modules-1" 5 | description = "Test nested modules functionality in terraform-aws-lambda" 6 | handler = "lambda1.lambda_handler" 7 | runtime = "python3.6" 8 | timeout = 30 9 | 10 | source_path = "${path.module}/lambda1.py" 11 | } 12 | -------------------------------------------------------------------------------- /tests/nested-modules/nested2/lambda2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claranet/terraform-aws-lambda/1e4bd6cbd434c6f49fa18290f281434e2022e7c4/tests/nested-modules/nested2/lambda2/__init__.py -------------------------------------------------------------------------------- /tests/nested-modules/nested2/lambda2/main.py: -------------------------------------------------------------------------------- 1 | def lambda_handler(event, context): 2 | import timeprint 3 | with timeprint: 4 | return 'test passed' 5 | -------------------------------------------------------------------------------- /tests/nested-modules/nested2/lambda2/requirements.txt: -------------------------------------------------------------------------------- 1 | timeprint 2 | -------------------------------------------------------------------------------- /tests/nested-modules/nested2/main.tf: -------------------------------------------------------------------------------- 1 | module "lambda" { 2 | source = "../../../" 3 | 4 | function_name = "terraform-aws-lambda-test-nested-modules-2" 5 | description = "Test nested modules functionality in terraform-aws-lambda" 6 | handler = "main.lambda_handler" 7 | runtime = "python3.6" 8 | timeout = 30 9 | 10 | source_path = "${path.module}/lambda2" 11 | } 12 | -------------------------------------------------------------------------------- /tests/policy/lambda.py: -------------------------------------------------------------------------------- 1 | def lambda_handler(event, context): 2 | if event['pass']: 3 | return True 4 | else: 5 | raise Exception('oh no') 6 | -------------------------------------------------------------------------------- /tests/policy/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "local" { 3 | path = "terraform.tfstate" 4 | } 5 | } 6 | 7 | provider "aws" { 8 | region = "eu-west-1" 9 | } 10 | 11 | resource "random_id" "name" { 12 | byte_length = 6 13 | prefix = "terraform-aws-lambda-policy-" 14 | } 15 | 16 | resource "aws_sqs_queue" "test" { 17 | name = random_id.name.hex 18 | } 19 | 20 | data "aws_iam_policy_document" "computed" { 21 | statement { 22 | effect = "Allow" 23 | 24 | actions = [ 25 | "sqs:SendMessage", 26 | ] 27 | 28 | resources = [ 29 | aws_sqs_queue.test.arn, 30 | ] 31 | } 32 | } 33 | 34 | data "aws_iam_policy_document" "known" { 35 | statement { 36 | effect = "Deny" 37 | 38 | actions = [ 39 | "sqs:SendMessage", 40 | ] 41 | 42 | resources = [ 43 | "*", 44 | ] 45 | } 46 | } 47 | 48 | module "lambda_with_computed_policy" { 49 | source = "../../" 50 | 51 | function_name = "${random_id.name.hex}-computed" 52 | description = "Test attaching policy in terraform-aws-lambda" 53 | handler = "lambda.lambda_handler" 54 | runtime = "python3.6" 55 | 56 | source_path = "${path.module}/lambda.py" 57 | 58 | policy = { 59 | json = data.aws_iam_policy_document.computed.json 60 | } 61 | } 62 | 63 | 64 | module "lambda_with_known_policy" { 65 | source = "../../" 66 | 67 | function_name = "${random_id.name.hex}-known" 68 | description = "Test attaching policy in terraform-aws-lambda" 69 | handler = "lambda.lambda_handler" 70 | runtime = "python3.6" 71 | 72 | source_path = "${path.module}/lambda.py" 73 | 74 | policy = { 75 | json = data.aws_iam_policy_document.known.json 76 | } 77 | } 78 | 79 | 80 | module "lambda_without_policy" { 81 | source = "../../" 82 | 83 | function_name = "${random_id.name.hex}-without" 84 | description = "Test attaching policy in terraform-aws-lambda" 85 | handler = "lambda.lambda_handler" 86 | runtime = "python3.6" 87 | 88 | source_path = "${path.module}/lambda.py" 89 | } 90 | -------------------------------------------------------------------------------- /tests/python-versions/lambda/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claranet/terraform-aws-lambda/1e4bd6cbd434c6f49fa18290f281434e2022e7c4/tests/python-versions/lambda/__init__.py -------------------------------------------------------------------------------- /tests/python-versions/lambda/main.py: -------------------------------------------------------------------------------- 1 | def lambda_handler(event, context): 2 | print('importing a package from requirements.txt') 3 | import timeprint 4 | with timeprint('importing a relative package included in source_dir'): 5 | import test_result 6 | return test_result.value 7 | -------------------------------------------------------------------------------- /tests/python-versions/lambda/requirements.txt: -------------------------------------------------------------------------------- 1 | timeprint 2 | -------------------------------------------------------------------------------- /tests/python-versions/lambda/test_result/__init__.py: -------------------------------------------------------------------------------- 1 | value = "test passed" 2 | -------------------------------------------------------------------------------- /tests/python-versions/python2/.envrc: -------------------------------------------------------------------------------- 1 | layout python2 2 | -------------------------------------------------------------------------------- /tests/python-versions/python2/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "local" { 3 | path = "terraform.tfstate" 4 | } 5 | } 6 | 7 | provider "aws" { 8 | region = "eu-west-1" 9 | } 10 | 11 | module "lambda_python2" { 12 | source = "../../../" 13 | 14 | function_name = "terraform-aws-lambda-test-python2-from-python2" 15 | description = "Test python2 runtime from python2 environment in terraform-aws-lambda" 16 | handler = "main.lambda_handler" 17 | runtime = "python2.7" 18 | timeout = 5 19 | 20 | source_path = "${path.module}/../lambda" 21 | } 22 | 23 | module "lambda_python3" { 24 | source = "../../../" 25 | 26 | function_name = "terraform-aws-lambda-test-python3-from-python2" 27 | description = "Test python3 runtime from python2 environment in terraform-aws-lambda" 28 | handler = "main.lambda_handler" 29 | runtime = "python3.7" 30 | timeout = 5 31 | 32 | source_path = "${path.module}/../lambda" 33 | } 34 | -------------------------------------------------------------------------------- /tests/python-versions/python3/.envrc: -------------------------------------------------------------------------------- 1 | layout python3 2 | -------------------------------------------------------------------------------- /tests/python-versions/python3/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "local" { 3 | path = "terraform.tfstate" 4 | } 5 | } 6 | 7 | provider "aws" { 8 | region = "eu-west-1" 9 | } 10 | 11 | module "lambda_python2" { 12 | source = "../../../" 13 | 14 | function_name = "terraform-aws-lambda-test-python2-from-python3" 15 | description = "Test python2 runtime from python3 environment in terraform-aws-lambda" 16 | handler = "main.lambda_handler" 17 | runtime = "python2.7" 18 | timeout = 5 19 | 20 | source_path = "${path.module}/../lambda" 21 | } 22 | 23 | module "lambda_python3" { 24 | source = "../../../" 25 | 26 | function_name = "terraform-aws-lambda-test-python3-from-python3" 27 | description = "Test python3 runtime from python3 environment in terraform-aws-lambda" 28 | handler = "main.lambda_handler" 29 | runtime = "python3.7" 30 | timeout = 5 31 | 32 | source_path = "${path.module}/../lambda" 33 | } 34 | -------------------------------------------------------------------------------- /tests/vpc-config/lambda.py: -------------------------------------------------------------------------------- 1 | def lambda_handler(event, context): 2 | return 'test passed' 3 | -------------------------------------------------------------------------------- /tests/vpc-config/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "local" { 3 | path = "terraform.tfstate" 4 | } 5 | } 6 | 7 | provider "aws" { 8 | region = "eu-west-1" 9 | } 10 | 11 | resource "aws_vpc" "test" { 12 | cidr_block = "10.255.255.0/24" 13 | 14 | tags = { 15 | Name = "terraform-aws-lambda-test-vpc-config" 16 | } 17 | } 18 | 19 | resource "aws_subnet" "test" { 20 | vpc_id = aws_vpc.test.id 21 | cidr_block = aws_vpc.test.cidr_block 22 | } 23 | 24 | resource "aws_security_group" "test" { 25 | name = "terraform-aws-lambda-test-vpc-config" 26 | vpc_id = aws_vpc.test.id 27 | } 28 | 29 | module "lambda" { 30 | source = "../../" 31 | 32 | function_name = "terraform-aws-lambda-test-vpc-config" 33 | description = "Test vpc-config in terraform-aws-lambda" 34 | handler = "lambda.lambda_handler" 35 | runtime = "python3.6" 36 | timeout = 30 37 | 38 | source_path = "${path.module}/lambda.py" 39 | 40 | vpc_config = { 41 | subnet_ids = [aws_subnet.test.id] 42 | security_group_ids = [aws_security_group.test.id] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | # Required variables. 2 | 3 | variable "function_name" { 4 | type = string 5 | } 6 | 7 | variable "handler" { 8 | type = string 9 | } 10 | 11 | variable "runtime" { 12 | type = string 13 | } 14 | 15 | variable "source_path" { 16 | description = "The absolute path to a local file or directory containing your Lambda source code" 17 | type = string 18 | } 19 | 20 | # Optional variables specific to this module. 21 | 22 | variable "build_command" { 23 | description = "The command to run to create the Lambda package zip file" 24 | type = string 25 | default = "python build.py '$filename' '$runtime' '$source'" 26 | } 27 | 28 | variable "build_paths" { 29 | description = "The files or directories used by the build command, to trigger new Lambda package builds whenever build scripts change" 30 | type = list(string) 31 | default = ["build.py"] 32 | } 33 | 34 | variable "cloudwatch_logs" { 35 | description = "Set this to false to disable logging your Lambda output to CloudWatch Logs" 36 | type = bool 37 | default = true 38 | } 39 | 40 | variable "lambda_at_edge" { 41 | description = "Set this to true if using Lambda@Edge, to enable publishing, limit the timeout, and allow edgelambda.amazonaws.com to invoke the function" 42 | type = bool 43 | default = false 44 | } 45 | 46 | variable "policy" { 47 | description = "An additional policy to attach to the Lambda function role" 48 | type = object({ 49 | json = string 50 | }) 51 | default = null 52 | } 53 | 54 | variable "trusted_entities" { 55 | description = "Lambda function additional trusted entities for assuming roles (trust relationship)" 56 | type = list(string) 57 | default = [] 58 | } 59 | 60 | locals { 61 | publish = var.lambda_at_edge ? true : var.publish 62 | timeout = var.lambda_at_edge ? min(var.timeout, 5) : var.timeout 63 | } 64 | 65 | # Optional attributes to pass through to the resource. 66 | 67 | variable "description" { 68 | type = string 69 | default = null 70 | } 71 | 72 | variable "layers" { 73 | type = list(string) 74 | default = null 75 | } 76 | 77 | variable "kms_key_arn" { 78 | type = string 79 | default = null 80 | } 81 | 82 | variable "memory_size" { 83 | type = number 84 | default = null 85 | } 86 | 87 | variable "publish" { 88 | type = bool 89 | default = false 90 | } 91 | variable "reserved_concurrent_executions" { 92 | type = number 93 | default = null 94 | } 95 | 96 | variable "tags" { 97 | type = map(string) 98 | default = null 99 | } 100 | 101 | variable "timeout" { 102 | type = number 103 | default = 3 104 | } 105 | 106 | # Optional blocks to pass through to the resource. 107 | 108 | variable "dead_letter_config" { 109 | type = object({ 110 | target_arn = string 111 | }) 112 | default = null 113 | } 114 | 115 | variable "environment" { 116 | type = object({ 117 | variables = map(string) 118 | }) 119 | default = null 120 | } 121 | 122 | variable "tracing_config" { 123 | type = object({ 124 | mode = string 125 | }) 126 | default = null 127 | } 128 | 129 | 130 | variable "vpc_config" { 131 | type = object({ 132 | security_group_ids = list(string) 133 | subnet_ids = list(string) 134 | }) 135 | default = null 136 | } 137 | 138 | variable "enabled" { 139 | description = "Enable or disable the Lambda resources." 140 | type = bool 141 | default = true 142 | } 143 | --------------------------------------------------------------------------------