Skip to content

Commit 0fe6b2e

Browse files
authored
feat: Configure cloudwatch logging for step functions module (#9)
1 parent f7cf606 commit 0fe6b2e

File tree

6 files changed

+178
-16
lines changed

6 files changed

+178
-16
lines changed

.github/workflows/pre-commit.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ jobs:
9494
- name: Install pre-commit dependencies
9595
run: |
9696
pip install pre-commit
97-
curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-v0.12.0-linux-amd64" | head -n1)" > terraform-docs && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/
97+
curl -L "$(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-v0.12\..+?-linux-amd64" | head -n1)" > terraform-docs && chmod +x terraform-docs && sudo mv terraform-docs /usr/bin/
9898
curl -L "$(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" > tflint.zip && unzip tflint.zip && rm tflint.zip && sudo mv tflint /usr/bin/
9999
- name: Execute pre-commit
100100
# Run all pre-commit checks on max version supported

README.md

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,16 @@ Terraform module, which creates AWS Step Functions as well as required IAM role
44

55
This Terraform module is the part of [serverless.tf framework](https://github.com/antonbabenko/serverless.tf), which aims to simplify all operations when working with the serverless in Terraform.
66

7-
87
## Features
98

109
- [x] Creates AWS Step Function
1110
- [x] Conditional creation for many types of resources
1211
- [x] Support IAM policy attachments for [Integrated Services (eg, Lambda, SQS, ECS, EKS, Batch, DynamoDB, etc)](https://docs.aws.amazon.com/step-functions/latest/dg/service-integration-iam-templates.html) and various ways to create and attach additional policies
1312

14-
1513
## Usage
1614

1715
### Step Function
18-
16+
1917
```hcl
2018
module "step_function" {
2119
source = "terraform-aws-modules/step-functions/aws"
@@ -39,7 +37,7 @@ module "step_function" {
3937
}
4038
}
4139
EOF
42-
40+
4341
service_integrations = {
4442
dynamodb = {
4543
dynamodb = ["arn:aws:dynamodb:eu-west-1:052212379155:table/Test"]
@@ -79,7 +77,7 @@ module "step_function" {
7977
sqs = {
8078
sqs = "arn:aws:sqs:..." # sqs queue ARN is required because there is no default_resources key for such integration
8179
}
82-
80+
8381
# Special case to deny all actions for the step function (this will override all IAM policies allowed for the function)
8482
no_tasks = {
8583
deny_all = true
@@ -88,7 +86,6 @@ module "step_function" {
8886
}
8987
```
9088

91-
9289
## Additional IAM policies for Step Function
9390

9491
In addition to all supported AWS service integrations you may want to create and attach additional policies.
@@ -101,7 +98,6 @@ There are 5 supported ways to attach additional IAM policies to IAM role used by
10198
1. `policies` - List of ARNs of existing IAM policies, when `attach_policies = true` and `number_of_policies > 0`.
10299
1. `policy_statements` - Map of maps to define IAM statements which will be generated as IAM policy. Requires `attach_policy_statements = true`. See `examples/complete` for more information.
103100

104-
105101
## Conditional creation
106102

107103
Sometimes you need to have a way to create resources conditionally, so the solution is to specify `create` arguments.
@@ -117,11 +113,9 @@ module "step_function" {
117113
}
118114
```
119115

120-
121116
## Examples
122117

123-
* [Complete](https://github.com/terraform-aws-modules/terraform-aws-step-functions/tree/master/examples/complete) - Create Step Function and required IAM resources in various combinations with all supported features.
124-
118+
- [Complete](https://github.com/terraform-aws-modules/terraform-aws-step-functions/tree/master/examples/complete) - Create Step Function and required IAM resources in various combinations with all supported features.
125119

126120
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
127121
## Requirements
@@ -145,37 +139,48 @@ No modules.
145139

146140
| Name | Type |
147141
|------|------|
142+
| [aws_cloudwatch_log_group.sfn](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
148143
| [aws_iam_policy.additional_inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
149144
| [aws_iam_policy.additional_json](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
150145
| [aws_iam_policy.additional_jsons](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
146+
| [aws_iam_policy.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
151147
| [aws_iam_policy.service](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
152148
| [aws_iam_policy_attachment.additional_inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy_attachment) | resource |
153149
| [aws_iam_policy_attachment.additional_json](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy_attachment) | resource |
154150
| [aws_iam_policy_attachment.additional_jsons](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy_attachment) | resource |
151+
| [aws_iam_policy_attachment.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy_attachment) | resource |
155152
| [aws_iam_policy_attachment.service](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy_attachment) | resource |
156153
| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
157154
| [aws_iam_role_policy_attachment.additional_many](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
158155
| [aws_iam_role_policy_attachment.additional_one](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
159156
| [aws_sfn_state_machine.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sfn_state_machine) | resource |
157+
| [aws_cloudwatch_log_group.sfn](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudwatch_log_group) | data source |
160158
| [aws_iam_policy_document.additional_inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
161159
| [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
160+
| [aws_iam_policy_document.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
162161
| [aws_iam_policy_document.service](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
163162
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
164163

165164
## Inputs
166165

167166
| Name | Description | Type | Default | Required |
168167
|------|-------------|------|---------|:--------:|
168+
| <a name="input_attach_cloudwatch_logs_policy"></a> [attach\_cloudwatch\_logs\_policy](#input\_attach\_cloudwatch\_logs\_policy) | Controls whether CloudWatch Logs policy should be added to IAM role for Lambda Function | `bool` | `true` | no |
169169
| <a name="input_attach_policies"></a> [attach\_policies](#input\_attach\_policies) | Controls whether list of policies should be added to IAM role | `bool` | `false` | no |
170170
| <a name="input_attach_policies_for_integrations"></a> [attach\_policies\_for\_integrations](#input\_attach\_policies\_for\_integrations) | Whether to attach AWS Service policies to IAM role | `bool` | `true` | no |
171171
| <a name="input_attach_policy"></a> [attach\_policy](#input\_attach\_policy) | Controls whether policy should be added to IAM role | `bool` | `false` | no |
172172
| <a name="input_attach_policy_json"></a> [attach\_policy\_json](#input\_attach\_policy\_json) | Controls whether policy\_json should be added to IAM role | `bool` | `false` | no |
173173
| <a name="input_attach_policy_jsons"></a> [attach\_policy\_jsons](#input\_attach\_policy\_jsons) | Controls whether policy\_jsons should be added to IAM role | `bool` | `false` | no |
174174
| <a name="input_attach_policy_statements"></a> [attach\_policy\_statements](#input\_attach\_policy\_statements) | Controls whether policy\_statements should be added to IAM role | `bool` | `false` | no |
175175
| <a name="input_aws_region_assume_role"></a> [aws\_region\_assume\_role](#input\_aws\_region\_assume\_role) | Name of AWS regions where IAM role can be assumed by the Step Function | `string` | `""` | no |
176+
| <a name="input_cloudwatch_log_group_kms_key_id"></a> [cloudwatch\_log\_group\_kms\_key\_id](#input\_cloudwatch\_log\_group\_kms\_key\_id) | The ARN of the KMS Key to use when encrypting log data. | `string` | `null` | no |
177+
| <a name="input_cloudwatch_log_group_name"></a> [cloudwatch\_log\_group\_name](#input\_cloudwatch\_log\_group\_name) | Name of Cloudwatch Logs group name to use. | `string` | `null` | no |
178+
| <a name="input_cloudwatch_log_group_retention_in_days"></a> [cloudwatch\_log\_group\_retention\_in\_days](#input\_cloudwatch\_log\_group\_retention\_in\_days) | Specifies the number of days you want to retain log events in the specified log group. Possible values are: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, and 3653. | `number` | `null` | no |
179+
| <a name="input_cloudwatch_log_group_tags"></a> [cloudwatch\_log\_group\_tags](#input\_cloudwatch\_log\_group\_tags) | A map of tags to assign to the resource. | `map(string)` | `{}` | no |
176180
| <a name="input_create"></a> [create](#input\_create) | Whether to create Step Function resource | `bool` | `true` | no |
177181
| <a name="input_create_role"></a> [create\_role](#input\_create\_role) | Whether to create IAM role for the Step Function | `bool` | `true` | no |
178182
| <a name="input_definition"></a> [definition](#input\_definition) | The Amazon States Language definition of the Step Function | `string` | `""` | no |
183+
| <a name="input_logging_configuration"></a> [logging\_configuration](#input\_logging\_configuration) | Defines what execution history events are logged and where they are logged | `map(string)` | `{}` | no |
179184
| <a name="input_name"></a> [name](#input\_name) | The name of the Step Function | `string` | `""` | no |
180185
| <a name="input_number_of_policies"></a> [number\_of\_policies](#input\_number\_of\_policies) | Number of policies to attach to IAM role | `number` | `0` | no |
181186
| <a name="input_number_of_policy_jsons"></a> [number\_of\_policy\_jsons](#input\_number\_of\_policy\_jsons) | Number of policies JSON to attach to IAM role | `number` | `0` | no |
@@ -195,6 +200,7 @@ No modules.
195200
| <a name="input_tags"></a> [tags](#input\_tags) | Maps of tags to assign to the Step Function | `map(string)` | `{}` | no |
196201
| <a name="input_trusted_entities"></a> [trusted\_entities](#input\_trusted\_entities) | Step Function additional trusted entities for assuming roles (trust relationship) | `list(string)` | `[]` | no |
197202
| <a name="input_type"></a> [type](#input\_type) | Determines whether a Standard or Express state machine is created. The default is STANDARD. Valid Values: STANDARD \| EXPRESS | `string` | `"STANDARD"` | no |
203+
| <a name="input_use_existing_cloudwatch_log_group"></a> [use\_existing\_cloudwatch\_log\_group](#input\_use\_existing\_cloudwatch\_log\_group) | Whether to use an existing CloudWatch log group or create new | `bool` | `false` | no |
198204
| <a name="input_use_existing_role"></a> [use\_existing\_role](#input\_use\_existing\_role) | Whether to use an existing IAM role for this Step Function | `bool` | `false` | no |
199205

200206
## Outputs
@@ -215,7 +221,6 @@ Module managed by [Anton Babenko](https://github.com/antonbabenko). Check out [s
215221

216222
Please reach out to [Betajob](https://www.betajob.com/) if you are looking for commercial support for your Terraform, AWS, or serverless project.
217223

218-
219224
## License
220225

221226
Apache 2 Licensed. See LICENSE for full details.

examples/complete/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@ Note that this example may create resources which cost money. Run `terraform des
3939
|------|--------|---------|
4040
| <a name="module_disabled_step_function"></a> [disabled\_step\_function](#module\_disabled\_step\_function) | ../../ | |
4141
| <a name="module_step_function"></a> [step\_function](#module\_step\_function) | ../../ | |
42+
| <a name="module_step_function_with_existing_log_group"></a> [step\_function\_with\_existing\_log\_group](#module\_step\_function\_with\_existing\_log\_group) | ../../ | |
4243

4344
## Resources
4445

4546
| Name | Type |
4647
|------|------|
48+
| [aws_cloudwatch_log_group.external](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
4749
| [aws_sqs_queue.queue](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue) | resource |
4850
| [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource |
4951

examples/complete/main.tf

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ locals {
3030
EOF
3131
}
3232

33-
3433
module "step_function" {
3534
source = "../../"
3635

@@ -40,6 +39,11 @@ module "step_function" {
4039

4140
definition = local.definition_template
4241

42+
logging_configuration = {
43+
include_execution_data = true
44+
level = "ALL"
45+
}
46+
4347
service_integrations = {
4448

4549
dynamodb = {
@@ -61,9 +65,10 @@ module "step_function" {
6165
xray = true
6266
}
6367

64-
no_tasks = {
65-
deny_all = true
66-
}
68+
# # NB: This will "Deny" everything (including logging)!
69+
# no_tasks = {
70+
# deny_all = true
71+
# }
6772
}
6873

6974
######################
@@ -135,6 +140,34 @@ EOF
135140
}
136141
}
137142

143+
###############################################
144+
# With CloudWatch log group created externally
145+
###############################################
146+
147+
resource "aws_cloudwatch_log_group" "external" {
148+
name = "${random_pet.this.id}-my-log-group"
149+
}
150+
151+
module "step_function_with_existing_log_group" {
152+
source = "../../"
153+
154+
name = "${random_pet.this.id}-existing-log-group"
155+
156+
type = "express"
157+
158+
definition = local.definition_template
159+
160+
use_existing_cloudwatch_log_group = true
161+
cloudwatch_log_group_name = aws_cloudwatch_log_group.external.name
162+
163+
logging_configuration = {
164+
include_execution_data = false
165+
level = "ERROR"
166+
}
167+
168+
depends_on = [aws_cloudwatch_log_group.external]
169+
}
170+
138171
###########
139172
# Disabled
140173
###########

main.tf

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ locals {
22
create_role = var.create && var.create_role && !var.use_existing_role
33
aws_region = local.create_role && var.aws_region_assume_role == "" ? data.aws_region.current[0].name : var.aws_region_assume_role
44

5+
enable_logging = try(var.logging_configuration["level"], "OFF") != "OFF"
6+
7+
# Normalize ARN by trimming ":*" because data-source has it, but resource does not have it
8+
log_group_arn = trimsuffix(element(concat(data.aws_cloudwatch_log_group.sfn.*.arn, aws_cloudwatch_log_group.sfn.*.arn, [""]), 0), ":*")
9+
510
role_name = local.create_role ? coalesce(var.role_name, var.name) : null
611
}
712

@@ -13,6 +18,16 @@ resource "aws_sfn_state_machine" "this" {
1318
role_arn = var.use_existing_role ? var.role_arn : aws_iam_role.this[0].arn
1419
definition = var.definition
1520

21+
dynamic "logging_configuration" {
22+
for_each = local.enable_logging ? [true] : []
23+
24+
content {
25+
log_destination = lookup(var.logging_configuration, "log_destination", "${local.log_group_arn}:*")
26+
include_execution_data = lookup(var.logging_configuration, "include_execution_data", null)
27+
level = lookup(var.logging_configuration, "level", null)
28+
}
29+
}
30+
1631
type = upper(var.type)
1732

1833
tags = merge({ Name = var.name }, var.tags)
@@ -217,3 +232,64 @@ resource "aws_iam_policy_attachment" "additional_inline" {
217232
roles = [aws_iam_role.this[0].name]
218233
policy_arn = aws_iam_policy.additional_inline[0].arn
219234
}
235+
236+
#################################
237+
# IAM policy for Cloudwatch Logs
238+
#################################
239+
240+
data "aws_iam_policy_document" "logs" {
241+
count = local.create_role && local.enable_logging && var.attach_cloudwatch_logs_policy ? 1 : 0
242+
243+
# Copied from https://docs.aws.amazon.com/step-functions/latest/dg/cw-logs.html
244+
statement {
245+
effect = "Allow"
246+
247+
actions = [
248+
"logs:CreateLogDelivery",
249+
"logs:GetLogDelivery",
250+
"logs:UpdateLogDelivery",
251+
"logs:DeleteLogDelivery",
252+
"logs:ListLogDeliveries",
253+
"logs:PutResourcePolicy",
254+
"logs:DescribeResourcePolicies",
255+
"logs:DescribeLogGroups",
256+
]
257+
258+
resources = ["*"]
259+
}
260+
}
261+
262+
resource "aws_iam_policy" "logs" {
263+
count = local.create_role && local.enable_logging && var.attach_cloudwatch_logs_policy ? 1 : 0
264+
265+
name = "${local.role_name}-logs"
266+
policy = data.aws_iam_policy_document.logs[0].json
267+
}
268+
269+
resource "aws_iam_policy_attachment" "logs" {
270+
count = local.create_role && local.enable_logging && var.attach_cloudwatch_logs_policy ? 1 : 0
271+
272+
name = "${local.role_name}-logs"
273+
roles = [aws_iam_role.this[0].name]
274+
policy_arn = aws_iam_policy.logs[0].arn
275+
}
276+
277+
##################
278+
# CloudWatch Logs
279+
##################
280+
281+
data "aws_cloudwatch_log_group" "sfn" {
282+
count = var.create && local.enable_logging && var.use_existing_cloudwatch_log_group ? 1 : 0
283+
284+
name = var.cloudwatch_log_group_name
285+
}
286+
287+
resource "aws_cloudwatch_log_group" "sfn" {
288+
count = var.create && local.enable_logging && !var.use_existing_cloudwatch_log_group ? 1 : 0
289+
290+
name = coalesce(var.cloudwatch_log_group_name, var.name)
291+
retention_in_days = var.cloudwatch_log_group_retention_in_days
292+
kms_key_id = var.cloudwatch_log_group_kms_key_id
293+
294+
tags = merge(var.tags, var.cloudwatch_log_group_tags)
295+
}

variables.tf

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ variable "use_existing_role" {
1616
default = false
1717
}
1818

19+
variable "use_existing_cloudwatch_log_group" {
20+
description = "Whether to use an existing CloudWatch log group or create new"
21+
type = bool
22+
default = false
23+
}
24+
1925
################
2026
# Step Function
2127
################
@@ -55,6 +61,46 @@ variable "type" {
5561
}
5662
}
5763

64+
#################
65+
# CloudWatch Logs
66+
#################
67+
68+
variable "logging_configuration" {
69+
description = "Defines what execution history events are logged and where they are logged"
70+
type = map(string)
71+
default = {}
72+
}
73+
74+
variable "cloudwatch_log_group_name" {
75+
description = "Name of Cloudwatch Logs group name to use."
76+
type = string
77+
default = null
78+
}
79+
80+
variable "cloudwatch_log_group_retention_in_days" {
81+
description = "Specifies the number of days you want to retain log events in the specified log group. Possible values are: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, and 3653."
82+
type = number
83+
default = null
84+
}
85+
86+
variable "cloudwatch_log_group_kms_key_id" {
87+
description = "The ARN of the KMS Key to use when encrypting log data."
88+
type = string
89+
default = null
90+
}
91+
92+
variable "cloudwatch_log_group_tags" {
93+
description = "A map of tags to assign to the resource."
94+
type = map(string)
95+
default = {}
96+
}
97+
98+
variable "attach_cloudwatch_logs_policy" {
99+
description = "Controls whether CloudWatch Logs policy should be added to IAM role for Lambda Function"
100+
type = bool
101+
default = true
102+
}
103+
58104
###########
59105
# IAM Role
60106
###########

0 commit comments

Comments
 (0)