Skip to content

Commit b941323

Browse files
authored
feat(ses-identity): add ses-identity module to manage SES identities (#36)
1 parent 716f7b1 commit b941323

File tree

11 files changed

+431
-2
lines changed

11 files changed

+431
-2
lines changed

.github/labeler.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
- any-glob-to-any-file:
1515
- modules/msk-cluster/**/*
1616

17+
":floppy_disk: ses-identity":
18+
- changed-files:
19+
- any-glob-to-any-file:
20+
- modules/ses-identity/**/*
21+
1722
":floppy_disk: sns-fifo-topic":
1823
- changed-files:
1924
- any-glob-to-any-file:

.github/labels.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@
4949
- color: "fbca04"
5050
description: "This issue or pull request is related to msk-cluster module."
5151
name: ":floppy_disk: msk-cluster"
52+
- color: "fbca04"
53+
description: "This issue or pull request is related to ses-identity module."
54+
name: ":floppy_disk: ses-identity"
5255
- color: "fbca04"
5356
description: "This issue or pull request is related to sns-fifo-topic module."
5457
name: ":floppy_disk: sns-fifo-topic"

.tflint.hcl

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,30 @@ rule "terraform_unused_required_providers" {
7070

7171
plugin "aws" {
7272
source = "github.com/terraform-linters/tflint-ruleset-aws"
73-
version = "0.38.0"
73+
version = "0.39.0"
7474

7575
enabled = true
7676
deep_check = false
7777
}
78+
79+
# INFO: Wrong validation
80+
rule "aws_route53_record_invalid_health_check_id" {
81+
enabled = false
82+
}
83+
# INFO: Wrong validation
84+
rule "aws_route53_record_invalid_name" {
85+
enabled = false
86+
}
87+
# INFO: Wrong validation
88+
rule "aws_route53_record_invalid_set_identifier" {
89+
enabled = false
90+
}
91+
# INFO: Wrong validation
92+
rule "aws_route53_record_invalid_type" {
93+
enabled = false
94+
}
95+
# INFO: Wrong validation
96+
rule "aws_route53_record_invalid_zone_id" {
97+
enabled = false
98+
}
99+

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Terraform module which creates messaging related resources on AWS.
99
- [eventbridge-event-bus](./modules/eventbridge-event-bus)
1010
- [eventbridge-rule](./modules/eventbridge-rule)
1111
- [msk-cluster](./modules/msk-cluster)
12+
- [ses-identity](./modules/ses-identity)
1213
- [sns-fifo-topic](./modules/sns-fifo-topic)
1314
- [sns-standard-topic](./modules/sns-standard-topic)
1415

@@ -22,6 +23,10 @@ Terraform Modules from [this package](https://github.com/tedilabs/terraform-aws-
2223
- Rule
2324
- **AWS MSK (Managed Streaming for Apache Kafka)**
2425
- Cluster
26+
- **AWS SES (Simple Email Service)**
27+
- Identity
28+
- Domain Identity
29+
- Email Identity
2530
- **AWS SNS (Simple Notification Service)**
2631
- FIFO Topic
2732
- Standard Topic
@@ -46,4 +51,4 @@ Like this project? Follow the repository on [GitHub](https://github.com/tedilabs
4651

4752
Provided under the terms of the [Apache License](LICENSE).
4853

49-
Copyright © 2023, [Byungjin Park](https://www.posquit0.com).
54+
Copyright © 2023-2025, [Byungjin Park](https://www.posquit0.com).

modules/ses-identity/README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# ses-identity
2+
3+
This module creates following resources.
4+
5+
- `aws_sesv2_email_identity`
6+
- `aws_route53_record` (optional)
7+
8+
<!-- BEGIN_TF_DOCS -->
9+
## Requirements
10+
11+
| Name | Version |
12+
|------|---------|
13+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.10 |
14+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.84 |
15+
16+
## Providers
17+
18+
| Name | Version |
19+
|------|---------|
20+
| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.98.0 |
21+
22+
## Modules
23+
24+
| Name | Source | Version |
25+
|------|--------|---------|
26+
| <a name="module_resource_group"></a> [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.10.0 |
27+
28+
## Resources
29+
30+
| Name | Type |
31+
|------|------|
32+
| [aws_route53_record.dkim](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
33+
| [aws_sesv2_email_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sesv2_email_identity) | resource |
34+
35+
## Inputs
36+
37+
| Name | Description | Type | Default | Required |
38+
|------|-------------|------|---------|:--------:|
39+
| <a name="input_name"></a> [name](#input\_name) | (Required) The domain name of the SES domain identity. | `string` | n/a | yes |
40+
| <a name="input_configuration_set"></a> [configuration\_set](#input\_configuration\_set) | (Optional) The configuration set to use by default when sending from this identity. Note that any configuration set defined in the email sending request takes precedence. | `string` | `null` | no |
41+
| <a name="input_dkim"></a> [dkim](#input\_dkim) | (Optional) The configuration for the DKIM (DomainKeys Identified Mail). `dkim` as defined below.<br/> (Optional) `type` - Whether to use either Easy DKIM (`EASY_DKIM`) or Bring Your Own DKIM (`BYODKIM`), and depending on your choice, you'll have to configure the signing key length of the private key. Valid values are `EASY_DKIM` and `BYODKIM`. Defaults to `EASY_DKIM`.<br/> (Optional) `signing_key_type` - The key type of the future DKIM key pair to be generated. This can be changed at most once per day. The signing key length of the private key. Valid values are `RSA_1024` and `RSA_2048`. Defaults to `RSA_2048`. Only required if `type` is `EASY_DKIM`.<br/> (Optional) `private_key` - A private key that's used to generate a DKIM signature. The private key must use 1024 or 2048-bit RSA encryption, and must be encoded using base64 encoding. Only required if `type` is `BYODKIM`.<br/> (Optional) `selector_name` - A string that's used to identify a public key in the DNS configuration for a domain. Only required if `type` is `BYODKIM`.<br/> (Optional) `verification` - A configuration for the DKIM verification. `verification` as defined below.<br/> (Optional) `enabled` - Whether to process DKIM verification by creating the necessary domain records in the module. Defaults to `false`.<br/> (Optional) `zone_id` - The ID of Hosted Zone to automatically manage the records for DKIM verification. | <pre>object({<br/> type = optional(string, "EASY_DKIM")<br/> signing_key_type = optional(string, "RSA_2048")<br/> private_key = optional(string)<br/> selector_name = optional(string)<br/> verification = optional(object({<br/> enabled = optional(bool, false)<br/> zone_id = optional(string)<br/> }))<br/> })</pre> | `{}` | no |
42+
| <a name="input_module_tags_enabled"></a> [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no |
43+
| <a name="input_resource_group_description"></a> [resource\_group\_description](#input\_resource\_group\_description) | (Optional) The description of Resource Group. | `string` | `"Managed by Terraform."` | no |
44+
| <a name="input_resource_group_enabled"></a> [resource\_group\_enabled](#input\_resource\_group\_enabled) | (Optional) Whether to create Resource Group to find and group AWS resources which are created by this module. | `bool` | `true` | no |
45+
| <a name="input_resource_group_name"></a> [resource\_group\_name](#input\_resource\_group\_name) | (Optional) The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. | `string` | `""` | no |
46+
| <a name="input_tags"></a> [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no |
47+
48+
## Outputs
49+
50+
| Name | Description |
51+
|------|-------------|
52+
| <a name="output_arn"></a> [arn](#output\_arn) | The ARN of the SES identity. |
53+
| <a name="output_configuration_set"></a> [configuration\_set](#output\_configuration\_set) | The configuration set to use by default when sending from this identity. |
54+
| <a name="output_dkim"></a> [dkim](#output\_dkim) | The configuration for the DKIM. |
55+
| <a name="output_id"></a> [id](#output\_id) | The ID of the SES identity. |
56+
| <a name="output_name"></a> [name](#output\_name) | The domain name for the SES identity. |
57+
| <a name="output_status"></a> [status](#output\_status) | The status of the SES identity. |
58+
| <a name="output_type"></a> [type](#output\_type) | The type of the SES identity. |
59+
<!-- END_TF_DOCS -->

modules/ses-identity/main.tf

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
locals {
2+
metadata = {
3+
package = "terraform-aws-messaging"
4+
version = trimspace(file("${path.module}/../../VERSION"))
5+
module = basename(path.module)
6+
name = var.name
7+
}
8+
module_tags = var.module_tags_enabled ? {
9+
"module.terraform.io/package" = local.metadata.package
10+
"module.terraform.io/version" = local.metadata.version
11+
"module.terraform.io/name" = local.metadata.module
12+
"module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}"
13+
"module.terraform.io/instance" = local.metadata.name
14+
} : {}
15+
}
16+
17+
locals {
18+
signing_key_type = {
19+
"RSA_1024" = "RSA_1024_BIT"
20+
"RSA_2048" = "RSA_2048_BIT"
21+
}
22+
signing_key_type_reverse = {
23+
for k, v in local.signing_key_type : v => k
24+
}
25+
}
26+
27+
###################################################
28+
# SES Domain Identity
29+
###################################################
30+
31+
resource "aws_sesv2_email_identity" "this" {
32+
email_identity = var.name
33+
configuration_set_name = var.configuration_set
34+
35+
dkim_signing_attributes {
36+
# Easy DKIM
37+
next_signing_key_length = (var.dkim.type == "EASY_DKIM"
38+
? local.signing_key_type[var.dkim.signing_key_type]
39+
: null
40+
)
41+
42+
# BYODKIM
43+
domain_signing_private_key = (var.dkim.type == "BYODKIM"
44+
? var.dkim.private_key
45+
: null
46+
)
47+
domain_signing_selector = (var.dkim.type == "BYODKIM"
48+
? var.dkim.selector_name
49+
: null
50+
)
51+
}
52+
53+
tags = merge(
54+
{
55+
"Name" = local.metadata.name
56+
},
57+
local.module_tags,
58+
var.tags,
59+
)
60+
}

modules/ses-identity/outputs.tf

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
output "arn" {
2+
description = "The ARN of the SES identity."
3+
value = aws_sesv2_email_identity.this.arn
4+
}
5+
6+
output "id" {
7+
description = "The ID of the SES identity."
8+
value = aws_sesv2_email_identity.this.id
9+
}
10+
11+
output "status" {
12+
description = "The status of the SES identity."
13+
value = aws_sesv2_email_identity.this.verified_for_sending_status ? "VERIFIED" : "PENDING"
14+
}
15+
16+
output "name" {
17+
description = "The domain name for the SES identity."
18+
value = aws_sesv2_email_identity.this.email_identity
19+
}
20+
21+
output "type" {
22+
description = "The type of the SES identity."
23+
value = aws_sesv2_email_identity.this.identity_type
24+
}
25+
26+
output "configuration_set" {
27+
description = "The configuration set to use by default when sending from this identity."
28+
value = aws_sesv2_email_identity.this.configuration_set_name
29+
}
30+
31+
output "dkim" {
32+
description = "The configuration for the DKIM."
33+
value = {
34+
type = var.dkim.type
35+
status = one(aws_sesv2_email_identity.this.dkim_signing_attributes[*].status)
36+
current_signing_key_type = local.signing_key_type_reverse[one(aws_sesv2_email_identity.this.dkim_signing_attributes[*].current_signing_key_length)]
37+
signing_key_type = local.signing_key_type_reverse[one(aws_sesv2_email_identity.this.dkim_signing_attributes[*].next_signing_key_length)]
38+
selector_name = one(aws_sesv2_email_identity.this.dkim_signing_attributes[*].domain_signing_selector)
39+
origin = one(aws_sesv2_email_identity.this.dkim_signing_attributes[*].signing_attributes_origin)
40+
last_key_generated_at = one(aws_sesv2_email_identity.this.dkim_signing_attributes[*].last_key_generation_timestamp)
41+
42+
verification = {
43+
enabled = var.dkim.verification.enabled
44+
zone = {
45+
id = var.dkim.verification.zone_id
46+
}
47+
records = [
48+
for record in aws_route53_record.dkim : {
49+
name = record.name
50+
value = record.records
51+
type = record.type
52+
ttl = record.ttl
53+
}
54+
]
55+
}
56+
}
57+
}
58+
59+
# output "debug" {
60+
# value = {
61+
# for k, v in aws_sesv2_email_identity.this :
62+
# k => v
63+
# if !contains(["arn", "id", "email_identity", "identity_type", "tags", "tags_all", "dkim_signing_attributes", "verified_for_sending_status", "configuration_set_name"], k)
64+
# }
65+
# }
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
locals {
2+
resource_group_name = (var.resource_group_name != ""
3+
? var.resource_group_name
4+
: join(".", [
5+
local.metadata.package,
6+
local.metadata.module,
7+
replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"),
8+
])
9+
)
10+
}
11+
12+
13+
module "resource_group" {
14+
source = "tedilabs/misc/aws//modules/resource-group"
15+
version = "~> 0.10.0"
16+
17+
count = (var.resource_group_enabled && var.module_tags_enabled) ? 1 : 0
18+
19+
name = local.resource_group_name
20+
description = var.resource_group_description
21+
22+
query = {
23+
resource_tags = local.module_tags
24+
}
25+
26+
module_tags_enabled = false
27+
tags = merge(
28+
local.module_tags,
29+
var.tags,
30+
)
31+
}

modules/ses-identity/route53.tf

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
###################################################
2+
# DKIM Verification
3+
###################################################
4+
5+
resource "aws_route53_record" "dkim" {
6+
for_each = (var.dkim.type == "EASY_DKIM" && var.dkim.verification.enabled
7+
? toset(aws_sesv2_email_identity.this.dkim_signing_attributes[0].tokens)
8+
: []
9+
)
10+
11+
zone_id = var.dkim.verification.zone_id
12+
name = "${each.key}._domainkey.${aws_sesv2_email_identity.this.email_identity}"
13+
type = "CNAME"
14+
records = ["${each.key}.dkim.amazonses.com"]
15+
16+
ttl = "600"
17+
allow_overwrite = true
18+
}

0 commit comments

Comments
 (0)