What's Changed
New Contributors
[1.4.15] — 2026-08-10
Added
- RDS —
ManageMasterUserPasswordwires Aurora clusters to Secrets Manager —CreateDBCluster(ManageMasterUserPassword=true)previously ignored the flag: no secret was created, noMasterUserSecretwas returned, and code paths that resolve database credentials from Secrets Manager (the common production pattern) could not be rehearsed locally. The flag now generates a random master password, stores it in MiniStack's own Secrets Manager as{"username", "password"}under the AWS naming convention (rds!cluster-<uuid>), and returnsMasterUserSecret(SecretArn,SecretStatus,KmsKeyId) from create/describe/modify.ModifyDBCluster(RotateMasterUserPassword=true, ApplyImmediately=true)rotates the real database login through the same path as an explicit password change — including the pending-rotation behavior for stopped compute — and promotes the new credentials toAWSCURRENT(previous ones stay readable asAWSPREVIOUS); a rotation whose managed secret was deleted out from under RDS fails withInvalidDBClusterStateFaultand flipsSecretStatustoimpaired, as on AWS.DeleteDBClusterdeletes the managed secret with the cluster (and only when the delete actually succeeds). AWS-exact rejections:ManageMasterUserPassword+ explicitMasterUserPasswordat create, explicit password against a managed cluster,RotateMasterUserPasswordwithout a managed secret or withoutApplyImmediately, and explicit password + rotate flag in one request — validated before anything mutates. The RDS↔Secrets Manager seam is in-process and one-directional (rds → secretsmanager), reusing the emulator's existing stage-promotion and replica-sync internals. Contributed by @Kiran01bm. - EventBridge — API destination targets are now invoked over HTTP on
PutEvents— API destinations and connections were control-plane stubs: a matching rule with an api-destination target logged "unsupported event target ARN" and dropped the event, so webhook-style pipelines (EventBridge → HTTPS endpoint) could not be tested locally. Matching events now POST (or the destination's configured method) to theInvocationEndpointwith the input-selected payload (Input/InputPath/InputTransformerapply as for other targets). Connection authorization is honored:BASICpopulatesAuthorization: Basic …,API_KEYsends the configured header, andOAUTH_CLIENT_CREDENTIALSexchanges the client ID/secret at the authorization endpoint (OAuthHttpParametersmerged in,grant_type=client_credentialsdefaulted), caches the token per connection and invalidates it when the connection is deleted, re-authorized, or deauthorized (a connection recreated under a reused name never inherits its predecessor's token), refreshes proactively when it expires within 60 seconds, and refreshes + retries once on a401/407response — matching documented AWS behavior. ConnectionInvocationHttpParametersand targetHttpParametersare merged with connection values taking precedence (per theHttpParametersAPI reference),PathParameterValuespopulate*path wildcards, and body parameters fold into JSON-object bodies. Requests carry the AWS default headers (User-Agent: Amazon/EventBridge/ApiDestinationsandRangenon-overridable,Content-Typedefaulting toapplication/json; charset=utf-8), strip the headers real EventBridge removes, and time out after 5 seconds (the documented maximum client execution timeout). Delivery runs on a background thread mirroring the SNS HTTP(S) path. Not modeled, mirroring the cross-region FailedInvocations policy: the 24h/185-attempt retry pipeline,Retry-After, DLQs, andInvocationRateLimitPerSecond— retryable statuses (401,407,409,429,5xx) are logged and dropped. Contributed by @t-rech. - KMS — HMAC keys,
GenerateMac, andVerifyMac— the four HMAC key specs (HMAC_224/HMAC_256/HMAC_384/HMAC_512) withKeyUsage=GENERATE_VERIFY_MAC, RFC 2104 HMAC generation and constant-time verification (KMSInvalidMacExceptionon mismatch),MacAlgorithmsin the key metadata, andDryRun. HMAC keys are rejected byEncrypt/Decrypt/Sign/Verify/GenerateDataKey*, and automatic key rotation follows AWS:EnableKeyRotationandDisableKeyRotationreject them withUnsupportedOperationException, whileGetKeyRotationStatussucceeds and reportsKeyRotationEnabled: false. Contributed by @nafdev. - Lambda —
LAMBDA_KEEPALIVE_MS=0forces a per-invocation cold start — a LocalStack-compat lever (not an AWS behavior): for Docker RIE runtimes (Ruby/Java/.NET),LAMBDA_KEEPALIVE_MS=0tears the warm container down after each invocation so the next invoke re-runs INIT, giving deterministic cold-start isolation for test suites. Unset or any non-zero value keeps the warm-pool behavior. Reported by @mayankgupta57.
Fixed
- S3 — POST Object no longer misreads form fields as the object body — a multipart part was classified as the object content when its name was
fileor it carried afilenameattribute, but browsers and HTTP libraries (Pythonrequests'files=) setfilenameon ordinary form fields, so every field looked like the body, nokeyfield survived, and the upload was rejected withInvalidArgument. Only the field literally namedfileis the body now, matching S3. Reported by @gaul. - S3 —
GetObjectwithpartNumberreturns the requested part — thepartNumberparameter was dropped, so a client fetching an N-part object in parallel received N full copies. A completed multipart object now returns the requested part as206 Partial Contentwith aContent-Rangeandx-amz-mp-parts-count, matching S3. Reported by @gaul. - S3 —
ListObjectVersionsreturns continuation markers when truncated — a truncated response setIsTruncated=truebut emitted neitherNextKeyMarkernorNextVersionIdMarker, and the incomingversion-id-markerwas ignored, so a paginating client looped on page one or (as boto3 does) rejectedKeyMarker=None. The markers are now emitted andversion-id-markerresumes withinkey-marker. Reported by @gaul. - S3 —
CompleteMultipartUploadreturns400 MalformedXMLfor an unparseable body — an empty or malformed body raised an unguardedParseErrorthat escaped as a500with a JSON document no S3 SDK can parse, so clients treated it as a transient fault and retried. It now returns400 MalformedXML, as XML. Reported by @gaul. - S3 —
CompleteMultipartUploadis idempotent — the upload record was dropped on the first call, so a retry (how a client recovers from a lost response) returnedNoSuchUpload. The completed response is now retained and replayed for a repeat call with the same upload id, without minting a second object version, matching S3. Reported by @gaul. - S3 — object owner id is consistent between listings and ACLs —
ListBuckets/ListObjects/ListObjectVersions/ListPartshard-coded the owner idowner-idwhileGetObjectAcl/GetBucketAclused the account id, so a client matching an object's owner against an ACL grantee always got a mismatch. All of them now use the account id. Reported by @gaul. - S3 — presigned URLs are rejected once expired —
X-Amz-Date+X-Amz-Expireswere never compared against the current time, so a URL minted with a one-second lifetime served the object indefinitely. An expired presigned URL now returns403 AccessDenied(Request has expired), matching S3. Reported by @gaul. - S3 — conditional deletes honour
If-Match—DeleteObjectignored theIf-Matchheader andDeleteObjectsignored a per-objectETag, so a delete carrying a stale ETag removed the object anyway (and the batch reported it underDeleted).DeleteObjectnow returns412 PreconditionFailedon an ETag mismatch, andDeleteObjectsreports the key underError(PreconditionFailed) instead of deleting it, matching S3's compare-and-swap delete. Reported by @gaul. - CloudWatch Logs — ARN-based tag operations resolve vended-delivery resources —
TagResource,UntagResource, andListTagsForResourceonly resolved log-group ARNs, so the AWS provider's read-after-create onaws_cloudwatch_log_delivery_source/aws_cloudwatch_log_delivery_destination/aws_cloudwatch_log_deliveryfailed withResourceNotFoundExceptionand broketerraform applyof any stack using EventBridge bus logging (the community EventBridge module ≥ v4.1 provisions the trio). All three operations now resolve the delivery records' tags. Contributed by @t-rech. - Route 53 —
ChangeResourceRecordSetsDELETEnow requires the values provided to match the current values — aDELETEmatched only on name, type, and set identifier, so a delete carrying a stale TTL or stale record values silently removed the live record. Real Route 53 requires the values in aDELETEto match the current record exactly and rejects the whole batch withInvalidChangeBatchotherwise — the compare-and-swap semantics that guarded-delete workflows (delete only if the record still holds the values I last observed) rely on to detect concurrent modification, which the emulator's silent success defeated. A mismatchedDELETEnow fails the batch atomically with the AWS-shaped message (Tried to delete resource record set [name='…', type='…'] but the values provided do not match the current values); record values are compared as an unordered set, so the same values in a different order still match. Contributed by @jayjanssen. - S3 —
CopyObjectandHeadObjecthonour the sourceversionId— a?versionId=on the copy source (and onHeadObject) was discarded, so both operated on the current object instead of the requested version.CopyObjectnow copies the exact version and echoesx-amz-copy-source-version-id,HeadObjectreturns that version's metadata, and a non-existent version is rejected withNoSuchVersion. Reported by @Kaphaalor. - SQS — FIFO deduplication holds for the full 5-minute window — the dedup entry was cleared when a message was deleted (including the Lambda event-source-mapping consume path), so a duplicate sent seconds after the original was consumed was delivered again. A
MessageDeduplicationIdis now retained for its full 5-minute window from send time regardless of receive/delete, matching AWS FIFO semantics. Reported by @giannimassi. - S3 —
NewerNoncurrentVersionssurvives the lifecycle configuration round-trip —NoncurrentVersionExpirationandNoncurrentVersionTransitiondroppedNewerNoncurrentVersionson thePUT/GETround-trip, so terraform-provider-aws never converged andterraform applyof a lifecycle configuration timed out. The field is now emitted and parsed on both rules. Contributed by @sac-outsystems. - Step Functions — aws-sdk integration preserves query-protocol singleton lists — the query-XML to JSON converter collapsed a known list wrapper with an irregular item name (e.g.
VpcSecurityGroupstoVpcSecurityGroupMembership) into an object when it held a single item, so SDK consumers expecting a stable list shape broke. Known wrappers now decode to a list for zero, one, or multiple items. Contributed by @Areson. - CodeBuild — a timed-out build reports
TIMED_OUT— a build stopped bytimeoutInMinuteswas labelledFAILEDinstead ofTIMED_OUT, soBatchGetBuildscould not distinguish a timeout from a genuine build failure. It now reports theTIMED_OUTbuild status, matching AWS. - RDS Data API — requests that AWS rejects no longer succeed through permissive fallbacks — the Data API accepted calls that real AWS refuses, so a local integration passed where the equivalent AWS request would fail. A cluster whose HTTP endpoint is not enabled now returns
HttpEndpointNotEnabledException(SQL runs only afterEnableHttpEndpoint); a secret that is absent, scheduled for deletion, or missing a password returnsSecretsErrorException/InvalidSecretException, and this validation now applies in stub mode too.CommitTransaction/RollbackTransactionnow requireresourceArnandsecretArn(AWS marks both required), and a transaction is bound to its originating cluster: a mismatched or unknown transaction returnsTransactionNotFoundException(404) fromExecuteStatement/BatchExecuteStatementandNotFoundException(404) fromCommitTransaction/RollbackTransaction, matching AWS's per-operation error model. Statement timeouts surface asStatementTimeoutExceptionand unmodeled stub-mode SQL asBadRequestExceptioninstead of a fabricated success. Contributed by @Areson.