aws-cloudformation / aws-cloudformation/cloudformation-coverage-roadmap
[AWS::ECR::Repository] Adding EncryptionConfiguration matching live AES256 default forces destructive Replacement on imported repos
- Dominant language
- No language data
- Stars
- 1.1k
- Forks
- 62
- PR merge metrics
- No merged PRs in 30d
Description
### Name of the resource
AWS::ECR::Repository
### Resource Name
_No response_
### Issue Description
The `AWS::ECR::Repository.EncryptionConfiguration` property is documented as **`Update requires: Replacement`**. AES256 is the AWS default for every ECR repository — every repo created without an explicit encryption block already has `EncryptionConfiguration.EncryptionType: AES256` in its live state.
When a repo is imported into a CloudFormation stack without an explicit `EncryptionConfiguration` declaration in the template, and a user later adds the declaration `EncryptionConfiguration: { EncryptionType: AES256 }` to match the live state, CloudFormation generates a changeset with `Replacement: True`. Executing this changeset would destroy the repository (and all image history) and create a new one — even though the live encryption state would be identical before and after.
This is technically consistent with the documented "Update requires: Replacement" contract, but it produces a surprising and destructive outcome for a no-op-equivalent change. Several common workflows hit this wall:
- **Security-tool-driven remediation.** Static analyzers (Aikido, Checkov, cfn-nag, tfsec, etc.) flag missing `EncryptionConfiguration` declarations and demand explicit settings, even when the live default already encrypts at rest. Users cannot resolve the finding without either (a) destroying their repos or (b) suppressing the rule.
- **Drift-resistance hardening.** Teams adopting CloudFormation want explicit declarations to lock against drift. For imported repos, this is currently impossible without replacement.
- **Compliance audits.** Auditors increasingly want to see encryption explicitly declared in IaC. The current behavior pushes teams toward documentation-only workarounds.
### Expected Behavior
When a property declaration is added to an imported resource and the declared value matches the resource's actual live state at import time, CloudFormation should resolve the diff as no-op (`Modify` with no actual API call) rather than `Replacement`.
Specifically for `AWS::ECR::Repository.EncryptionConfiguration`: declaring `EncryptionType: AES256` on a repo whose live state is already AES256 should not require replacement.
### Observed Behavior
Changeset preview shows `Replacement: True` on every imported ECR repo when `EncryptionConfiguration: { EncryptionType: AES256 }` is added to a previously-undeclared template. Executing the changeset would destroy the repos.
### Test Cases
**Setup:** import an existing ECR repository (created with default AES256 encryption) into a CloudFormation stack with this template:
```yaml
Resources:
MyRepo:
Type: AWS::ECR::Repository
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
RepositoryName: my-existing-repo
ImageTagMutability: MUTABLE
ImageScanningConfiguration:
ScanOnPush: true
```
Confirm import succeeds and the repo's live `EncryptionConfiguration.EncryptionType` is `AES256` (the default).
**Reproduce:** add `EncryptionConfiguration` to the template:
```yaml
Resources:
MyRepo:
Type: AWS::ECR::Repository
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
RepositoryName: my-existing-repo
ImageTagMutability: MUTABLE
ImageScanningConfiguration:
ScanOnPush: true
EncryptionConfiguration: # Added — matches live state
EncryptionType: AES256
```
Create a change set and inspect:
```bash
aws cloudformation create-change-set \
--stack-name my-stack \
--template-body file://template.yaml \
--change-set-name add-encryption \
--change-set-type UPDATE \
--capabilities CAPABILITY_IAM
aws cloudformation describe-change-set \
--stack-name my-stack \
--change-set-name add-encryption
```
**Result:** changeset shows `Action: Modify, Replacement: True` on the repository. Executing it would destroy and recreate the repo.
### Other Details
**Why this matters:**
- ECR repos contain image history that is not trivially reproducible. Re-pushing all tags from CI is possible but disruptive (multi-minute outages for every consumer pulling during the gap; no atomic swap; lifecycle-policy state lost).
- AWS does not allow ECR encryption to be turned off — the property only exists to choose between AES256 and KMS-backed. So declaring AES256 on a repo that has AES256 cannot result in a meaningful state change at the AWS API level.
- The same logical pattern exists for many other resources where a property has an implicit live default and adding the explicit declaration matching that default forces replacement.
**Workaround (used to address this in our codebase):** remove the resource from the stack via `DeletionPolicy: Retain` UPDATE, then re-import via IMPORT change-set with the explicit declaration matching live state. CFN's import flow validates declarations against live AWS state (not previous template state), so declared-AES256-against-live-AES256 succeeds without replacement. This works but is operationally heavy: requires two coordinated change-sets per stack, breaks drift detection during the gap, and must be repeated for every imported resource hit by this pattern.
**Related issues** (same general pattern, different specifics):
- #1045 — `Fn::GetAtt` syntax form changes treated as property modifications during Update / Import
- #2356 — `ElastiCache::ReplicationGroup` AuthToken→UserGroupIds in-place via Console/CLI but Replacement via CFN
**Suggested fix scope:** at minimum, treat `AWS::ECR::Repository.EncryptionConfiguration` adds whose value matches the imported live state as `Modify` with `Replacement: False`. More broadly, consider this pattern for any property where the imported-state value is known and matches the new declaration.
### References
- [AWS::ECR::Repository docs (`EncryptionConfiguration: Update requires: Replacement`)](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecr-repository.html)
- [ECR API `EncryptionConfiguration` reference](https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_EncryptionConfiguration.html)
Contributor guide
Research direction
Start by importing an existing AES256 ECR repository, add the explicit EncryptionConfiguration shown in the issue, and run the provided create-change-set and describe-change-set commands. Compare the resulting Action and Replacement fields with the expected no-op-equivalent behavior; done means matching live AES256 does not require destructive replacement.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws
- Domain
- cloud, infrastructure
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100