Skip to content

Commit 65b4f5e

Browse files
authored
Merge branch 'master' into aws_iam_role_policy_attachment
2 parents 5f9060d + ace2bc9 commit 65b4f5e

File tree

21 files changed

+393
-319
lines changed

21 files changed

+393
-319
lines changed

Makefile

Lines changed: 0 additions & 23 deletions
This file was deleted.

README.md

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ This Terraform module creates and uploads an AWS Lambda function and hides the u
1616
* Python 2.7 or higher
1717
* Linux/Unix/Windows
1818

19+
## Terraform version compatibility
20+
21+
| Module version | Terraform version |
22+
|----------------|-------------------|
23+
| 1.x.x | 0.12.x |
24+
| 0.x.x | 0.11.x |
25+
1926
## Usage
2027

2128
```js
@@ -32,67 +39,59 @@ module "lambda" {
3239
source_path = "${path.module}/lambda.py"
3340

3441
// Attach a policy.
35-
attach_policy = true
36-
policy = "${data.aws_iam_policy_document.lambda.json}"
42+
policy = {
43+
json = data.aws_iam_policy_document.lambda.json
44+
}
3745

3846
// Add a dead letter queue.
39-
attach_dead_letter_config = true
40-
dead_letter_config {
41-
target_arn = "${var.dead_letter_queue_arn}"
47+
dead_letter_config = {
48+
target_arn = aws_sqs_queue.dlq.arn
4249
}
4350

4451
// Add environment variables.
45-
environment {
46-
variables {
47-
SLACK_URL = "${var.slack_url}"
52+
environment = {
53+
variables = {
54+
SLACK_URL = var.slack_url
4855
}
4956
}
5057

5158
// Deploy into a VPC.
52-
attach_vpc_config = true
53-
vpc_config {
54-
subnet_ids = ["${aws_subnet.test.id}"]
55-
security_group_ids = ["${aws_security_group.test.id}"]
59+
vpc_config = {
60+
subnet_ids = [aws_subnet.test.id]
61+
security_group_ids = [aws_security_group.test.id]
5662
}
5763
}
5864
```
5965

60-
### NB - Multi-region usage
61-
62-
IAM and Lambda function names need to be globally unique within your account.
63-
If you will be deploying this template to multiple regions, you must make the
64-
function name unique per region, for example by setting
65-
`function_name = "deployment-deploy-status-${data.aws_region.current.name}"`
66-
6766
## Inputs
6867

68+
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:
69+
6970
| Name | Description | Type | Default | Required |
70-
|------|-------------|:----:|:-----:|:-----:|
71-
| attach\_dead\_letter\_config | Set this to true if using the dead_letter_config variable | string | `"false"` | no |
72-
| attach\_policy | Set this to true if using the policy variable | string | `"false"` | no |
73-
| attach\_vpc\_config | Set this to true if using the vpc_config variable | string | `"false"` | no |
74-
| build\_command | The command that creates the Lambda package zip file | string | `"python build.py '$filename' '$runtime' '$source'"` | no |
75-
| build\_paths | The files or directories used by the build command, to trigger new Lambda package builds whenever build scripts change | list | `<list>` | no |
76-
| dead\_letter\_config | Dead letter configuration for the Lambda function | map | `<map>` | no |
77-
| description | Description of what your Lambda function does | string | `"Managed by Terraform"` | no |
78-
| enable\_cloudwatch\_logs | Set this to false to disable logging your Lambda output to CloudWatch Logs | string | `"true"` | no |
79-
| environment | Environment configuration for the Lambda function | map | `<map>` | no |
80-
| function\_name | A unique name for your Lambda function (and related IAM resources) | string | n/a | yes |
81-
| handler | The function entrypoint in your code | string | n/a | yes |
82-
| memory\_size | Amount of memory in MB your Lambda function can use at runtime | string | `"128"` | no |
83-
| policy | An addional policy to attach to the Lambda function | string | `""` | no |
84-
| reserved\_concurrent\_executions | The amount of reserved concurrent executions for this Lambda function | string | `"0"` | no |
85-
| runtime | The runtime environment for the Lambda function | string | n/a | yes |
86-
| source\_path | The source file or directory containing your Lambda source code | string | n/a | yes |
87-
| tags | A mapping of tags | map | `<map>` | no |
88-
| timeout | The amount of time your Lambda function had to run in seconds | string | `"10"` | no |
89-
| vpc\_config | VPC configuration for the Lambda function | map | `<map>` | no |
71+
|------|-------------|------|---------|----------|
72+
| **source\_path** | The absolute path to a local file or directory containing your Lambda source code | `string` | | yes |
73+
| build\_command | The command to run to create the Lambda package zip file | `string` | `"python build.py '$filename' '$runtime' '$source'"` | no |
74+
| 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 |
75+
| cloudwatch\_logs | Set this to false to disable logging your Lambda output to CloudWatch Logs | `bool` | `true` | no |
76+
| 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 |
77+
| policy | An additional policy to attach to the Lambda function role | `object({json=string})` | | no |
78+
79+
The following arguments from the [aws_lambda_function](https://www.terraform.io/docs/providers/aws/r/lambda_function.html) resource are not supported:
80+
81+
* filename (use source\_path instead)
82+
* role (one is automatically created)
83+
* s3_bucket
84+
* s3_key
85+
* s3_object_version
86+
* source_code_hash (changes are handled automatically)
9087

9188
## Outputs
9289

9390
| Name | Description |
9491
|------|-------------|
9592
| function\_arn | The ARN of the Lambda function |
93+
| function\_invoke\_arn | The Invoke ARN of the Lambda function |
9694
| function\_name | The name of the Lambda function |
95+
| function\_qualified\_arn | The qualified ARN of the Lambda function |
9796
| role\_arn | The ARN of the IAM role created for the Lambda function |
9897
| role\_name | The name of the IAM role created for the Lambda function |

archive.tf

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,26 @@
1-
locals {
2-
module_relpath = "${substr(path.module, length(path.cwd) + 1, -1)}"
3-
}
4-
51
# Generates a filename for the zip archive based on the contents of the files
62
# in source_path. The filename will change when the source code changes.
73
data "external" "archive" {
84
program = ["python", "${path.module}/hash.py"]
95

106
query = {
11-
build_command = "${var.build_command}"
12-
build_paths = "${jsonencode(var.build_paths)}"
13-
module_relpath = "${local.module_relpath}"
14-
runtime = "${var.runtime}"
15-
source_path = "${var.source_path}"
7+
build_command = var.build_command
8+
build_paths = jsonencode(var.build_paths)
9+
module_relpath = path.module
10+
runtime = var.runtime
11+
source_path = var.source_path
1612
}
1713
}
1814

1915
# Build the zip archive whenever the filename changes.
2016
resource "null_resource" "archive" {
21-
triggers {
22-
filename = "${lookup(data.external.archive.result, "filename")}"
17+
triggers = {
18+
filename = lookup(data.external.archive.result, "filename")
2319
}
2420

2521
provisioner "local-exec" {
26-
command = "${lookup(data.external.archive.result, "build_command")}"
27-
working_dir = "${path.module}"
22+
command = lookup(data.external.archive.result, "build_command")
23+
working_dir = path.module
2824
}
2925
}
3026

@@ -37,9 +33,9 @@ data "external" "built" {
3733
program = ["python", "${path.module}/built.py"]
3834

3935
query = {
40-
build_command = "${lookup(data.external.archive.result, "build_command")}"
41-
filename_old = "${lookup(null_resource.archive.triggers, "filename")}"
42-
filename_new = "${lookup(data.external.archive.result, "filename")}"
43-
module_relpath = "${local.module_relpath}"
36+
build_command = lookup(data.external.archive.result, "build_command")
37+
filename_old = lookup(null_resource.archive.triggers, "filename")
38+
filename_new = lookup(data.external.archive.result, "filename")
39+
module_relpath = path.module
4440
}
4541
}

build.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Installs dependencies with pip automatically.
33

44
import os
5+
import shlex
56
import shutil
67
import subprocess
78
import sys
@@ -104,9 +105,17 @@ def create_zip_file(source_dir, target_file):
104105
)
105106

106107

107-
filename = sys.argv[1]
108-
runtime = sys.argv[2]
109-
source_path = sys.argv[3]
108+
def dequote(value):
109+
"""
110+
Handles quotes around values in a shell-compatible fashion.
111+
112+
"""
113+
return ' '.join(shlex.split(value))
114+
115+
116+
filename = dequote(sys.argv[1])
117+
runtime = dequote(sys.argv[2])
118+
source_path = dequote(sys.argv[3])
110119

111120
absolute_filename = os.path.abspath(filename)
112121

@@ -132,6 +141,7 @@ def create_zip_file(source_dir, target_file):
132141
os.makedirs(target_dir)
133142
print('cp {} {}'.format(file_name, target_path))
134143
shutil.copyfile(file_name, target_path)
144+
shutil.copymode(file_name, target_path)
135145

136146
# Install dependencies into the temporary directory.
137147
if runtime.startswith('python'):

hash.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def update_hash(hash_obj, file_root, file_path):
107107
build_paths = json.loads(query['build_paths'])
108108
module_relpath = query['module_relpath']
109109
runtime = query['runtime']
110-
source_path = query['source_path']
110+
source_path = os.path.abspath(query['source_path'])
111111

112112
# Validate the query.
113113
if not source_path:

iam.tf

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,27 @@ data "aws_iam_policy_document" "assume_role" {
77

88
principals {
99
type = "Service"
10-
identifiers = ["lambda.amazonaws.com"]
10+
identifiers = slice(list("lambda.amazonaws.com", "edgelambda.amazonaws.com"), 0, var.lambda_at_edge ? 2 : 1)
1111
}
1212
}
1313
}
1414

1515
resource "aws_iam_role" "lambda" {
16-
name = "${var.function_name}"
17-
assume_role_policy = "${data.aws_iam_policy_document.assume_role.json}"
16+
name = var.function_name
17+
assume_role_policy = data.aws_iam_policy_document.assume_role.json
18+
tags = var.tags
1819
}
1920

2021
# Attach a policy for logs.
2122

23+
locals {
24+
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}"
25+
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}"
26+
log_group_arns = slice(list(local.lambda_log_group_arn, local.lambda_edge_log_group_arn), 0, var.lambda_at_edge ? 2 : 1)
27+
}
28+
2229
data "aws_iam_policy_document" "logs" {
23-
count = "${var.enable_cloudwatch_logs ? 1 : 0}"
30+
count = var.cloudwatch_logs ? 1 : 0
2431

2532
statement {
2633
effect = "Allow"
@@ -30,7 +37,7 @@ data "aws_iam_policy_document" "logs" {
3037
]
3138

3239
resources = [
33-
"arn:${data.aws_partition.current.partition}:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*",
40+
"*",
3441
]
3542
}
3643

@@ -42,31 +49,29 @@ data "aws_iam_policy_document" "logs" {
4249
"logs:PutLogEvents",
4350
]
4451

45-
resources = [
46-
"arn:${data.aws_partition.current.partition}:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:/aws/lambda/${var.function_name}:*",
47-
]
52+
resources = concat(formatlist("%v:*", local.log_group_arns), formatlist("%v:*:*", local.log_group_arns))
4853
}
4954
}
5055

5156
resource "aws_iam_policy" "logs" {
52-
count = "${var.enable_cloudwatch_logs ? 1 : 0}"
57+
count = var.cloudwatch_logs ? 1 : 0
5358

5459
name = "${var.function_name}-logs"
55-
policy = "${data.aws_iam_policy_document.logs.json}"
60+
policy = data.aws_iam_policy_document.logs[0].json
5661
}
5762

5863
resource "aws_iam_policy_attachment" "logs" {
59-
count = "${var.enable_cloudwatch_logs ? 1 : 0}"
64+
count = var.cloudwatch_logs ? 1 : 0
6065

6166
name = "${var.function_name}-logs"
62-
roles = ["${aws_iam_role.lambda.name}"]
63-
policy_arn = "${aws_iam_policy.logs.arn}"
67+
roles = [aws_iam_role.lambda.name]
68+
policy_arn = aws_iam_policy.logs[0].arn
6469
}
6570

6671
# Attach an additional policy required for the dead letter config.
6772

6873
data "aws_iam_policy_document" "dead_letter" {
69-
count = "${var.attach_dead_letter_config ? 1 : 0}"
74+
count = var.dead_letter_config == null ? 0 : 1
7075

7176
statement {
7277
effect = "Allow"
@@ -77,29 +82,31 @@ data "aws_iam_policy_document" "dead_letter" {
7782
]
7883

7984
resources = [
80-
"${lookup(var.dead_letter_config, "target_arn", "")}",
85+
var.dead_letter_config.target_arn,
8186
]
8287
}
8388
}
8489

8590
resource "aws_iam_policy" "dead_letter" {
86-
count = "${var.attach_dead_letter_config ? 1 : 0}"
91+
count = var.dead_letter_config == null ? 0 : 1
8792

8893
name = "${var.function_name}-dl"
89-
policy = "${data.aws_iam_policy_document.dead_letter.json}"
94+
policy = data.aws_iam_policy_document.dead_letter[0].json
9095
}
9196

9297
resource "aws_iam_policy_attachment" "dead_letter" {
93-
count = "${var.attach_dead_letter_config ? 1 : 0}"
98+
count = var.dead_letter_config == null ? 0 : 1
9499

95100
name = "${var.function_name}-dl"
96-
roles = ["${aws_iam_role.lambda.name}"]
97-
policy_arn = "${aws_iam_policy.dead_letter.arn}"
101+
roles = [aws_iam_role.lambda.name]
102+
policy_arn = aws_iam_policy.dead_letter[0].arn
98103
}
99104

100105
# Attach an additional policy required for the VPC config
101106

102107
data "aws_iam_policy_document" "network" {
108+
count = var.vpc_config == null ? 0 : 1
109+
103110
statement {
104111
effect = "Allow"
105112

@@ -116,32 +123,32 @@ data "aws_iam_policy_document" "network" {
116123
}
117124

118125
resource "aws_iam_policy" "network" {
119-
count = "${var.attach_vpc_config ? 1 : 0}"
126+
count = var.vpc_config == null ? 0 : 1
120127

121128
name = "${var.function_name}-network"
122-
policy = "${data.aws_iam_policy_document.network.json}"
129+
policy = data.aws_iam_policy_document.network[0].json
123130
}
124131

125132
resource "aws_iam_role_policy_attachment" "network" {
126-
count = "${var.attach_vpc_config ? 1 : 0}"
133+
count = var.vpc_config == null ? 0 : 1
127134

128-
role = "${aws_iam_role.lambda.name}"
129-
policy_arn = "${aws_iam_policy.network.arn}"
135+
role = aws_iam_role.lambda.name
136+
policy_arn = aws_iam_policy.network[0].arn
130137
}
131138

132139
# Attach an additional policy if provided.
133140

134141
resource "aws_iam_policy" "additional" {
135-
count = "${var.attach_policy ? 1 : 0}"
142+
count = var.policy == null ? 0 : 1
136143

137-
name = "${var.function_name}"
138-
policy = "${var.policy}"
144+
name = var.function_name
145+
policy = var.policy.json
139146
}
140147

141148
resource "aws_iam_policy_attachment" "additional" {
142-
count = "${var.attach_policy ? 1 : 0}"
149+
count = var.policy == null ? 0 : 1
143150

144-
name = "${var.function_name}"
145-
roles = ["${aws_iam_role.lambda.name}"]
146-
policy_arn = "${aws_iam_policy.additional.arn}"
151+
name = var.function_name
152+
roles = [aws_iam_role.lambda.name]
153+
policy_arn = aws_iam_policy.additional[0].arn
147154
}

0 commit comments

Comments
 (0)