Environment
Description
Following up on #10035 / #10041, thanks for adding StackInstanceNotFoundException, note this issue builds upon that, there is no regression.
StackInstances.update() now raises StackInstanceNotFound as soon as it hits the first requested (account, region) target that doesn't have an existing instance:
def update(self, accounts, regions, parameters):
for account in accounts:
for region in regions:
instance = self.get_instance(account, region)
if instance is None:
raise StackInstanceNotFound()
instance.parameters = parameters or []
StackInstanceNotFoundException is a documented error for this API (UpdateStackInstances reference), so raising it isn't wrong in isolation. The issue is that raising immediately abandons the rest of the batch, so any targets ordered after the missing one in the Accounts/Regions lists never get updated, even though they do have an existing instance and should have been. The result depends entirely on request-list ordering, which isn't how AWS documents this operation behaving.
See StackSet operation options:
If a stack can't update in the first Region, the update operation continues in that Region, and then moves on to the next Region.
And the documented per-instance status codes (SUCCEEDED, FAILED, PENDING, etc.) model each account/Region's outcome independently within a single operation, not as an all-or-nothing, order-dependent result.
Reproduction
import boto3
from moto import mock_aws
@mock_aws
def repro():
c = boto3.client("cloudformation", region_name="eu-west-1")
c.create_stack_set(
StackSetName="demo",
TemplateBody='{"Resources": {}}',
PermissionModel="SELF_MANAGED",
Parameters=[{"ParameterKey": "Foo", "ParameterValue": "original"}],
)
# Only accountA (111...) has an instance; accountC (333...) does not.
c.create_stack_instances(StackSetName="demo", Accounts=["111111111111"], Regions=["eu-west-1"])
try:
# accountC is ordered before accountA.
c.update_stack_instances(
StackSetName="demo",
Accounts=["333333333333", "111111111111"],
Regions=["eu-west-1"],
ParameterOverrides=[{"ParameterKey": "Foo", "ParameterValue": "updated"}],
)
except Exception as e:
print("raised:", type(e).__name__, e)
for i in c.list_stack_instances(StackSetName="demo")["Summaries"]:
print(i["Account"], i["Region"], i.get("ParameterOverrides"))
repro()
Actual behaviour
raised: StackInstanceNotFoundException An error occurred (StackInstanceNotFoundException) when calling the UpdateStackInstances operation: The specified stack instance doesn't exist.
111111111111 eu-west-1 None
accountA's existing instance was never updated (ParameterOverrides stayed None), purely because accountC happened to be listed first.
Expected behaviour
accountA's instance should be updated regardless of the position of accountC in the request, and regardless of whether accountC has an instance yet. StackInstanceNotFoundException seems like the right error for the operation as a whole to surface, but it shouldn't come at the cost of silently dropping updates to valid, existing targets elsewhere in the same request.
Environment
Description
Following up on #10035 / #10041, thanks for adding
StackInstanceNotFoundException, note this issue builds upon that, there is no regression.StackInstances.update()now raisesStackInstanceNotFoundas soon as it hits the first requested(account, region)target that doesn't have an existing instance:StackInstanceNotFoundExceptionis a documented error for this API (UpdateStackInstancesreference), so raising it isn't wrong in isolation. The issue is that raising immediately abandons the rest of the batch, so any targets ordered after the missing one in theAccounts/Regionslists never get updated, even though they do have an existing instance and should have been. The result depends entirely on request-list ordering, which isn't how AWS documents this operation behaving.See StackSet operation options:
And the documented per-instance status codes (
SUCCEEDED,FAILED,PENDING, etc.) model each account/Region's outcome independently within a single operation, not as an all-or-nothing, order-dependent result.Reproduction
Actual behaviour
accountA's existing instance was never updated (ParameterOverridesstayedNone), purely becauseaccountChappened to be listed first.Expected behaviour
accountA's instance should be updated regardless of the position ofaccountCin the request, and regardless of whetheraccountChas an instance yet.StackInstanceNotFoundExceptionseems like the right error for the operation as a whole to surface, but it shouldn't come at the cost of silently dropping updates to valid, existing targets elsewhere in the same request.