Security Mail That Reaches Someone Awake
The alternate contacts AWS uses when something is wrong with an account. Without a security contact, abuse reports and vulnerability notices go only to the root user's mailbox - what Security Hub's Account.1 and CIS v5.0.0 control 1.2 check for. The module refuses to run without one unless told to, and validates what the API accepts, which is not what the provider checks.
Verification
Static-verifiedPassed: validated and lint-clean (provider-schema-validated for AWS/Azure/GCP; Terraform-language lint elsewhere).
Conformance
- Static validation (fmt · validate · tflint)
- Security scan clean (Checkov)
- Plan tests (mocked: validation rules · outputs)
Provenance
- SHA-256 checksum
- Signature (pending)
Functional
- Live test pending (no cloud run yet)
Last verified 2026-09-19 · how we verify
Use it from the registry
terraform · opentofumodule "account_contacts" {
source = "www.iac-bazaar.com/iac-bazaar/aws-account-contacts/aws"
version = "1.0.0"
}Needs a registry token from /account/tokens. The module itself is free; the account is what identifies you. Full setup: registry docs.
Inputs & outputs
Create a free account to read this module's contract
The declared contract - every input name, type, default and description, plus every output - is shown to signed-in accounts, not to anonymous visitors.
A free account sees the contract of every module in the catalogue. There is no subscription and nothing to buy - the modules are free to download, and they run under Vizier.
Documentation
aws-account-contacts
The alternate contacts AWS uses when something is wrong with an account. Works
with Terraform and OpenTofu (>= 1.6), aws provider >= 6.0, < 7.0.
Without a security contact, security email goes to the root user. An abuse
report about one of your instances, an exposed credential, a vulnerability
notice - all of it arrives at the root address, which on most accounts is a
mailbox nobody opens. This is what Security Hub's Account.1 checks (CIS AWS
Foundations Benchmark v5.0.0 control 1.2, Config rule
security-account-information-provided), and this module refuses to run without
one unless allow_missing_security_contact says otherwise.
Use an address, not a person. AWS says it plainly: an alternate contact "doesn't have to be a specific person. You could instead add an email distribution list". A rota address outlives whoever set it up.
The module validates what AWS validates, which is not what the provider
validates. The provider's email check is an UNANCHORED
[\w+=,.-]+@[\w.-]+\.[\w]+, so it passes any string that merely
contains something address-shaped, and its character class allows a comma. Run
against the provider, all three of these plan cleanly and would be refused by the
API: a name-wrapped address, an address with a comma, and a 35-character phone
number (AWS's limit is 25). This module applies AWS's own anchored pattern and
its length limits, so the refusal arrives at plan time with a reason.
Destroying this removes the contacts. The provider's delete calls
DeleteAlternateContact, so a destroy returns the account to notifying only the
root user. That is worth knowing before a terraform destroy in an account
baseline.
Member accounts need trusted access. Setting account_id works from the
organization's management account or a delegated administrator, and only once
trusted access for the Account service is enabled. Without it the API refuses.
The primary_contact block sets the account's own legal address. It is optional,
and the module never clears it.
Verification
Static validation runs tofu fmt, init, validate, tflint and checkov.
This module has not yet had a live test, so it is published as statically
validated with its live test pending and does not carry the live-tested mark.
Usage code & full reference need an account
The complete copy-paste usage, the full input/output reference, and operational notes are free with an account - shown here and bundled in the download. Sign in and this section fills in.
- Usage
Related modules
aws-budgets
A budget with no notification tracks correctly and tells nobody, so the invoice is the first notice; ACTUAL-only alerts arrive after the money is gone. And include_credit defaults true, so a budget on a credited account reports runway rather than spend - until the credits end and nothing crosses a threshold.
aws-control-tower
A Control Tower landing zone from a manifest the module writes: the governed regions (the only usable ones), the Security and Sandbox units, centralized logging in the log archive account you name kept a year (access logs ten) under your KMS key (the AWS-managed key by name), the audit account, Identity Center access, and the controls you map to organizational units.
aws-account-baseline
The IAM password policy, S3 account-wide Block Public Access and, per region, default EBS encryption, snapshot and AMI sharing blocks, IMDSv2 as the default and the serial console off. Five of these are per region, so the module covers a list. The password policy follows NIST SP 800-63-4, and the module says which settings destroying it turns back off.
aws-cost-anomaly-detection
AWS Cost Anomaly Detection with monitors and the alerts that make them useful. A monitor alone leaves anomalies in the console; AWS sends immediate alerts only over SNS and summaries only by email, so this module builds both, with a topic encrypted by a key the service may use. It needs a cost threshold, and says an account holds one AWS services monitor.
aws-organizations
The organization, its organizational units, member accounts and the service control policies attached to them. Root attachment is off by default, because an SCP sets the ceiling on what anybody may do - including the account root user who would have to undo it.