github ministackorg/ministack v1.5.36

3 hours ago

What's Changed

New Contributors

[1.5.36] — 2026-11-02

Added

  • CloudFormation — AWS::Events::Archive — a template with an EventBridge archive was refused as an unrecognized resource type, though the archive API itself was supported. It now creates the archive: Ref is the archive name, a generated one is the logical id cut to 35 characters, - and 12 letters and digits, and Fn::GetAtt Arn is its ARN; Fn::GetAtt on any other property rolls the stack back with AWS's "must be a readonly property in schema". The event pattern is stored compact, with non-ASCII characters as written and each object's keys in the order AWS stores them. A changed Description, RetentionDays, EventPattern or KmsKeyIdentifier updates the archive in place, and one left out of the template keeps its value (an empty KmsKeyIdentifier clears the key); an update that leaves none of the first three fails with the handler's message, so rolling an update back to such a template ends UPDATE_ROLLBACK_FAILED, as on AWS. A new ArchiveName replaces the archive, a new SourceArn replaces a generated one and is refused for a custom name, and a stack delete removes it. Early Validation refuses a template that breaks the resource schema (a missing SourceArn, an ArchiveName too long, too short or outside its pattern, a value of the wrong type, an unsupported property, a KmsKeyIdentifier over 2048 characters) and a new archive whose name is taken, with AWS's messages and in AWS's order. The handler refuses what the schema lets through, as on AWS: a name or SourceArn outside the API's pattern, a negative RetentionDays, a KMS key that does not exist, and relays EventBridge's own refusals (a taken name, a bus that does not exist, an invalid pattern) in its Resource handler returned message form with the SDK's suffix. Measured against a real account in eu-west-1. Contributed by @toby6578.
  • CloudFormation — AWS::CloudFormation::StackSet with one stack instance in its own account and Region — a template with a StackSet was refused as an unrecognized resource type. A SELF_MANAGED StackSet whose one StackInstancesGroup targets the stack's own account and Region now deploys its inline TemplateBody as a stack instance through the nested-stack engine, which updates it, rolls a failed update back with the stack and deletes it. Ref returns the StackSetId; DescribeStackSet (by name, or by StackSetId also once deleted) and ListStackInstances answer with the shapes of a real-AWS capture. Any other StackSet fails the resource with an error naming what is deployed. Contributed by @chris-plucker.
  • S3 Control — account-level public access block — PutPublicAccessBlock, GetPublicAccessBlock and DeletePublicAccessBlock on /v20180820/configuration/publicAccessBlock, which answered InvalidURI. The configuration is held per account and applies in every region, and its settings apply to the account's buckets with their own (BlockPublicPolicy refuses a public bucket policy); DeletePublicAccessBlock is authorized as s3:PutAccountPublicAccessBlock. An unset GetPublicAccessBlock answers 404 NoSuchPublicAccessBlockConfiguration carrying <AccountId> inside <Error>, and an unsupported method 405 MethodNotAllowed with ResourceType ACCOUNT_PUBLIC_ACCESS_BLOCK. Responses and errors verified against a real AWS account. Contributed by @pingedbrain.
  • SSM — documents and automation executions — CreateDocument, DescribeDocument, GetDocument, ListDocuments, DeleteDocument, and StartAutomationExecution, GetAutomationExecution, DescribeAutomationExecutions, StopAutomationExecution, which answered InvalidAction. Documents are stored per account and region with AWS's real shapes: Hash is the SHA-256 of the verbatim content, Status goes Creating → Active, PlatformTypes defaults to all three platforms, CreatedDate is sent as epoch seconds, GetDocument returns DocumentFormat JSON by default with the content re-serialized in AWS's canonical JSON layout, and a document's declared parameters appear in the description. Content is validated on create: unparsable JSON/YAML answers InvalidDocumentContent with AWS's "not well-formed" messages, an Automation document without mainSteps Missing steps in "mainSteps"., an unknown step action Unknown action: <action>, a bad DocumentType AWS's ValidationException, a duplicate name DocumentAlreadyExists, and a name with a prefix AWS reserves (aws, amazon, amzn, AWSEC2, AWSConfigRemediation, AWSSupport) is refused. Executions build real StepExecutions from the document's mainSteps (one StepExecutionId each, inputs JSON-stringified with {{ param }} references resolved from declared defaults and passed Parameters), expose CurrentStepName/CurrentAction while running, and run the steps they can: aws:sleep waits its ISO-8601 duration, aws:executeAwsApi dispatches into the emulated ssm API, aws:runCommand dispatches SendCommand; a step MiniStack cannot execute fails the execution with AWS's FailureMessage/FailureDetails shape instead of reporting a fabricated success, and StopAutomationExecution lands Cancelled; under AUTH=true a step calls the API with the permissions of the document's assumeRole, or of the caller, so a step the caller may not run fails with AccessDeniedException. StartAutomationExecution answers AutomationDefinitionNotFoundException for a document the account doesn't hold and InvalidAutomationExecutionParametersException for undeclared parameters. Responses and errors verified against a real AWS account. Contributed by @pingedbrain.

Fixed

  • API Gateway — REST, HTTP and WebSocket APIs answer callers from any account — an execute-api call reached an API only in the account its access key selected, so an unsigned call, which MiniStack serves as the default account, or a call signed by another account answered 404 {"message": "Not Found"} for an API another account owns. On AWS an execute-api URL reaches its API whatever the caller (captured on AWS): a method without authorization answered 200 unsigned, with another account's Authorization header and when signed by another account. The call now runs as the API's owner account, and the caller is authenticated in its own account. Under AUTH=true an AWS_IAM method answers a caller from another account that no REST API policy grants with 403 AccessDeniedException {"Message": "User: <caller> is not authorized to access this resource"}, as AWS does, and a policy allowing that account lets it in. An HTTP or WebSocket API is served as its owner the same way. Contributed by @chandra-purbia.
  • CloudFormation — validation and provisioning errors carry no OperationStatus — DescribeEvents gave a create or update's VALIDATION_ERROR and PROVISIONING_ERROR events OperationStatus FAILED, which AWS's don't have. Measured against a real account in eu-west-1. Contributed by @toby6578.
  • CloudFormation — change sets run Early Validation — a change set whose template fails pre-deployment validation was created and failed only on execution; CreateChangeSet now checks the resources it adds or modifies and fails it as AWS does: FAILED with "The following hook(s)/validation failed: [AWS::EarlyValidation::...]", naming the ResourceExistenceCheck and PropertyValidation hooks in the order of their first errors, keeping its changes. DescribeEvents for the change set (OperationType CREATE_CHANGESET) lists, newest first, its outcome, its validation errors by logical id and its start, and the stack's own events don't include them; a CREATE or UPDATE change set that succeeds has its start and SUCCEEDED, and one with no changes has none. DisableValidation skips it. ExecuteChangeSet on a failed change set answers AWS's "cannot be executed in its current status of [FAILED]". AWS let some invalid in-place changes through to fail at execution, from a change set or UpdateStack, and refused others at validation; MiniStack refuses them all at validation. Measured against a real account in eu-west-1. Contributed by @toby6578.
  • CloudWatch Logs — every stored event has an eventId — events written by a Lambda, a CodeBuild build or a DynamoDB import came back from FilterLogEvents without the eventId AWS always returns, so aws logs tail --follow stopped with KeyError: 'eventId' on a Lambda's log group. Contributed by @chandra-purbia.
  • Cognito — a failing PreTokenGeneration Lambda fails the request — a trigger that returned an error, including running past its own timeout, was logged and the token issued without its claims, and one that could not be invoked did the same unless MINISTACK_COGNITO_PRETOKEN_STRICT=1 turned either into a 500. As on AWS, InitiateAuth, RespondToAuthChallenge, the refresh flows and /oauth2/token now answer 400 UserLambdaValidationException <trigger> failed with error <errorMessage>. for the first and UnexpectedLambdaException for the second; /oauth2/token carries the text and code in error. MINISTACK_COGNITO_PRETOKEN_STRICT is removed. Contributed by @ryuzo-hayashi.
  • Cognito — PreSignUp and CustomMessage Lambda errors — a Lambda that returned an error failed the request with its raw error body as the message; it is now AWS's UserLambdaValidationException text, PreSignUp failed with error <errorMessage>. and CustomMessage failed with error <errorMessage>.
  • ElastiCache — replication group endpoints after a restart — with persisted state, a replication group's containers were respawned on restart but its PrimaryEndpoint, ReaderEndpoint, members' ReadEndpoint and ConfigurationEndpoint kept the old container's address; they now report the new one, as cache clusters already did. Reported by @chris-plucker.
  • EventBridge — an archive created without a description or a pattern has neither — DescribeArchive returned "Description": "" and "EventPattern": ""; it now leaves both keys out, as AWS does. Contributed by @toby6578.
  • EventBridge — CreateArchive refusals — an archive on an event bus that does not exist was created; it now answers ResourceNotFoundException "Event bus does not exist.", and a taken name's ResourceAlreadyExistsException ends with a period, as AWS's does. Contributed by @toby6578.
  • EventBridge — PutEvents to a bus ARN in another region — an entry naming an event bus ARN in a region other than the request's failed on its own with ResourceNotFoundException. AWS refuses the whole request instead: it answered 400 ValidationException "Cross-region api call is not allowed." for a bus in the caller's own account (captured on AWS). PutEvents now answers the same, before any entry is delivered. Contributed by @chandra-purbia.
  • EventBridge — rule targets in another account — PutTargets accepted an SQS (or other) target in another account without a RoleArn, and the events were then silently dropped. As on AWS, it now answers ValidationException "RoleArn is required for target ." for such a target in any region, and with an execution role an SQS target in another account of the same region receives the events, as AWS's cross-account targets do, and under AUTH=true only when the queue's policy allows that role. A target in another region is still accepted and not reached. Contributed by @chandra-purbia.
  • EventBridge — rule targets that are event buses in another account or region — PutTargets accepted such a target and every event was then dropped ("event bus … not found"). An event now reaches the target bus's rules in its owner's account and region, keeping its account and region; crossing into another region gives it a new id and time. As on AWS, a forwarded event reaches another bus in the same region only once and crosses regions only once (after which it may make one more same-region hop), which also stops a chain of rules from looping, so the per-rule recursion guard is gone. PutTargets answers AWS's errors for event bus targets: after its existing ARN checks, 400 AccessDeniedException Access to the resource <role> is denied. Reason: Cross-account pass role is not allowed. for another account's role; then, target by target, Source EventBus and Target EventBus must not be the same. for the rule's own bus and Retry policy is not supported for Event bus targets. for any RetryPolicy; then RoleArn is required for target <arn>. for any bus target without a role, the rule's own account and region included (it was RoleArn is required, and only for another account or region). CloudFormation's AWS::Events::Rule now runs PutTargets' checks on create, as it did on update, so a target that is not a well-formed ARN of a supported service now fails the create too. With AUTH=true the RoleArn must name the rule account's role exactly, its trust policy must let EventBridge assume it for the rule (conditions see the rule's account as aws:SourceAccount and its ARN as aws:SourceArn), and it must be allowed events:PutEvents on the bus, with aws:RequestedRegion the bus's region; a Deny in the bus's policy refuses it, and a bus in another account must also allow it, with events:eventBusInvocation true for its conditions. MiniStack does not model target dead-letter queues, so a refused event is dropped and logged with the ERROR_CODE AWS records (NO_PERMISSIONS, THIRD_ACCOUNT_HOP_DETECTED or THIRD_REGION_HOP_DETECTED). A schedule whose target is an event bus still publishes to it as before. Recorded with three accounts in eu-west-2, us-east-1 and us-west-2. Contributed by @toby6578. Reported by @lucasmfraser.
  • KMS — keys in other accounts, missing keys and denials answer as AWS does — another account's key named by key ARN or alias ARN, or by the ciphertext Decrypt is given, is now governed by its key policy in both auth modes. A refusal names the key when it exists and not when it does not; operations AWS does not serve across accounts answer This operation cannot be called cross account; another Region's ARN answers Invalid arn <region>. A key or alias that does not exist answers NotFoundException before any permission check, worded as AWS words it, and a denial no policy allows is HTTP 400 naming the key. DescribeKey carries AWSAccountId, GetKeyRotationStatus carries KeyId (and no period while rotation is off), and the default key policy has AWS's layout. Recorded with two accounts in us-east-1. Contributed by @Jolley71717. Reported by @kharabasz.
  • Lambda — nodejs24.x refuses callback-based handlers — a handler taking a callback ran on nodejs24.x; as AWS's Node.js 24 runtime does, a handler with three or more parameters that is not a response-streaming handler now fails with Runtime.CallbackHandlerDeprecated "ERROR: AWS Lambda has removed support for callback-based function handlers starting with Node.js 24. …", with the local executor and with LAMBDA_EXECUTOR=docker, and local workers carry AWS_EXECUTION_ENV (AWS_Lambda_<runtime>) as AWS's runtimes do.
  • Lambda — Node.js functions in a "type": "module" package under LAMBDA_EXECUTOR=docker — the context shim MiniStack adds to a Node function was written as _msctx_shim.js, which Node loads as an ES module when the package's package.json says "type": "module", so every invoke failed with ReferenceError: require is not defined in ES module scope. It is now _msctx_shim.cjs, which Node always loads as CommonJS, and the function runs as it does on AWS. Contributed by @Jolley71717. Reported by @evan-burrell.
  • Lambda — a function with no declared architecture after an arm64 one — with LAMBDA_EXECUTOR=docker, invoking an arm64 function pulled the arm64 image under the shared runtime tag, and a function that declared no architecture then ran that image on an x86 host and failed with exec format error; the host's image is now pulled back and run.
  • RDS — a cluster master password change is visible before it settles — ModifyDBCluster with MasterUserPassword changed the password at once and every DescribeDBClusters reported available, so a client waiting to observe the reset never saw it. As captured from real AWS, the ModifyDBCluster response and the first DescribeDBClusters show the cluster available with PendingModifiedValues.MasterUserPassword ****, the next reports resetting-master-credentials (still pending), and later ones available with nothing pending. A password change on a stopped cluster now fails with InvalidDBClusterStateFault ("Db cluster cannot be in the stopped state."), as AWS does. Contributed by @chris-plucker.
  • RDS — default Aurora cluster parameter groups — DescribeDBClusterParameterGroups and DescribeDBClusterParameters answered DBParameterGroupNotFound for a default group such as default.aurora-postgresql16, and a new cluster reported default.aurora-postgresql with no version; each Aurora family in the engine-version catalog now has its default.<family> group, and a cluster created without a parameter group reports its engine version's default, as AWS names it; an unfiltered DescribeDBClusterParameterGroups lists a default group only once it exists in the account (described by name or used by a cluster), as AWS does, and a major engine upgrade moves a cluster on the default group to the target version's default while keeping a custom group (the upgrade is reported as pending, then upgrading, then done, and member instances on the old default group follow), and a default group cannot be modified (InvalidParameterValue "Cannot modify a default parameter group.") or deleted (InvalidDBParameterGroupState "Default DBClusterParameterGroup cannot be deleted: "), while a reset succeeds; deleting a cluster parameter group that does not exist answers "DBClusterParameterGroup not found: ". Contributed by @chris-plucker.
  • RDS — ModifyDBCluster major upgrades need AllowMajorVersionUpgrade — a change to another major version went through without it; it now answers 400 InvalidParameterCombination "The AllowMajorVersionUpgrade flag must be present when upgrading to a new major version.", as AWS's Aurora upgrade guide shows.
  • S3 — Tagging on CreateMultipartUpload — the x-amz-tagging header was ignored, so an object uploaded in parts had no tags, including S3-to-S3 copies by aws s3 cp of objects large enough to copy in parts. The tags now land on the completed object's version, decide tag-filtered replication rules and carry to the replica, as with PutObject; more than 10 answer 400 BadRequest "Object tags cannot be greater than 10" at create time, and an untagged multipart upload that overwrites a tagged object leaves it without tags, as on AWS. Contributed by @zalefin.
  • S3 — an overwritten object's tags and ACL — PutObject and a POST upload without tags or an ACL kept the overwritten object's, so GetObjectTagging and GetObjectAcl reported them on the new object; as on AWS, the new object starts with no tags and the default ACL.
  • S3 Tables — Iceberg last-column-id counts nested fields — a table created through the Iceberg REST catalog reported the number of top-level fields as its last-column-id, and add-schema advanced it to the highest top-level id, so with a list, map or struct column a client's next column reused a nested field's id and Trino's ADD COLUMN failed with Multiple entries with same key. It is now the highest field id anywhere in the schema, a list's element-id and a map's key-id and value-id included, as on AWS. Contributed by @Jolley71717. Reported by @Wazabit.
  • S3 Tables — Iceberg REST dropTable and dropNamespace — DELETE …/namespaces/{ns}/tables/{table} and DELETE …/namespaces/{ns} answered Unknown Iceberg REST path, so Trino's DROP TABLE failed. A table is now dropped, with its metadata files, when the call sets purgeRequested=true, so a table created again under the same name starts empty; without the flag the answer is AWS's purge_enabled error. A namespace that still holds tables answers namespace_not_empty. HEAD on a namespace is served, and HEAD on a table or namespace answers 204 as AWS does. Contributed by @Jolley71717. Reported by @Wazabit.
  • S3 Tables — Iceberg REST createTable location and stage-create — a "location": null, which Trino sends for a table without a location, was stored as the table's location, so Trino failed with Cannot parse to a string value: location: null. As on AWS, the location is now always the one S3 Tables assigns, whatever the client sends, and a create without stage-create, or with a null one, answers 400 required_field_is_null "stage-create is a required field and cannot be null". Reported by @Wazabit.
  • S3 Tables — Iceberg REST createTable renumbers field ids — the client's field ids were kept; as on AWS they are now renumbered from 1, each struct's fields before their nested types, with last-column-id, the partition spec's and the sort order's source-ids following the new ids, and a write-order becomes the table's sort order.
  • Secrets Manager — a replicated secret reports its own PrimaryRegion — DescribeSecret and ListSecrets returned PrimaryRegion only for a replica, so a client reading the Region of a secret it replicated got nothing; the primary of a replicated secret now reports its own Region, as AWS does, a replica still reports its source Region, and a secret that is not replicated still has none. Contributed by @chris-plucker.
  • Step Functions — ecs:runTask.sync fails with the task — the state waited for the task to stop and then always succeeded, with the whole DescribeTasks response as its result. As on AWS, a task whose essential container exits non-zero, or that never starts, now fails the state with States.TaskFailed, the stopped task as JSON in PascalCase (epoch-millisecond timestamps) as the cause, so Retry and Catch apply; otherwise the state's result is that task document. Reported by @lucasmfraser.

Don't miss a new ministack release

NewReleases is sending notifications on new releases.