How do I encrypt Amazon Connect call recordings, transcripts, and Contact Lens analytics with my own AWS KMS keys?
Connect encrypts customer content at rest by default with a service-managed KMS key, but customers in regulated industries (PCI DSS v4.0, GDPR, HIPAA) typically need full key control — independent rotation, granular IAM, auditable decrypt logs, and the ability to revoke service access. This article walks through configuring Amazon Connect to use a customer-managed KMS key (CMK) for call recordings, chat transcripts, screen recordings, and Contact Lens output, with a deployable AWS CDK example.
Short description
Amazon Connect stores customer content in an Amazon S3 bucket that you associate with the instance via AWS::Connect::InstanceStorageConfig. Each storage configuration takes a ResourceType (such as CALL_RECORDINGS, CHAT_TRANSCRIPTS, SCREEN_RECORDINGS) and an EncryptionConfig that points at your CMK. When you call AssociateInstanceStorageConfig, Connect creates a KMS grant on your CMK with the instance's service-linked role (AWSServiceRoleForAmazonConnect_*) as the grantee — that role, not a generic service principal, is what reads and writes encrypted objects at runtime. Contact Lens conversational analytics output (transcripts and redacted audio) is written under the Analysis/ prefix of the same bucket and inherits the same SSE-KMS encryption.
Resolution
1. Why use a customer-managed CMK
| Benefit | What it gives you |
|---|---|
| Granular key policy | Decide which IAM principals (Connect SLR via grant, compliance auditors, recovery roles) can call kms:Decrypt |
| CloudTrail auditability | Every Encrypt / Decrypt / GenerateDataKey call against your CMK is logged |
| Independent rotation | KMS automatic rotation is configurable (default 365 days, range 90–2560 days); manual rotation any time without re-encrypting objects |
| Revocation | Disable the key, revoke the grant, or schedule deletion to render existing ciphertext unreadable |
| External Key Store (XKS) | Hold key material outside AWS for compliance programs that require it (verify XKS support per downstream Connect feature before relying on it) |
The Connect service-linked role needs kms:Decrypt, kms:Encrypt, kms:GenerateDataKey*, and kms:DescribeKey to read and write encrypted objects. Connect creates the grant automatically when you associate a CMK with a storage configuration — the IAM principal calling AssociateInstanceStorageConfig (your CDK/CloudFormation deployment role) must hold kms:CreateGrant on the CMK for that association to succeed.
2. Storage resource types you need to know
A common mistake is assuming CHAT_TRANSCRIPTS covers Contact Lens voice transcripts. It does not. Contact Lens conversational analytics output is written automatically under the Analysis/ prefix of whichever S3 bucket is associated with the instance for CALL_RECORDINGS (voice) or CHAT_TRANSCRIPTS (chat).
S3-backed storage types (these accept EncryptionConfig with your CMK):
| ResourceType | What lands here |
|---|---|
CALL_RECORDINGS | Voice call audio (WAV, stereo); Contact Lens voice transcripts and redacted audio land in the same bucket under Analysis/Voice/ and Analysis/Voice/Redacted/ |
CHAT_TRANSCRIPTS | Chat transcripts; Contact Lens chat output lands under Analysis/Chat/ and Analysis/Chat/Redacted/ |
SCREEN_RECORDINGS | Agent screen recordings (when enabled) |
ATTACHMENTS | Files exchanged in chat |
Streaming-backed storage types (encryption is configured on the destination stream, not on the storage config's EncryptionConfig):
| ResourceType | Destination |
|---|---|
CONTACT_TRACE_RECORDS | Kinesis Data Stream or Kinesis Data Firehose |
AGENT_EVENTS | Kinesis Data Stream |
MEDIA_STREAMS | Kinesis Video Streams (encrypt with KVS-side CMK) |
REAL_TIME_CONTACT_ANALYSIS_SEGMENTS | Kinesis Data Stream |
For the streaming targets, set the CMK on the underlying Kinesis/KVS resource — Connect publishes into a stream you've already encrypted. The CDK example below covers the most common S3 targets: CALL_RECORDINGS, CHAT_TRANSCRIPTS, and SCREEN_RECORDINGS.
Email channel: Contact Lens output for Email also lands under
Analysis/Email/if you have email enabled. See the Output file locations page for the current list of supported channels in your Region.
3. Architecture
All S3 objects above are encrypted with your CMK via SSE-KMS. For media in transit, Connect uses TLS 1.2+ for signaling and REST APIs, and DTLS-SRTP for the WebRTC media plane.
4. Deploy with AWS CDK (Python)
from aws_cdk import ( Stack, aws_kms as kms, aws_s3 as s3, aws_connect as connect, RemovalPolicy, ) from constructs import Construct class ConnectEncryptionStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs): super().__init__(scope, id, **kwargs) # CDK's kms.Key creates a default policy granting account root full # administrative access. The Connect service-linked role does NOT # appear in the key policy directly — it gets access through a grant # that AssociateInstanceStorageConfig creates on your behalf. connect_key = kms.Key( self, "ConnectDataKey", alias="alias/connect-data-key", enable_key_rotation=True, description="Customer-managed CMK for Amazon Connect recordings and transcripts", ) recording_bucket = s3.Bucket( self, "ConnectRecordingBucket", encryption=s3.BucketEncryption.KMS, encryption_key=connect_key, bucket_key_enabled=True, block_public_access=s3.BlockPublicAccess.BLOCK_ALL, enforce_ssl=True, versioned=True, removal_policy=RemovalPolicy.RETAIN, ) instance = connect.CfnInstance( self, "SecureConnectInstance", identity_management_type="CONNECT_MANAGED", instance_alias="secure-contact-center", attributes=connect.CfnInstance.AttributesProperty( inbound_calls=True, outbound_calls=True, contactflow_logs=True, contact_lens=True, auto_resolve_best_voices=True, ), ) kms_encryption = connect.CfnInstanceStorageConfig.EncryptionConfigProperty( encryption_type="KMS", key_id=connect_key.key_arn, ) for logical_id, resource_type, prefix in [ ("CallRecordingsStorage", "CALL_RECORDINGS", "call-recordings/"), ("ChatTranscriptsStorage", "CHAT_TRANSCRIPTS", "chat-transcripts/"), ("ScreenRecordingsStorage", "SCREEN_RECORDINGS", "screen-recordings/"), ]: connect.CfnInstanceStorageConfig( self, logical_id, instance_arn=instance.attr_arn, resource_type=resource_type, storage_type="S3", s3_config=connect.CfnInstanceStorageConfig.S3ConfigProperty( bucket_name=recording_bucket.bucket_name, bucket_prefix=prefix, encryption_config=kms_encryption, ), )
A few notes that matter in production:
- Deployer permissions. The principal running
cdk deploymust holdkms:CreateGranton the CMK. The default CDK key policy (account-root access) covers this for most setups; if you tighten the policy, add an explicit allow for the deployment role. bucket_key_enabled=Trueuses an S3 Bucket Key — a bucket-level intermediate key derived from your CMK — to dramatically reduce KMS API charges on high-call-volume instances. Trade-off: per-object KMSGenerateDataKey/Decryptevents no longer appear individually in CloudTrail (a small number of audit programs require per-object visibility; most PCI/HIPAA audits accept Bucket Key).enforce_ssl=Trueadds a bucket policy that denies any non-TLS request — required for most compliance audits.- Versioning lets you recover from accidental deletion or overwrite. Pair with an S3 Lifecycle rule to expire non-current versions after your retention window.
5. Verify encryption end-to-end
After deployment, place a test call and end it. Then:
-
S3 console: Connect writes recordings under a path like
<bucket>/<bucket-prefix>/connect/<instance-alias>/CallRecordings/<YYYY>/<MM>/<DD>/<contactId>_<timestamp>.wav. Open one of those objects and under Server-side encryption settings confirmAWS Key Management Service key (SSE-KMS)with the key ARN matching your CMK. -
CLI:
aws s3 ls "s3://<bucket-name>/call-recordings/" --recursive | tail -1 aws s3api head-object \ --bucket <bucket-name> \ --key <full-key-from-previous-command>Confirm
"ServerSideEncryption": "aws:kms"and that"SSEKMSKeyId"matches your CMK ARN. With Bucket Key enabled you'll also see"BucketKeyEnabled": true. -
CloudTrail: Filter on event source
kms.amazonaws.comand resource ARN = your CMK. You'll seeCreateGrantevents from the originalAssociateInstanceStorageConfigcall, andGenerateDataKeycalls from the Connect SLR during recording delivery (less frequent with Bucket Key — one per bucket-key rotation rather than per object). -
Contact Lens: After post-call analysis completes (typically 1–2 minutes), check
s3://<bucket>/Analysis/Voice/<YYYY>/<MM>/<DD>/. The transcript JSON and the redacted audio (underAnalysis/Voice/Redacted/) should be there, encrypted with the same CMK.
6. Production considerations
-
Restrict the key policy. The Connect SLR gets access via grant; agents and most analytics users do not need anything in the key policy at all. Compliance auditors should have a separate role with
kms:Decryptonly when they need to read the original (un-redacted) transcript. If you tighten the default key policy, keepkms:CreateGrantavailable to the deployer role andkms:Decryptavailable to whoever consumes recordings downstream (Athena workgroups, QuickSight, S3-event-driven Lambdas). -
Lifecycle policy. S3 Lifecycle rules to transition recordings to S3 Glacier Instant Retrieval after 30–90 days, then expire after your retention window (commonly 7 years for financial services, 6 years for HIPAA).
-
Object Lock. Enable S3 Object Lock in compliance mode if regulators require WORM retention. Object Lock is easiest to enable at bucket creation; since November 2023 it can also be enabled on existing buckets via API by contacting AWS Support — plan ahead either way.
-
Cross-Region replication (CRR). If you need a DR copy, use SSE-KMS replication with a separate CMK in the destination Region. Replication preserves encryption but re-encrypts with the destination key.
-
Voice ID — important deprecation notice. AWS will end support for Amazon Connect Voice ID on May 20, 2026. New deployments should not adopt Voice ID; existing customers should plan migration to a third-party voice biometrics integration before the EOL date. Voice ID's KMS configuration was set on the Voice ID domain (
voiceid:CreateDomainwith aServerSideEncryptionConfigurationblock) — not viaInstanceStorageConfig. TheAWS::Connect::InstanceStorageConfigresource has noVOICE_IDResourceType. -
Customer Profiles and Connect AI agents. These features encrypt their stores independently of the S3 storage configuration above:
- Customer Profiles: configure a CMK at domain creation, optionally a per-Object-Type CMK. Once Data Vault is enabled, you cannot rotate the domain or Object Type CMK — plan key choice carefully up-front.
- Connect AI agents (formerly Wisdom / Amazon Q in Connect): supports BYOK for knowledge documents and call transcripts; search indices are always encrypted with an AWS-owned key.
-
Cases. Connect Cases encrypts customer-provided data with AWS-owned keys only — there is currently no BYOK option, so Cases is out of scope for a CMK strategy.
Related information
- Encryption at rest in Amazon Connect
- Encryption in transit in Amazon Connect
- Key management in Amazon Connect
- Output file locations for Contact Lens
- Best practices for PCI compliance in Amazon Connect
- Best practices for HIPAA compliance in Amazon Connect
- AWS::Connect::InstanceStorageConfig CloudFormation reference
Relevant content
- Accepted Answer
- asked 4 years ago
AWS OFFICIALUpdated 6 months ago