{"id":17993962,"url":"https://github.com/jimeh/terraform-cloudflare-email","last_synced_at":"2026-04-28T20:06:00.028Z","repository":{"id":155652379,"uuid":"632692556","full_name":"jimeh/terraform-cloudflare-email","owner":"jimeh","description":"Terraform module to configure various email related DNS records on Cloudflare.","archived":false,"fork":false,"pushed_at":"2023-04-26T01:48:47.000Z","size":22,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-02T00:09:56.454Z","etag":null,"topics":["cloudflare","dmarc","dmarc-record","domainkeys","email","mta-sts","mx","mx-record","spf","terraform","terraform-module"],"latest_commit_sha":null,"homepage":"https://registry.terraform.io/modules/jimeh/email/cloudflare","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jimeh.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-25T23:59:37.000Z","updated_at":"2024-08-07T19:29:55.000Z","dependencies_parsed_at":null,"dependency_job_id":"84c98a03-d133-4c2a-a4d1-5404c751fa0f","html_url":"https://github.com/jimeh/terraform-cloudflare-email","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimeh%2Fterraform-cloudflare-email","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimeh%2Fterraform-cloudflare-email/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimeh%2Fterraform-cloudflare-email/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimeh%2Fterraform-cloudflare-email/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jimeh","download_url":"https://codeload.github.com/jimeh/terraform-cloudflare-email/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247123087,"owners_count":20887261,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudflare","dmarc","dmarc-record","domainkeys","email","mta-sts","mx","mx-record","spf","terraform","terraform-module"],"created_at":"2024-10-29T20:13:35.449Z","updated_at":"2026-04-28T20:06:00.023Z","avatar_url":"https://github.com/jimeh.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  terraform-cloudflare-email\n\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003e\n    Terraform module to configure various email related DNS records on\n    Cloudflare.\n  \u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/jimeh/terraform-cloudflare-email/releases\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/v/tag/jimeh/terraform-cloudflare-email?label=release\" alt=\"GitHub tag (latest SemVer)\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/jimeh/terraform-cloudflare-email/issues\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/issues-raw/jimeh/terraform-cloudflare-email.svg?style=flat\u0026logo=github\u0026logoColor=white\" alt=\"GitHub issues\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/jimeh/terraform-cloudflare-email/pulls\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/issues-pr-raw/jimeh/terraform-cloudflare-email.svg?style=flat\u0026logo=github\u0026logoColor=white\" alt=\"GitHub pull requests\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/jimeh/terraform-cloudflare-email/blob/master/LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/license/jimeh/terraform-cloudflare-email.svg?style=flat\" alt=\"License Status\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nModule that configures various email related DNS records on Cloudflare,\nincluding serving a MTA-STS policy text file via Cloudflare Workers.\n\n## Features\n\n- Configure MX records.\n- Configure SPF record.\n- Configure DMARC record.\n- Configure SMTP TLS reporting record.\n- Configure MTA-STS record, generate `mta-sts.txt` policy file and serve it with\n  a Cloudflare Worker on\n  `https://mta-sts.\u003cyour-domain\u003e/.well-known/mta-sts.txt`.\n- Configure domain key records (`\u003cselector\u003e._domainkey.\u003cyour-domain\u003e`).\n\n## Example Usage\n\n\u003c!-- x-release-please-start-version --\u003e\n\nExamples assume that you have the following variables setup:\n\n- `cloudflare_account_id` — Your Account ID.\n- `cloudflare_zone_id` — ID of the Zone (domain name).\n- `cloudflare_zone_name` — Domain name, e.g. `foobar.com`.\n\nAdjust examples as needed to fit your setup.\n\n### Google Workspace\n\nBelow example is based on the\n[DNS Basics](https://support.google.com/a/answer/48090?hl=en) support article.\nWhen going through the domain setup wizard within the Google Workspace Admin,\nyou are likely to be given a slightly different list of MX records, and\nobviously you should use the ones that are given to you by Google.\n\nAlso make sure you generate your own domain key from under Apps \u003e Google\nWorkspace \u003e Gmail \u003e Authenticate Email.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ccode\u003emain.tf\u003c/code\u003e\u003c/summary\u003e\n\n```terraform\nmodule \"email\" {\n  source  = \"jimeh/email/cloudflare\"\n  version = \"0.0.3\"\n\n  account_id = var.cloudflare_account_id\n  zone_id    = var.cloudflare_zone_id\n\n  mx = {\n    \"aspmx.l.google.com\"      = 1\n    \"alt1.aspmx.l.google.com\" = 5\n    \"alt2.aspmx.l.google.com\" = 5\n    \"aspmx2.googlemail.com\"   = 10\n    \"aspmx3.googlemail.com\"   = 10\n  }\n\n  spf_terms = [\n    \"include:_spf.google.com\",\n    \"~all\",\n  ]\n\n  mta_sts_mode    = \"enforce\"\n  mta_sts_max_age = 86400\n  mta_sts_mx = [\n    \"*.aspmx.l.google.com\",\n    \"*.googlemail.com\",\n    \"aspmx.l.google.com\",\n  ]\n\n  tlsrpt_rua = [\n    \"mailto:tls-report@${var.cloudflare_zone_name}\",\n  ]\n\n  dmarc_policy = \"reject\"\n  dmarc_rua = [\n    \"mailto:dmarc-report@${var.cloudflare_zone_name}\",\n  ]\n\n  domainkeys = {\n    \"google\" = {\n      type = \"TXT\"\n      value = join(\"\", [\n        # TODO: Replace this example key with a real one.\n        \"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApAVNwJ9\",\n        \"+6ArXN23ZaR8SFSYxVEEbbHRZplZqHVt6uEpcirY+jxHOqV2bvqAY3BHZQs/KoHnFSWUf\",\n        \"6zv6ajZgUxvU65UhCbrQ7CwrJCjU8sQFDk+CpbvmXyJIe9G470HuGEs4NmQDoddJZr09V\",\n        \"7d3anX8n7ePSCsIxwGi53DMhwijQXqHYMFALml+QIMZ/03ydL6/B3EwDNDFSBSEqzt2QS\",\n        \"N43EYb3FlUiGu5NGHl3gibEsbywTmGtN3kmkp/rxqaJPLv16NVpTe+0lAqPiq/pgJT4pp\",\n        \"ACz2ENh6BD0H+hDiCKBiw+gyAeDbOn1c5yslENSEyDxqpn17tnxo+O/ZFmwIDAQAB\"\n      ])\n    }\n  }\n}\n\nresource \"cloudflare_record\" \"cname\" {\n  for_each = {\n    \"mail\" = { value = \"ghs.googlehosted.com\", proxied = false }\n  }\n\n  name    = lookup(each.value, \"name\", each.key)\n  proxied = lookup(each.value, \"proxied\", false)\n  ttl     = lookup(each.value, \"ttl\", 1)\n  type    = \"CNAME\"\n  value   = each.value.value\n  zone_id = var.cloudflare_zone_id\n}\n\nresource \"cloudflare_record\" \"txt\" {\n  for_each = {\n    \"google\" = {\n      value = (\n        \"google-site-verification=__REPLACE_ME_WITH_A_REAL_VALUE__\"\n      )\n    }\n  }\n\n  name    = lookup(each.value, \"name\", local.zone_name)\n  proxied = lookup(each.value, \"proxied\", false)\n  ttl     = lookup(each.value, \"ttl\", 1)\n  type    = \"TXT\"\n  value   = each.value.value\n  zone_id = var.cloudflare_zone_id\n}\n```\n\n\u003c/details\u003e\n\n### Fastmail\n\nThe below example is based on Fastmail's\n[Manual DNS configuration](https://www.fastmail.help/hc/en-us/articles/360060591153-Manual-DNS-configuration)\nhelp article.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ccode\u003emain.tf\u003c/code\u003e\u003c/summary\u003e\n\n```terraform\nmodule \"email\" {\n  source  = \"jimeh/email/cloudflare\"\n  version = \"0.0.3\"\n\n  account_id = var.cloudflare_account_id\n  zone_id    = var.cloudflare_zone_id\n\n  mx = {\n    \"in1-smtp.messagingengine.com\" = 10\n    \"in2-smtp.messagingengine.com\" = 20\n  }\n  mx_subdomains = [\"*\"]\n\n  spf_terms = [\n    \"include:spf.messagingengine.com\",\n    \"?all\"\n  ]\n\n  mta_sts_mode    = \"enforce\"\n  mta_sts_max_age = 86400\n  mta_sts_mx = [\n    \"in1-smtp.messagingengine.com\",\n    \"in2-smtp.messagingengine.com\",\n  ]\n\n  tlsrpt_rua = [\n    \"mailto:tls-report@${var.cloudflare_zone_name}\",\n  ]\n\n  dmarc_policy = \"reject\"\n  dmarc_rua = [\n    \"mailto:dmarc-report@${var.cloudflare_zone_name}\",\n  ]\n\n  domainkeys = {\n    \"fm1\" = {\n      type  = \"CNAME\"\n      value = \"fm1.${var.cloudflare_zone_name}.dkim.fmhosted.com\"\n    }\n    \"fm2\" = {\n      type  = \"CNAME\"\n      value = \"fm2.${var.cloudflare_zone_name}.dkim.fmhosted.com\"\n    }\n    \"fm3\" = {\n      type  = \"CNAME\"\n      value = \"fm3.${var.cloudflare_zone_name}.dkim.fmhosted.com\"\n    }\n    \"mesmtp\" = {\n      type  = \"CNAME\"\n      value = \"mesmtp.${var.cloudflare_zone_name}.dkim.fmhosted.com\"\n    }\n  }\n}\n\nresource \"cloudflare_record\" \"srv\" {\n  for_each = {\n    \"_caldav._tcp\" = {}\n    \"_caldavs._tcp\" = {\n      port   = 433\n      target = \"caldav.fastmail.com\"\n      weight = 1\n    }\n    \"_carddav._tcp\" = {}\n    \"_carddavs._tcp\" = {\n      port   = 443\n      target = \"carddav.fastmail.com\"\n      weight = 1\n    }\n    \"_imap._tcp\" = {}\n    \"_imaps._tcp\" = {\n      port   = 993\n      target = \"imap.fastmail.com\"\n      weight = 1\n    }\n    \"_jmap._tcp\" = {\n      port   = 443\n      target = \"jmap.fastmail.com\"\n      weight = 1\n    }\n    \"_pop3._tcp\" = {}\n    \"_pop3s._tcp\" = {\n      port     = 995\n      priority = 10\n      target   = \"pop.fastmail.com\"\n      weight   = 1\n    }\n    \"_submission._tcp\" = {\n      port   = 587\n      target = \"smtp.fastmail.com\"\n      weight = 1\n    }\n  }\n\n  name    = lookup(each.value, \"name\", each.key)\n  proxied = lookup(each.value, \"proxied\", false)\n  ttl     = lookup(each.value, \"ttl\", 1)\n  type    = \"SRV\"\n  zone_id = var.cloudflare_zone_id\n  data {\n    name     = var.cloudflare_zone_name\n    port     = lookup(each.value, \"port\", 0)\n    priority = lookup(each.value, \"priority\", 0)\n    proto    = split(\".\", each.key)[1]\n    service  = split(\".\", each.key)[0]\n    target   = lookup(each.value, \"target\", \".\")\n    weight   = lookup(each.value, \"weight\", 0)\n  }\n}\n```\n\n\u003c/details\u003e\n\n\u003c!-- x-release-please-end --\u003e\n\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- BEGIN_TF_DOCS --\u003e\n## Requirements\n\n| Name | Version |\n|------|---------|\n| \u003ca name=\"requirement_terraform\"\u003e\u003c/a\u003e [terraform](#requirement\\_terraform) | \u003e= 1.1 |\n| \u003ca name=\"requirement_cloudflare\"\u003e\u003c/a\u003e [cloudflare](#requirement\\_cloudflare) | \u003e= 3.0, \u003c 5.0 |\n\n## Providers\n\n| Name | Version |\n|------|---------|\n| \u003ca name=\"provider_cloudflare\"\u003e\u003c/a\u003e [cloudflare](#provider\\_cloudflare) | \u003e= 3.0, \u003c 5.0 |\n\n## Modules\n\nNo modules.\n\n## Resources\n\n| Name | Type |\n|------|------|\n| [cloudflare_record.dmarc](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/record) | resource |\n| [cloudflare_record.domainkeys](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/record) | resource |\n| [cloudflare_record.mta-sts-a](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/record) | resource |\n| [cloudflare_record.mta-sts-aaaa](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/record) | resource |\n| [cloudflare_record.mta_sts](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/record) | resource |\n| [cloudflare_record.mx](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/record) | resource |\n| [cloudflare_record.smtp_tls](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/record) | resource |\n| [cloudflare_record.spf](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/record) | resource |\n| [cloudflare_worker_route.mta_sts_route](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/worker_route) | resource |\n| [cloudflare_worker_script.mta_sts](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/worker_script) | resource |\n| [cloudflare_workers_kv.mta_sts](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/workers_kv) | resource |\n| [cloudflare_workers_kv_namespace.mta_sts](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/workers_kv_namespace) | resource |\n| [cloudflare_zone.zone](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/data-sources/zone) | data source |\n\n## Inputs\n\n| Name | Description | Type | Default | Required |\n|------|-------------|------|---------|:--------:|\n| \u003ca name=\"input_account_id\"\u003e\u003c/a\u003e [account\\_id](#input\\_account\\_id) | Cloudflare Account ID | `string` | n/a | yes |\n| \u003ca name=\"input_dmarc_dkim_mode\"\u003e\u003c/a\u003e [dmarc\\_dkim\\_mode](#input\\_dmarc\\_dkim\\_mode) | The DMARC DKIM mode for alignment (options: `relaxed`, `strict`). | `string` | `\"relaxed\"` | no |\n| \u003ca name=\"input_dmarc_fo\"\u003e\u003c/a\u003e [dmarc\\_fo](#input\\_dmarc\\_fo) | Failure reporting options for DMARC (characters: `0`, `1`, `d`, `s`, separated by `:`). | `string` | `\"1:d:s\"` | no |\n| \u003ca name=\"input_dmarc_percent\"\u003e\u003c/a\u003e [dmarc\\_percent](#input\\_dmarc\\_percent) | Percentage of messages to apply the DMARC policy to (0-100). | `number` | `100` | no |\n| \u003ca name=\"input_dmarc_policy\"\u003e\u003c/a\u003e [dmarc\\_policy](#input\\_dmarc\\_policy) | The DMARC policy to apply (options: `none`, `quarantine`, `reject`). | `string` | `\"none\"` | no |\n| \u003ca name=\"input_dmarc_rua\"\u003e\u003c/a\u003e [dmarc\\_rua](#input\\_dmarc\\_rua) | Where aggregate DMARC reports about policy violations should be sent. | `list(string)` | n/a | yes |\n| \u003ca name=\"input_dmarc_ruf\"\u003e\u003c/a\u003e [dmarc\\_ruf](#input\\_dmarc\\_ruf) | Where failure/forensic DMARC reports about policy violations should be sent. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_dmarc_spf_mode\"\u003e\u003c/a\u003e [dmarc\\_spf\\_mode](#input\\_dmarc\\_spf\\_mode) | The DMARC SPF mode for alignment (options: `relaxed`, `strict`). | `string` | `\"relaxed\"` | no |\n| \u003ca name=\"input_dmarc_ttl\"\u003e\u003c/a\u003e [dmarc\\_ttl](#input\\_dmarc\\_ttl) | TTL for `_dmarc` DNS record. `1` is auto. Default is `1`. | `number` | `1` | no |\n| \u003ca name=\"input_domainkeys\"\u003e\u003c/a\u003e [domainkeys](#input\\_domainkeys) | Map of domain keys with name, record type (`TXT` or `CNAME`), and value. | \u003cpre\u003emap(object({\u003cbr/\u003e    type  = string\u003cbr/\u003e    value = string\u003cbr/\u003e  }))\u003c/pre\u003e | `{}` | no |\n| \u003ca name=\"input_mta_sts_max_age\"\u003e\u003c/a\u003e [mta\\_sts\\_max\\_age](#input\\_mta\\_sts\\_max\\_age) | Maximum lifetime of the policy in seconds, up to 31557600, defaults to 604800 (1 week) | `number` | `604800` | no |\n| \u003ca name=\"input_mta_sts_mode\"\u003e\u003c/a\u003e [mta\\_sts\\_mode](#input\\_mta\\_sts\\_mode) | MTA policy mode, https://tools.ietf.org/html/rfc8461#section-5 | `string` | `\"testing\"` | no |\n| \u003ca name=\"input_mta_sts_mx\"\u003e\u003c/a\u003e [mta\\_sts\\_mx](#input\\_mta\\_sts\\_mx) | Additional permitted MX hosts for the MTA STS policy. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_mx\"\u003e\u003c/a\u003e [mx](#input\\_mx) | A map representing the MX records. Key is the mail server hostname and value is the priority. | `map(number)` | n/a | yes |\n| \u003ca name=\"input_mx_subdomains\"\u003e\u003c/a\u003e [mx\\_subdomains](#input\\_mx\\_subdomains) | List of sub-domains to also apply MX records to. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_record_ttl\"\u003e\u003c/a\u003e [record\\_ttl](#input\\_record\\_ttl) | TTL for DNS records. `1` is auto. Default is `1`. | `number` | `1` | no |\n| \u003ca name=\"input_spf_terms\"\u003e\u003c/a\u003e [spf\\_terms](#input\\_spf\\_terms) | List of SPF terms that should be included in the SPF TXT record. | `list(string)` | \u003cpre\u003e[\u003cbr/\u003e  \"mx\",\u003cbr/\u003e  \"a\",\u003cbr/\u003e  \"~all\"\u003cbr/\u003e]\u003c/pre\u003e | no |\n| \u003ca name=\"input_tlsrpt_rua\"\u003e\u003c/a\u003e [tlsrpt\\_rua](#input\\_tlsrpt\\_rua) | Locations to which aggregate TLS SMTP reports about policy violations should be sent, either `mailto:` or `https:` schema. | `list(string)` | n/a | yes |\n| \u003ca name=\"input_zone_id\"\u003e\u003c/a\u003e [zone\\_id](#input\\_zone\\_id) | Cloudflare Zone ID | `string` | n/a | yes |\n\n## Outputs\n\n| Name | Description |\n|------|-------------|\n| \u003ca name=\"output_mta_sts_policy_url\"\u003e\u003c/a\u003e [mta\\_sts\\_policy\\_url](#output\\_mta\\_sts\\_policy\\_url) | URL to the MTA-STS policy file. |\n\u003c!-- END_TF_DOCS --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjimeh%2Fterraform-cloudflare-email","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjimeh%2Fterraform-cloudflare-email","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjimeh%2Fterraform-cloudflare-email/lists"}