@kensio/yulin 1.21.7 → 1.21.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +22 -2
  2. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-properties.js +2 -0
  3. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.js +1 -1
  4. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-input.d.ts +2 -0
  5. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-input.js +4 -0
  6. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-refusals.js +0 -5
  7. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping.handler.d.ts +2 -0
  8. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping.handler.js +2 -0
  9. package/dist/service/lambda/command/event-source-mapping/sim-lambda-event-source-mapping-commands.d.ts +2 -0
  10. package/dist/service/lambda/destination/sim-lambda-destination-targets.d.ts +2 -1
  11. package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-shard-poller.d.ts +0 -4
  12. package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-shard-poller.js +10 -15
  13. package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-event-source-poller.d.ts +1 -6
  14. package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-event-source-poller.js +9 -27
  15. package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-polling.d.ts +19 -0
  16. package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-polling.js +27 -0
  17. package/dist/service/lambda/event-source/poll/sim-lambda-event-source-function.d.ts +1 -1
  18. package/dist/service/lambda/event-source/poll/sim-lambda-event-source-function.js +3 -1
  19. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-age.d.ts +13 -0
  20. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-age.js +27 -0
  21. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-info.d.ts +15 -0
  22. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-info.js +26 -0
  23. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-outcome.d.ts +1 -0
  24. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-outcome.js +7 -1
  25. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-processing.d.ts +17 -0
  26. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-processing.js +12 -0
  27. package/dist/service/lambda/event-source/poll/sim-lambda-stream-cursor.d.ts +15 -0
  28. package/dist/service/lambda/event-source/poll/sim-lambda-stream-cursor.js +28 -0
  29. package/dist/service/lambda/event-source/poll/sim-lambda-stream-expiry.d.ts +19 -0
  30. package/dist/service/lambda/event-source/poll/sim-lambda-stream-expiry.js +23 -0
  31. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-context.d.ts +3 -0
  32. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-context.js +19 -0
  33. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-destination.d.ts +15 -0
  34. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-destination.js +38 -0
  35. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-record.d.ts +41 -0
  36. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-record.js +17 -0
  37. package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress-state.d.ts +21 -0
  38. package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress-state.js +39 -0
  39. package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress.d.ts +7 -37
  40. package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress.js +22 -62
  41. package/dist/service/lambda/event-source/poll/sim-lambda-stream-retry.d.ts +4 -39
  42. package/dist/service/lambda/event-source/poll/sim-lambda-stream-retry.js +12 -64
  43. package/dist/service/lambda/event-source/sim-lambda-event-source-mapping.d.ts +10 -3
  44. package/dist/service/lambda/event-source/sim-lambda-event-source-mapping.js +12 -3
  45. package/dist/service/lambda/event-source/sim-lambda-stream-destination-config.d.ts +7 -0
  46. package/dist/service/lambda/event-source/sim-lambda-stream-destination-config.js +38 -0
  47. package/dist/service/lambda/event-source/stream/kinesis/sim-kinesis-event-source-streams.js +1 -0
  48. package/dist/service/lambda/event-source/stream/sim-dynamodb-event-source-stream-reader.js +1 -0
  49. package/dist/service/lambda/event-source/stream/sim-dynamodb-event-source-stream-shard.d.ts +1 -0
  50. package/dist/service/lambda/event-source/stream/sim-dynamodb-event-source-stream-shard.js +7 -0
  51. package/dist/service/lambda/event-source/stream/sim-lambda-event-source-streams.d.ts +1 -0
  52. package/dist/service/lambda/index.d.ts +1 -0
  53. package/dist/service/lambda/sim-lambda-commands.js +1 -0
  54. package/dist/service/ses/command/authorize/sim-ses-authorizer.d.ts +9 -0
  55. package/dist/service/ses/command/authorize/sim-ses-authorizer.js +18 -2
  56. package/dist/service/ses/command/send/sim-ses-send-email.js +2 -1
  57. package/docs/README.md +7 -0
  58. package/docs/ai-skill/README.md +6 -1
  59. package/docs/sdk/README.md +15 -0
  60. package/docs/services/acm/README.md +24 -40
  61. package/docs/services/apigateway/README.md +52 -71
  62. package/docs/services/apigatewayv2/README.md +55 -74
  63. package/docs/services/athena/README.md +17 -26
  64. package/docs/services/backup/README.md +29 -39
  65. package/docs/services/bedrock/README.md +38 -52
  66. package/docs/services/cloudformation/README.md +43 -55
  67. package/docs/services/cloudfront/README.md +69 -95
  68. package/docs/services/cloudwatch/README.md +40 -54
  69. package/docs/services/cognito/README.md +30 -45
  70. package/docs/services/dynamodb/README.md +34 -51
  71. package/docs/services/ecr/README.md +36 -77
  72. package/docs/services/ecs/README.md +26 -46
  73. package/docs/services/elbv2/README.md +19 -29
  74. package/docs/services/eventbridge/README.md +14 -18
  75. package/docs/services/firehose/README.md +24 -32
  76. package/docs/services/glue/README.md +41 -78
  77. package/docs/services/iam/README.md +13 -15
  78. package/docs/services/kinesis/README.md +53 -93
  79. package/docs/services/kms/README.md +22 -32
  80. package/docs/services/lambda/README.md +112 -85
  81. package/docs/services/logs/README.md +41 -50
  82. package/docs/services/organizations/README.md +50 -85
  83. package/docs/services/personalize/README.md +28 -44
  84. package/docs/services/rekognition/README.md +26 -38
  85. package/docs/services/route53/README.md +17 -17
  86. package/docs/services/s3/README.md +47 -51
  87. package/docs/services/scheduler/README.md +41 -52
  88. package/docs/services/secretsmanager/README.md +27 -42
  89. package/docs/services/ses/README.md +24 -34
  90. package/docs/services/sns/README.md +18 -26
  91. package/docs/services/sqs/README.md +14 -14
  92. package/docs/services/ssm/README.md +13 -17
  93. package/docs/services/stepfunctions/README.md +18 -20
  94. package/docs/services/sts/README.md +32 -45
  95. package/docs/services/wafv2/README.md +12 -17
  96. package/docs/testing/README.md +228 -0
  97. package/docs/time/README.md +10 -0
  98. package/llms.txt +1 -0
  99. package/package.json +1 -1
@@ -1,20 +1,15 @@
1
1
  # Simulated CloudWatch Logs
2
2
 
3
- Yulin includes a simulated Amazon CloudWatch Logs for tests and local development. It holds log
4
- groups, the streams inside them and the events written to those streams. A test can put log events
5
- and read them back with `GetLogEvents`, or search them with `FilterLogEvents`, without an AWS
6
- account.
7
-
8
- The point of it is to make log data addressable. Code that writes to CloudWatch Logs is code teams
9
- already have, and the alternative for a test is capturing process output.
3
+ Yulin simulates CloudWatch Logs in memory. It stores log groups, streams, and events. Application
4
+ code can write events through the normal SDK commands, and tests can read one stream with
5
+ `GetLogEvents` or search a group with `FilterLogEvents`.
10
6
 
11
7
  CloudWatch Logs specific types are imported from the `@kensio/yulin/logs` subpath.
12
8
 
13
9
  ## Writing and searching log events
14
10
 
15
- A log group holds streams, a stream holds events, and `FilterLogEvents` searches across every
16
- stream in a group. A test can therefore name the group and leave the stream out, without knowing
17
- which execution environment wrote the line.
11
+ `FilterLogEvents` searches every stream in a group. A test can find an event without knowing which
12
+ execution environment wrote it.
18
13
 
19
14
  ```typescript sim-logs-write-and-search
20
15
  /**
@@ -86,11 +81,9 @@ set of alternatives, and a quoted phrase matches with its spaces intact.
86
81
 
87
82
  An omitted or empty pattern matches everything.
88
83
 
89
- The structured pattern syntaxes are refused. A JSON property pattern (`{ $.level = "ERROR" }`), a
84
+ Yulin refuses structured pattern syntaxes. A JSON property pattern (`{ $.level = "ERROR" }`), a
90
85
  space delimited field pattern (`[level=ERROR, message]`) and a regular expression term
91
- (`%ERROR|WARN%`) each raise `SimLogsUnsupportedOperationException`. Approximating one would be
92
- worse. A pattern quietly treated as matching everything would turn an assertion about one log line
93
- into an assertion about any log line at all, and the test would keep passing while testing nothing.
86
+ (`%ERROR|WARN%`) each raise `SimLogsUnsupportedOperationException`.
94
87
 
95
88
  ## Reading one stream
96
89
 
@@ -166,10 +159,8 @@ console.log(read);
166
159
 
167
160
  ## Retention
168
161
 
169
- Retention is held as a property to assert on. Events stay where they are, and seeing one go would
170
- mean moving the clock by months. What teams get wrong about retention is the value they deployed,
171
- ahead of the deletion that eventually follows from it. A log group with no retention keeps its
172
- events forever, the AWS default.
162
+ Retention is stored and reported but leaves events in place. A log group without a retention setting
163
+ keeps events forever, matching the AWS default.
173
164
 
174
165
  The accepted values are a fixed set. A reasonable-looking `retentionInDays: 10` is refused here
175
166
  exactly as it is by an account.
@@ -210,9 +201,8 @@ console.log(
210
201
 
211
202
  ## Lambda handler output
212
203
 
213
- A Lambda function's output is recorded into `/aws/lambda/<function name>` as it runs, whether its
214
- code is a zip archive or a real in-process handler. A test can then assert on what a handler logged
215
- by searching its log group.
204
+ A simulated Lambda function writes its output to `/aws/lambda/<function name>`, whether it runs from
205
+ a zip archive or an in-process handler. Search that group to assert on handler output.
216
206
 
217
207
  ```typescript sim-logs-lambda-output
218
208
  /**
@@ -256,11 +246,9 @@ const found = await simAws.logs().filterLogEvents(
256
246
  console.log(found.events?.[0]?.message);
257
247
  ```
258
248
 
259
- The output still reaches the terminal as well. Real Lambda sends it to CloudWatch Logs and nowhere
260
- else, but a test tool that swallowed it would make a failing test harder to debug. Recording is a
261
- tee.
249
+ Output also remains visible in the terminal to help diagnose failing tests.
262
250
 
263
- An invocation that ends in an error nothing caught leaves an `ERROR Invoke Error` line in the group
251
+ An invocation that ends in an uncaught error leaves an `ERROR Invoke Error` line in the group
264
252
  after whatever it printed, carrying the error's type, its message and its stack. The
265
253
  [simulated Lambda docs](https://yulinsim.dev/services/lambda/ "Simulated Lambda usage docs") show
266
254
  one.
@@ -272,8 +260,8 @@ shape in a test, and leave the value alone.
272
260
 
273
261
  Writing on this path is unconditional. A real function needs `logs:CreateLogGroup` and
274
262
  `logs:PutLogEvents` on its execution Role, and one without them produces no logs at all, in silence.
275
- Simulating that would leave nearly every function in a test logging nothing, with no failure to
276
- explain why.
263
+ Yulin always captures this output so missing log permissions do not hide diagnostic messages in
264
+ tests.
277
265
 
278
266
  ## Declaring a log group in a template
279
267
 
@@ -308,12 +296,11 @@ Two divergences to know about:
308
296
 
309
297
  ## Delivering logs from another service
310
298
 
311
- CloudWatch Logs delivery carries the logs of another service somewhere. CloudFront standard logging
312
- v2 is the clearest case. A distribution has no logging property of its own, and turning logging on
313
- means three CloudWatch Logs resources.
299
+ CloudWatch Logs delivery connects a service's log source to a destination. CloudFront standard
300
+ logging v2 uses a delivery source, a delivery destination, and a delivery.
314
301
 
315
- A delivery source names what is being logged and which of its logs. A delivery destination names
316
- where they land and in what form. A delivery joins one to the other.
302
+ The source identifies the resource and log type. The destination identifies where logs would go and
303
+ their output format. The delivery joins them.
317
304
 
318
305
  ```typescript sim-logs-delivery
319
306
  /**
@@ -418,7 +405,7 @@ rules from real AWS are modelled here, each of them a deploy that looks fine unt
418
405
  - **A delivery holds both its ends.** Deleting the source or the destination while a delivery joins
419
406
  them fails with `ConflictException`. The delivery goes first, and CloudFormation orders that
420
407
  itself when a stack is deleted.
421
- - **CloudFront delivers `ACCESS_LOGS` and nothing else.** Any other `logType` over a distribution is
408
+ - **CloudFront supports only `ACCESS_LOGS`.** Any other `logType` over a distribution is
422
409
  refused.
423
410
  - **CloudFront delivery is set up from `us-east-1`**, whatever region the destination bucket is in.
424
411
  A CloudFront delivery source put from anywhere else is refused.
@@ -441,10 +428,9 @@ bare variables and let delivery name them.
441
428
 
442
429
  ### Declaring delivery in a template
443
430
 
444
- The same three resources in a template, which is the whole of what a CDK construct for CloudFront
445
- logging synthesises, alongside the distribution they are for. The source's `ResourceArn` is built
446
- around a `Ref` to that distribution, the way CDK builds it. A pinned distribution id fails the
447
- deploy (see [the rules above](#delivering-logs-from-another-service)).
431
+ A CloudFormation template can declare the same three resources alongside the CloudFront distribution.
432
+ The source's `ResourceArn` can use `Ref` to include the generated distribution ID. A hard-coded ID
433
+ for a missing distribution fails deployment (see [the rules above](#delivering-logs-from-another-service)).
448
434
 
449
435
  ```yaml
450
436
  SiteDistribution:
@@ -501,9 +487,9 @@ The template carries the S3 layout as two flat properties, and the API takes the
501
487
  delivery. CloudWatch Logs issues that ID. A template cannot predict it.
502
488
 
503
489
  `Fn::GetAtt` gives `Arn` on all three. The source also publishes `Service` and `ResourceArns`, and
504
- the delivery publishes `DeliveryId` and `DeliveryDestinationType`. The destination publishes nothing
505
- else, and `DeliveryDestinationType` is a property of it rather than an attribute, so read that one
506
- off the delivery. Anything outside this set is refused here, as CloudFormation refuses it.
490
+ the delivery publishes `DeliveryId` and `DeliveryDestinationType`. The destination publishes only
491
+ `Arn`. Read `DeliveryDestinationType` from the delivery rather than the destination. CloudFormation
492
+ refuses attributes outside this set.
507
493
 
508
494
  `Tags` and `DeliveryDestinationPolicy` are recorded as ignored properties, and the stack still
509
495
  deploys.
@@ -732,7 +718,8 @@ function is. `UpdateAlias` moves what the filter reaches, and the filter stays a
732
718
 
733
719
  ## Metric filters
734
720
 
735
- A metric filter turns matching log events into CloudWatch metric datapoints. It is how a log line becomes something an alarm can watch, with no handler publishing a metric of its own.
721
+ A metric filter converts matching log events into CloudWatch metric datapoints. An alarm can then
722
+ watch the metric without application code calling `PutMetricData`.
736
723
 
737
724
  ```typescript sim-logs-metric-filter
738
725
  /**
@@ -916,7 +903,9 @@ console.log(MetricAlarms?.[0]?.StateValue);
916
903
 
917
904
  ## Embedded Metric Format
918
905
 
919
- A log event that is itself an Embedded Metric Format document publishes the metrics it declares. This is how AWS Lambda Powertools counts anything. Its `Metrics` writes an EMF document to the handler's stdout and calls no CloudWatch API at all, and a CDK `NodejsFunction` bundling Powertools does the same in a deployed account.
906
+ A log event containing an Embedded Metric Format (EMF) document publishes its declared metrics.
907
+ AWS Lambda Powertools uses this path. It writes EMF to standard output instead of calling the
908
+ CloudWatch API.
920
909
 
921
910
  ```typescript sim-logs-embedded-metric-format
922
911
  /**
@@ -997,7 +986,9 @@ A log event is read as a document only where it parses as JSON, comes out as an
997
986
 
998
987
  Three things go on `simAws.logs().metricPublicationFailures` rather than passing quietly. A metric the metadata declares and the document body carries no number under, a metric asking for `StorageResolution: 1`, which simulated CloudWatch has no period short enough for, and a directive that asked for dimensions and got no usable set of them.
999
988
 
1000
- A dimension set is taken whole or dropped whole. One naming a key the body lacks, and one carrying anything but strings, are both dropped, because publishing part of a set would put the datapoint under a narrower identity than the document declared. A directive left with no usable set publishes nothing at all. Falling back to no dimensions would land the datapoint on the undimensioned metric, and that is one an alarm may well be watching.
989
+ A dimension set is accepted or dropped as a whole. Yulin drops a set when the body lacks one of its
990
+ keys or a value has a type other than string. Publishing only part of the set would create the wrong
991
+ metric identity. A directive without a usable set produces no datapoint.
1001
992
 
1002
993
  Each entry on the ledger names its `source`, which is `{ kind: "metricFilter", filterName }` or `{ kind: "embeddedMetricFormat" }`. The two are told apart by kind rather than by name, because a metric filter may be called anything.
1003
994
 
@@ -1132,7 +1123,7 @@ console.log(described.logGroups?.[0]?.logGroupArn);
1132
1123
  - **Events never expire.** Retention is stored and reported, never acted on.
1133
1124
  - **A metric filter reading a field of the log event.** A `metricValue` or a dimension value
1134
1125
  beginning `$` is refused where the filter is put. Both need a structured filter pattern, and
1135
- neither structured syntax is simulated.
1126
+ both require a structured syntax, which Yulin refuses.
1136
1127
  - **How far back a `defaultValue` looks.** A filter remembers the most recent minute it matched
1137
1128
  something in. A later write into that same minute publishes no default over the top, and a write
1138
1129
  landing in an earlier minute a match was already seen in does publish one. Events
@@ -1143,21 +1134,21 @@ console.log(described.logGroups?.[0]?.logGroupArn);
1143
1134
  read, which is how it reaches CloudWatch in an account.
1144
1135
  - **`AWS::Logs::SubscriptionFilter`.** Recorded as a gap. The log group, the metric filter and the
1145
1136
  three delivery resource types are what simulated CloudFormation deploys here.
1146
- - **`ApplyOnTransformedLogs` and `EmitSystemFieldDimensions` on a metric filter.** Recorded and
1147
- acted on by nothing. Log transformers are absent, so there is no transformed event to read.
1148
- - **Nothing is actually delivered.** A delivery records that a source was joined to a destination
1137
+ - **`ApplyOnTransformedLogs` and `EmitSystemFieldDimensions` on a metric filter.** Recorded as
1138
+ ignored properties. Log transformers are absent.
1139
+ - **Delivery resources store configuration only.** A delivery records that a source was joined to a destination
1149
1140
  and how the records would be written. No access log file ever reaches the bucket.
1150
1141
  - **`GetDeliverySource`, `GetDeliveryDestination` and `GetDelivery`.** Absent as SDK operations. The
1151
1142
  three `Describe` operations report the same resources. The three action names are authorized where
1152
1143
  CloudFormation reads a delivery Resource back.
1153
1144
  - **Delivery resource tags and cross-account delivery.** `PutDeliverySource`,
1154
1145
  `PutDeliveryDestination` and `CreateDelivery` refuse tags outright. `DeliveryDestinationPolicy` in
1155
- a template is recorded and acted on by nothing.
1146
+ a template is recorded as an ignored property.
1156
1147
  - **An `=` in a suffix path with Hive compatible paths off.** Taken. Whether real CloudWatch Logs
1157
1148
  takes one is unverified. A path hand-rolling its own partition keys without the option is left
1158
1149
  alone here, and refused with the option on.
1159
- - **Log types for services other than CloudFront.** Any `logType` is taken over a resource that is
1160
- not a distribution, because the valid set varies by service and this simulation does not carry it.
1150
+ - **Log types for services other than CloudFront.** Yulin accepts any `logType` for resources other
1151
+ than distributions because the valid set varies by service.
1161
1152
  - **Logs Insights, export tasks, tags, encryption and data protection policies.** Absent. Tags and
1162
1153
  `kmsKeyId` on `CreateLogGroup` are refused outright. A property cannot look set here and behave
1163
1154
  differently in an account.
@@ -1,18 +1,14 @@
1
1
  # Simulated Organizations
2
2
 
3
- Yulin simulates AWS Organizations service control policies. A test can find out that the
4
- organization around an account forbids something before a deployment does.
3
+ Yulin simulates AWS Organizations service control policies (SCPs) and organization structure.
5
4
 
6
- A service control policy filters what an account's principals may do and grants nothing. Policies
7
- attach to the organization root, to an organizational unit, or to one account, and an account
8
- inherits every policy on the path down to it. Sim IAM evaluates them ahead of that account's
9
- identity and resource policies. An SCP therefore applies to a CloudFormation deployment, an
10
- intercepted SDK client, and a direct service call alike.
5
+ An SCP limits permissions granted by identity and resource policies. Attach one to the organization
6
+ root, an organizational unit or an account. An account inherits policies from every node on its path
7
+ from the root. Simulated IAM applies them to direct calls, intercepted clients and CloudFormation.
11
8
 
12
9
  ## Attach a policy to an organizational unit
13
10
 
14
- A policy is usually attached to an organizational unit rather than to one account, and every account
15
- under that unit inherits it. Units nest, and the root sits above all of them.
11
+ Create an organizational unit and attach an SCP to it. Accounts below the unit inherit the policy.
16
12
 
17
13
  ```typescript sim-organizations-organizational-unit
18
14
  /**
@@ -44,16 +40,13 @@ const decision = simAws.account("123456789012").iam().authorize({
44
40
  console.log(decision.value); // "ExplicitDeny"
45
41
  ```
46
42
 
47
- The policy hangs two levels above the account and still reaches it. `createOrganizationalUnit` takes
48
- a parent unit as its second argument, and leaves the unit under the root without one.
49
- `organizations.root()` is the node above everything, and a policy attached there covers every
50
- account in the organization.
43
+ Pass a parent as the second argument to `createOrganizationalUnit` to nest units. With no parent, the
44
+ new unit sits below `organizations.root()`.
51
45
 
52
46
  ## Every level has to allow the action
53
47
 
54
- An account is filtered by each node on the path from the root down to it, and each one has to allow
55
- an action on its own. A root allowing S3 and a unit allowing DynamoDB leave an account beneath them
56
- able to do neither.
48
+ Every node on the path must allow the action. An allow at one level cannot supply an allow missing
49
+ at another. A deny at any level rejects the request.
57
50
 
58
51
  ```typescript sim-organizations-every-level-allows
59
52
  /**
@@ -89,15 +82,12 @@ console.log(decision.value); // "ImplicitDeny"
89
82
  console.log(decision.serviceControlPolicy.unallowedLevels); // [ "Workloads" ]
90
83
  ```
91
84
 
92
- `unallowedLevels` names the nodes that allowed nothing matching. That is the part of a real SCP
93
- denial that takes longest to track down.
94
-
95
- A `Deny` at any level ends the request whatever another level allows.
85
+ `unallowedLevels` identifies the nodes with no matching allow.
96
86
 
97
87
  ## The management account
98
88
 
99
- `setManagementAccount` names the account AWS exempts from every service control policy. That account
100
- is decided by its identity and resource policies alone, whatever is attached above it.
89
+ Use `setManagementAccount` to exempt one account from SCP evaluation. Its identity and resource
90
+ policies still apply.
101
91
 
102
92
  ```typescript sim-organizations-management-account
103
93
  /**
@@ -126,9 +116,8 @@ console.log(decision.serviceControlPolicy.isApplied); // false
126
116
 
127
117
  ## Attach a service control policy to an Account
128
118
 
129
- A policy attached straight to an account applies to that account alone. An organization spans
130
- accounts, so it belongs to the whole simulated environment and is reached as
131
- `simAws.organizations()`, not from an account scope.
119
+ An SCP attached directly to an account applies only to that account. Organization state belongs to
120
+ the whole `SimAws` instance and is available through `simAws.organizations()`.
132
121
 
133
122
  ```typescript sim-organizations-attach-scp
134
123
  /**
@@ -159,17 +148,15 @@ console.log(decision.serviceControlPolicy.isDenied); // true
159
148
  console.log(decision.serviceControlPolicy.denyStatements[0]?.Sid); // "DenyBucketCreation"
160
149
  ```
161
150
 
162
- The account also gets AWS's own `FullAWSAccess` policy, as it would in a real organization. One
163
- `Deny` statement therefore denies that one action and leaves the rest of the account working.
151
+ New organization nodes receive the AWS-managed `FullAWSAccess` policy. A deny-list SCP can then
152
+ block one action while leaving other actions allowed.
164
153
 
165
- An account with no policy attached to it stays outside the organization's reach, and its identity
166
- and resource policies decide its requests as they did before.
154
+ An account outside the organization is unaffected by SCPs.
167
155
 
168
156
  ## Catch a deployment the policy denies
169
157
 
170
- Sim CloudFormation creates each resource through the owning service's command handler, and that
171
- handler authorizes. A deployment that names no principal is decided as the account root. An SCP
172
- applies to a member account's root the same way AWS does.
158
+ CloudFormation creates resources through each service's authorized command path. A deployment with
159
+ no caller runs as the account root, and SCPs apply to that root.
173
160
 
174
161
  ```typescript sim-organizations-scp-deployment
175
162
  /**
@@ -213,14 +200,13 @@ const failed = simAws
213
200
  console.log(failed?.status); // "CREATE_FAILED"
214
201
  ```
215
202
 
216
- The resource is left `CREATE_FAILED` and the deployment rejects. A test asserting that a stack
217
- deploys then fails on the policy, with the policy named in the message.
203
+ When an SCP denies creation, the resource enters `CREATE_FAILED` and the deployment rejects. The
204
+ error names the policy.
218
205
 
219
206
  ## Name the principal a deployment runs as
220
207
 
221
- An organization that denies its accounts' root principals is ordinary, and a deployment decided as
222
- the root fails under one. `caller` says which principal the resources are created as, and a
223
- statement conditioned on `aws:PrincipalArn` then has a deploy role to match against.
208
+ Pass `caller` when the deployment should run as a role. SCP conditions on `aws:PrincipalArn` then
209
+ evaluate against that role.
224
210
 
225
211
  ```typescript sim-organizations-scp-deploy-role
226
212
  /**
@@ -285,17 +271,15 @@ const stack = await simAws.cloudFormation().deployTemplate({
285
271
  console.log(stack.getResource("ReportsBucket")?.status); // "CREATE_COMPLETE"
286
272
  ```
287
273
 
288
- The Role is created before the policy is attached. Creating it afterwards is a call the policy
289
- denies the root, and the account root is who a bare `createRole` runs as.
274
+ Create the role before attaching a policy that would deny the setup call.
290
275
 
291
276
  ## Name the caller the rest of a test reads as
292
277
 
293
- A deployment names its own principal. Every other call in the test still names none, and each one is
294
- the account root. Under a policy denying that root, a test reading back what a stack made is denied
295
- on every read.
278
+ A deployment caller applies only to that deployment. Other calls still use the account root unless
279
+ the simulation has a default caller.
296
280
 
297
- `defaultCaller` on `SimAws` says who those calls are. A test then reads the account as the person or
298
- role that would really be looking at it, and an explicit `caller` still wins wherever one is given.
281
+ Set `defaultCaller` on `SimAws` to choose the principal for unattributed calls. An explicit `caller`
282
+ still takes precedence.
299
283
 
300
284
  ```typescript sim-organizations-scp-default-caller
301
285
  /**
@@ -369,17 +353,13 @@ console.log(read.Parameter?.Value); // "reports-bucket"
369
353
  console.log(identity.Arn); // "arn:aws:iam::123456789012:role/Administrator"
370
354
  ```
371
355
 
372
- Setup runs before the policy is attached, for the same reason the deploy Role above does. The Role a
373
- simulation reads as has to exist and hold a policy, and creating it is itself a call.
374
-
375
- Naming a default caller says who an unattributed call comes from, and leaves the root's own identity
376
- access where it was. A test about root behaviour names `simAws.account().rootPrincipal` and gets the
377
- root. Under the policy above that call is denied, which is what the statement is written to do.
356
+ Create and configure the default caller before attaching policies that would deny those setup calls.
357
+ Pass `simAws.account().rootPrincipal` explicitly when a test needs to make a request as root.
378
358
 
379
359
  ## Write an allow list instead of a deny list
380
360
 
381
- `detachFullAwsAccess` takes AWS's own policy off an account. What remains has to allow an action
382
- for the account to be allowed it. That is an organization run as an allow list.
361
+ Call `detachFullAwsAccess` to use an allow-list SCP. The remaining policies must explicitly allow an
362
+ action.
383
363
 
384
364
  ```typescript sim-organizations-scp-allow-list
385
365
  /**
@@ -407,16 +387,13 @@ console.log(decision.denialReason);
407
387
  // "because no service control policy allows the s3:GetObject action"
408
388
  ```
409
389
 
410
- An account root holds unrestricted access in sim IAM, and this denies it anyway. That is what an
411
- SCP does in AWS, and it is why an allow list is worth writing in a test at all.
412
-
413
- Detaching `FullAWSAccess` on its own leaves the account holding no policy, and every action is then
414
- denied. AWS behaves the same way, and warns about it.
390
+ SCPs still limit an account root. Removing `FullAWSAccess` without adding another allow policy denies
391
+ every action.
415
392
 
416
393
  ## Deploy an organization from CloudFormation
417
394
 
418
- A template's `AWS::Organizations::*` resources build the organization they describe, so a stack
419
- already managing the org chart is the same one a test deploys.
395
+ Simulated CloudFormation supports the organization, organizational unit, account and policy
396
+ resource types.
420
397
 
421
398
  ```typescript sim-organizations-cloudformation
422
399
  /**
@@ -470,17 +447,13 @@ const decision = simAws.account("123456789012").iam().authorize({
470
447
  console.log(decision.value); // "ExplicitDeny"
471
448
  ```
472
449
 
473
- `Content` takes the policy document inline or as JSON text. `TargetIds` takes a list or a single
474
- value, and each entry names the root, a unit, or an Account. A policy reaches every target it names
475
- or none of them. A target this organization has never heard of fails the resource before anything is
476
- attached.
450
+ `Content` accepts an object or JSON text. `TargetIds` accepts one target or a list of roots, units and
451
+ accounts. An unknown target fails the resource before the policy is attached anywhere.
477
452
 
478
- A simulated environment has one organization from the start, so
479
- `AWS::Organizations::Organization` records the one already there rather than making another. It is
480
- worth declaring for `RootId`, which is what a unit hangs off.
453
+ A `SimAws` instance starts with one organization. `AWS::Organizations::Organization` adopts it and
454
+ exposes its `RootId`.
481
455
 
482
- `AWS::Organizations::Account` creates an Account with an id nobody chose, as AWS does. Read that id
483
- back with `Ref` or `Fn::GetAtt AccountId`.
456
+ `AWS::Organizations::Account` generates an account ID. Read it with `Ref` or `Fn::GetAtt AccountId`.
484
457
 
485
458
  These are the properties read from each resource:
486
459
 
@@ -491,10 +464,8 @@ These are the properties read from each resource:
491
464
  | `AWS::Organizations::Account` | `AccountName`, `Email`, `ParentIds` | `Tags`, `RoleName` |
492
465
  | `AWS::Organizations::Policy` | `Name`, `Type`, `Content`, `TargetIds` | `Tags`, `Description` |
493
466
 
494
- Tearing the stack down takes its own policies off the nodes they were attached to, removes the
495
- units, and takes any Account the stack created back out of the organization. A node holding
496
- policies from more than one stack keeps the others. A unit that still holds something when it goes
497
- hands what it holds to its parent, so every Account keeps a path to the root.
467
+ Stack teardown removes resources created by that stack. Policies from other stacks remain attached.
468
+ Accounts and child units move to the deleted unit's parent.
498
469
 
499
470
  ## Reading a denial
500
471
 
@@ -510,21 +481,16 @@ sides, through `decision.serviceControlPolicy`:
510
481
  | `denyStatements` | The matching `Deny` statements. |
511
482
  | `allowStatements` | The matching `Allow` statements. |
512
483
 
513
- `decision.denialReason` carries the wording AWS puts on the `AccessDenied` message, and every
514
- simulated service passes it through to the error it throws.
484
+ `decision.denialReason` contains the text used by service access-denied errors.
515
485
 
516
- `simAws.organizations().serviceControlPoliciesFor(accountId)` returns the policies in force for an
517
- account, in the order they were evaluated, including `FullAWSAccess` where it is still attached.
486
+ `serviceControlPoliciesFor(accountId)` returns the policies in evaluation order.
518
487
 
519
- `serviceControlPolicySetFor(accountId).levels` keeps the policies grouped by the node they hang on,
520
- root first. That grouping is what sim IAM evaluates.
488
+ `serviceControlPolicySetFor(accountId).levels` groups policies by node, starting at the root.
521
489
 
522
- The flattened list is empty in three cases that behave differently. An account that was never named
523
- sits outside the organization and stays unrestricted. The management account is exempt and equally
524
- unrestricted. An account left holding no policy is denied everything. `applies` separates the last
525
- of those from the other two.
526
- `serviceControlPolicySetFor(accountId).applies` tells the two apart, and so does
527
- `decision.serviceControlPolicy.isApplied`.
490
+ An empty policy list can mean that the account is outside the organization, is the management
491
+ account or has no policies. The first two are exempt. The last is denied every action. Check
492
+ `serviceControlPolicySetFor(accountId).applies` or `decision.serviceControlPolicy.isApplied` to
493
+ distinguish them.
528
494
 
529
495
  ## Available functionality
530
496
 
@@ -567,7 +533,6 @@ Simulated Organizations supports:
567
533
  | Service-linked roles | Evaluated like any other principal. AWS exempts a service-linked role from SCPs. |
568
534
  | Other policy types | Resource control policies, declarative policies, tag policies, backup policies and AI services opt-out policies are not simulated. |
569
535
  | The Organizations SDK | `CreatePolicy`, `AttachPolicy`, `ListAccounts` and the rest of the API are not handled. Policies are attached through the accessor. |
570
- | CloudFormation | `AWS::Organizations::Organization`, `::OrganizationalUnit`, `::Account` and `::Policy` are not created from a template. |
571
536
  | Organization condition keys | `aws:PrincipalOrgID` and `aws:PrincipalOrgPaths` are not populated. A condition naming either fails to match. |
572
537
  | Service principals | A request whose caller is a service principal or anonymous belongs to no Account and is subject to no policy. |
573
538
  | Condition operator coverage | The operators above are evaluated. Anything else fails closed and the statement holding it matches nothing. |