Skip to main content

fakecloud_ec2/
state.rs

1//! EC2 service state.
2//!
3//! Partitioned per account+region via [`fakecloud_core::multi_account`]. The
4//! `tags` map is keyed by EC2 resource id (e.g. `vpc-…`, `i-…`, `sg-…`) and is
5//! the backing store for `CreateTags`/`DeleteTags`/`DescribeTags` plus the
6//! `tag:`/`tag-key` describe filters shared across every resource family.
7
8use std::collections::BTreeMap;
9use std::sync::Arc;
10
11use parking_lot::RwLock;
12use serde::{Deserialize, Serialize};
13
14/// Shared, account-partitioned EC2 state handle.
15pub type SharedEc2State = Arc<RwLock<fakecloud_core::multi_account::MultiAccountState<Ec2State>>>;
16
17/// On-disk snapshot envelope for EC2 state. Versioned so format changes fail
18/// loudly on upgrade rather than silently mis-parsing. Backing containers are
19/// not serialized -- on restore the server reconciles them via
20/// `Ec2Service::recover_persisted_containers`.
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct Ec2Snapshot {
23    pub schema_version: u32,
24    #[serde(default)]
25    pub accounts: Option<fakecloud_core::multi_account::MultiAccountState<Ec2State>>,
26}
27
28pub const EC2_SNAPSHOT_SCHEMA_VERSION: u32 = 1;
29
30impl fakecloud_core::multi_account::AccountState for Ec2State {
31    fn new_for_account(account_id: &str, region: &str, _endpoint: &str) -> Self {
32        Self::new(account_id, region)
33    }
34}
35
36/// Recycle Bin retention rules in effect for an account, per resource type.
37/// Populated by AWS's `rbin` API (not modeled as a separate service here); when
38/// a flag is set, the corresponding `Delete*` op sends the resource to the
39/// recycle bin instead of hard-deleting it.
40#[derive(Clone, Debug, Default, Serialize, Deserialize)]
41pub struct RecycleBinRetention {
42    #[serde(default)]
43    pub volumes: bool,
44    #[serde(default)]
45    pub snapshots: bool,
46    #[serde(default)]
47    pub images: bool,
48}
49
50/// A single EC2 resource tag.
51#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
52pub struct Tag {
53    pub key: String,
54    pub value: String,
55}
56
57/// A secondary CIDR-block association on a VPC.
58#[derive(Clone, Debug, Serialize, Deserialize)]
59pub struct VpcCidrAssoc {
60    pub association_id: String,
61    pub cidr_block: String,
62    /// `associated` | `disassociated`.
63    pub state: String,
64}
65
66/// A Virtual Private Cloud.
67#[derive(Clone, Debug, Serialize, Deserialize)]
68pub struct Vpc {
69    pub vpc_id: String,
70    pub cidr_block: String,
71    /// `pending` | `available`.
72    pub state: String,
73    pub dhcp_options_id: String,
74    /// `default` | `dedicated` | `host`.
75    pub instance_tenancy: String,
76    pub is_default: bool,
77    pub enable_dns_support: bool,
78    pub enable_dns_hostnames: bool,
79    #[serde(default)]
80    pub cidr_associations: Vec<VpcCidrAssoc>,
81    /// Amazon-provided IPv6 /56 CIDR, set when the VPC was created (or updated)
82    /// with `AmazonProvidedIpv6CidrBlock=true`. Reported in the
83    /// `Ipv6CidrBlockAssociationSet`; the `aws_vpc` resource reads
84    /// `ipv6_cidr_block` / `assign_generated_ipv6_cidr_block` from it.
85    #[serde(default)]
86    pub ipv6_cidr_block: Option<String>,
87}
88
89/// One `key -> values` entry in a DHCP options set.
90#[derive(Clone, Debug, Serialize, Deserialize)]
91pub struct DhcpConfig {
92    pub key: String,
93    pub values: Vec<String>,
94}
95
96/// A DHCP options set.
97#[derive(Clone, Debug, Serialize, Deserialize)]
98pub struct DhcpOptions {
99    pub dhcp_options_id: String,
100    pub configurations: Vec<DhcpConfig>,
101}
102
103/// A subnet within a VPC.
104#[derive(Clone, Debug, Serialize, Deserialize)]
105pub struct Subnet {
106    pub subnet_id: String,
107    pub vpc_id: String,
108    pub cidr_block: String,
109    pub availability_zone: String,
110    pub availability_zone_id: String,
111    /// `pending` | `available`.
112    pub state: String,
113    pub available_ip_address_count: i32,
114    pub default_for_az: bool,
115    pub map_public_ip_on_launch: bool,
116    pub assign_ipv6_address_on_creation: bool,
117    pub map_customer_owned_ip_on_launch: bool,
118    pub enable_dns64: bool,
119    /// `ip-name` | `resource-name`.
120    pub private_dns_hostname_type: String,
121    /// IPv6 /64 associated with the subnet (via CreateSubnet `Ipv6CidrBlock` or
122    /// AssociateSubnetCidrBlock). Reported in the `ipv6CidrBlockAssociationSet`;
123    /// the `aws_subnet` resource waits for this association.
124    #[serde(default)]
125    pub ipv6_cidr_block: Option<String>,
126}
127
128/// A CIDR reservation within a subnet.
129#[derive(Clone, Debug, Serialize, Deserialize)]
130pub struct SubnetCidrReservation {
131    pub subnet_cidr_reservation_id: String,
132    pub subnet_id: String,
133    pub cidr: String,
134    /// `prefix` | `explicit`.
135    pub reservation_type: String,
136    pub description: String,
137}
138
139/// A security-group rule (ingress or egress), stored flat.
140#[derive(Clone, Debug, Serialize, Deserialize)]
141pub struct SecurityGroupRule {
142    pub rule_id: String,
143    pub group_id: String,
144    pub is_egress: bool,
145    pub ip_protocol: String,
146    pub from_port: i64,
147    pub to_port: i64,
148    pub cidr_ipv4: Option<String>,
149    pub cidr_ipv6: Option<String>,
150    pub prefix_list_id: Option<String>,
151    pub referenced_group_id: Option<String>,
152    /// Source-group reference by name (EC2-Classic / default-VPC form) when the
153    /// rule references a group without an id.
154    #[serde(default)]
155    pub referenced_group_name: Option<String>,
156    /// Owning account of a cross-account source-group reference.
157    #[serde(default)]
158    pub referenced_user_id: Option<String>,
159    pub description: String,
160}
161
162/// A security group.
163#[derive(Clone, Debug, Serialize, Deserialize)]
164pub struct SecurityGroup {
165    pub group_id: String,
166    pub group_name: String,
167    pub description: String,
168    pub vpc_id: String,
169    #[serde(default)]
170    pub rules: Vec<SecurityGroupRule>,
171}
172
173/// A route within a route table.
174#[derive(Clone, Debug, Default, Serialize, Deserialize)]
175pub struct Route {
176    pub destination_cidr_block: Option<String>,
177    pub destination_ipv6_cidr_block: Option<String>,
178    pub destination_prefix_list_id: Option<String>,
179    pub gateway_id: Option<String>,
180    pub nat_gateway_id: Option<String>,
181    pub network_interface_id: Option<String>,
182    pub instance_id: Option<String>,
183    pub vpc_peering_connection_id: Option<String>,
184    pub transit_gateway_id: Option<String>,
185    pub egress_only_internet_gateway_id: Option<String>,
186    /// `active` | `blackhole`.
187    pub state: String,
188    /// `CreateRouteTable` | `CreateRoute`.
189    pub origin: String,
190}
191
192/// A route-table association (to a subnet or gateway, or the VPC main table).
193#[derive(Clone, Debug, Serialize, Deserialize)]
194pub struct RouteTableAssociation {
195    pub association_id: String,
196    pub route_table_id: String,
197    pub subnet_id: Option<String>,
198    pub gateway_id: Option<String>,
199    pub main: bool,
200}
201
202/// A route table.
203#[derive(Clone, Debug, Serialize, Deserialize)]
204pub struct RouteTable {
205    pub route_table_id: String,
206    pub vpc_id: String,
207    #[serde(default)]
208    pub routes: Vec<Route>,
209    #[serde(default)]
210    pub associations: Vec<RouteTableAssociation>,
211}
212
213/// An internet gateway (or egress-only IGW) with its VPC attachments.
214#[derive(Clone, Debug, Serialize, Deserialize)]
215pub struct InternetGateway {
216    pub internet_gateway_id: String,
217    /// (vpc_id, state) pairs.
218    #[serde(default)]
219    pub attachments: Vec<(String, String)>,
220}
221
222/// A NAT gateway.
223#[derive(Clone, Debug, Serialize, Deserialize)]
224pub struct NatGateway {
225    pub nat_gateway_id: String,
226    pub subnet_id: String,
227    pub vpc_id: String,
228    /// `pending` | `available` | `deleting` | `deleted`.
229    pub state: String,
230    /// `public` | `private`.
231    pub connectivity_type: String,
232    pub allocation_id: Option<String>,
233}
234
235/// An Elastic IP allocation.
236#[derive(Clone, Debug, Serialize, Deserialize)]
237pub struct ElasticIp {
238    pub allocation_id: String,
239    pub public_ip: String,
240    /// `vpc` | `standard`.
241    pub domain: String,
242    pub association_id: Option<String>,
243    pub instance_id: Option<String>,
244    pub network_interface_id: Option<String>,
245    pub private_ip_address: Option<String>,
246    #[serde(default = "default_eip_public_ipv4_pool")]
247    pub public_ipv4_pool: String,
248    #[serde(default = "default_eip_network_border_group")]
249    pub network_border_group: String,
250    /// Reverse-DNS (PTR) record configured via ModifyAddressAttribute.
251    #[serde(default)]
252    pub domain_name: Option<String>,
253}
254
255fn default_eip_public_ipv4_pool() -> String {
256    "amazon".to_string()
257}
258
259fn default_eip_network_border_group() -> String {
260    "us-east-1".to_string()
261}
262
263/// An EC2 key pair (public-key metadata only).
264#[derive(Clone, Debug, Serialize, Deserialize)]
265pub struct KeyPair {
266    pub key_pair_id: String,
267    pub key_name: String,
268    /// `rsa` | `ed25519`.
269    pub key_type: String,
270    pub key_fingerprint: String,
271}
272
273/// A placement group.
274#[derive(Clone, Debug, Serialize, Deserialize)]
275pub struct PlacementGroup {
276    pub group_id: String,
277    pub group_name: String,
278    /// `cluster` | `spread` | `partition`.
279    pub strategy: String,
280    /// `available`.
281    pub state: String,
282    pub partition_count: Option<i64>,
283    pub spread_level: Option<String>,
284}
285
286/// An ENI attachment.
287#[derive(Clone, Debug, Serialize, Deserialize)]
288pub struct EniAttachment {
289    pub attachment_id: String,
290    pub instance_id: String,
291    pub device_index: i64,
292    /// `attaching` | `attached` | `detaching` | `detached`.
293    pub status: String,
294}
295
296/// An elastic network interface.
297#[derive(Clone, Debug, Serialize, Deserialize)]
298pub struct NetworkInterface {
299    pub network_interface_id: String,
300    pub subnet_id: String,
301    pub vpc_id: String,
302    pub availability_zone: String,
303    pub description: String,
304    pub mac_address: String,
305    pub private_ip_address: String,
306    /// `available` | `in-use`.
307    pub status: String,
308    pub interface_type: String,
309    pub source_dest_check: bool,
310    #[serde(default)]
311    pub group_ids: Vec<String>,
312    #[serde(default)]
313    pub private_ips: Vec<String>,
314    #[serde(default)]
315    pub ipv6_addresses: Vec<String>,
316    pub attachment: Option<EniAttachment>,
317    /// `publicIpDnsNameOptions.publicHostnameType` set by
318    /// `ModifyPublicIpDnsNameOptions`. AWS exposes no Describe that returns this
319    /// setting, so it is persisted (and the ENI's existence enforced) but not
320    /// reflected back through a Describe.
321    #[serde(default)]
322    pub public_ip_dns_hostname_type: Option<String>,
323}
324
325/// A network-interface permission grant.
326#[derive(Clone, Debug, Serialize, Deserialize)]
327pub struct NetworkInterfacePermission {
328    pub permission_id: String,
329    pub network_interface_id: String,
330    pub aws_account_id: String,
331    /// `INSTANCE-ATTACH` | `EIP-ASSOCIATE`.
332    pub permission: String,
333}
334
335/// An EC2 instance (metadata-faithful; a Docker-backed runtime layers on top).
336#[derive(Clone, Debug, Serialize, Deserialize)]
337pub struct Instance {
338    pub instance_id: String,
339    pub image_id: String,
340    pub instance_type: String,
341    /// EC2 state code: 0 pending, 16 running, 32 shutting-down, 48 terminated,
342    /// 64 stopping, 80 stopped.
343    pub state_code: i64,
344    pub state_name: String,
345    pub private_ip: String,
346    pub public_ip: Option<String>,
347    pub subnet_id: Option<String>,
348    pub vpc_id: Option<String>,
349    pub key_name: Option<String>,
350    #[serde(default)]
351    pub security_group_ids: Vec<String>,
352    pub reservation_id: String,
353    pub ami_launch_index: i64,
354    pub monitoring: bool,
355    pub az: String,
356    pub launch_time: String,
357    /// Id of the backing container/Pod, when this instance is backed by a
358    /// real container runtime. `None` in metadata-only mode.
359    #[serde(default)]
360    pub container_id: Option<String>,
361    // ---- modifiable instance attributes (ModifyInstanceAttribute et al.) ----
362    /// `disableApiTermination` — when true, TerminateInstances is rejected.
363    #[serde(default)]
364    pub disable_api_termination: bool,
365    /// `disableApiStop` — when true, StopInstances is rejected.
366    #[serde(default)]
367    pub disable_api_stop: bool,
368    /// `sourceDestCheck` — defaults to true on AWS.
369    #[serde(default = "default_true")]
370    pub source_dest_check: bool,
371    /// `ebsOptimized`.
372    #[serde(default)]
373    pub ebs_optimized: bool,
374    /// `instanceInitiatedShutdownBehavior` — `stop` (default) | `terminate`.
375    #[serde(default = "default_shutdown_behavior")]
376    pub instance_initiated_shutdown_behavior: String,
377    /// `userData` — base64-encoded, as supplied at launch / via Modify.
378    #[serde(default)]
379    pub user_data: Option<String>,
380    // ---- Modify*Options round-trip state ----
381    /// `metadataOptions` (`ModifyInstanceMetadataOptions`).
382    #[serde(default)]
383    pub metadata_options: MetadataOptions,
384    /// `cpuOptions` (`ModifyInstanceCpuOptions`).
385    #[serde(default)]
386    pub cpu_options: Option<CpuOptions>,
387    /// `bandwidthWeighting` (`ModifyInstanceNetworkPerformanceOptions`).
388    #[serde(default)]
389    pub bandwidth_weighting: Option<String>,
390    /// `maintenanceOptions` (`ModifyInstanceMaintenanceOptions`).
391    #[serde(default)]
392    pub maintenance_options: MaintenanceOptions,
393    /// `placement` tenancy/affinity overrides (`ModifyInstancePlacement`).
394    #[serde(default)]
395    pub placement_tenancy: Option<String>,
396    #[serde(default)]
397    pub placement_affinity: Option<String>,
398    #[serde(default)]
399    pub placement_group_name: Option<String>,
400    // ---- ModifyPrivateDnsNameOptions round-trip state ----
401    /// `privateDnsNameOptions.hostnameType` — `ip-name` | `resource-name`.
402    /// `None` reports the AWS default `ip-name`.
403    #[serde(default)]
404    pub private_dns_hostname_type: Option<String>,
405    /// `privateDnsNameOptions.enableResourceNameDnsARecord`.
406    #[serde(default)]
407    pub enable_resource_name_dns_a_record: bool,
408    /// `privateDnsNameOptions.enableResourceNameDnsAAAARecord`.
409    #[serde(default)]
410    pub enable_resource_name_dns_aaaa_record: bool,
411}
412
413fn default_true() -> bool {
414    true
415}
416
417fn default_shutdown_behavior() -> String {
418    "stop".to_string()
419}
420
421/// IMDS (instance-metadata service) options, round-tripped by
422/// `ModifyInstanceMetadataOptions` and reflected in DescribeInstances.
423#[derive(Clone, Debug, Serialize, Deserialize)]
424pub struct MetadataOptions {
425    /// `optional` | `required`.
426    pub http_tokens: String,
427    /// `disabled` | `enabled`.
428    pub http_endpoint: String,
429    pub http_put_response_hop_limit: i64,
430    /// `disabled` | `enabled`.
431    pub http_protocol_ipv6: String,
432    /// `disabled` | `enabled`.
433    pub instance_metadata_tags: String,
434}
435
436/// Account/region-level IMDS defaults (ModifyInstanceMetadataDefaults). Each
437/// field is `None` until explicitly set; a set of `no-preference` clears it
438/// back to `None` (AWS drops the default rather than storing the sentinel).
439#[derive(Clone, Debug, Default, Serialize, Deserialize)]
440pub struct InstanceMetadataDefaults {
441    #[serde(default)]
442    pub http_tokens: Option<String>,
443    #[serde(default)]
444    pub http_endpoint: Option<String>,
445    #[serde(default)]
446    pub http_put_response_hop_limit: Option<i64>,
447    #[serde(default)]
448    pub instance_metadata_tags: Option<String>,
449    #[serde(default)]
450    pub http_tokens_enforced: Option<String>,
451}
452
453impl Default for MetadataOptions {
454    fn default() -> Self {
455        Self {
456            http_tokens: "optional".to_string(),
457            http_endpoint: "enabled".to_string(),
458            http_put_response_hop_limit: 1,
459            http_protocol_ipv6: "disabled".to_string(),
460            instance_metadata_tags: "disabled".to_string(),
461        }
462    }
463}
464
465/// CPU options round-tripped by `ModifyInstanceCpuOptions`.
466#[derive(Clone, Debug, Serialize, Deserialize)]
467pub struct CpuOptions {
468    pub core_count: i64,
469    pub threads_per_core: i64,
470}
471
472/// Maintenance options round-tripped by `ModifyInstanceMaintenanceOptions`.
473#[derive(Clone, Debug, Serialize, Deserialize)]
474pub struct MaintenanceOptions {
475    /// `disabled` | `default`.
476    pub auto_recovery: String,
477    /// `disabled` | `default`.
478    pub reboot_migration: String,
479}
480
481impl Default for MaintenanceOptions {
482    fn default() -> Self {
483        Self {
484            auto_recovery: "default".to_string(),
485            reboot_migration: "default".to_string(),
486        }
487    }
488}
489
490/// An EBS volume attachment.
491#[derive(Clone, Debug, Serialize, Deserialize)]
492pub struct VolumeAttachment {
493    pub volume_id: String,
494    pub instance_id: String,
495    pub device: String,
496    /// `attaching` | `attached` | `detaching` | `detached`.
497    pub status: String,
498    pub delete_on_termination: bool,
499}
500
501/// A recorded `ModifyVolume` request, surfaced by `DescribeVolumesModifications`.
502#[derive(Clone, Debug, Serialize, Deserialize)]
503pub struct VolumeModification {
504    pub original_size: i64,
505    pub original_iops: Option<i64>,
506    pub original_throughput: Option<i64>,
507    pub original_volume_type: String,
508    pub target_size: i64,
509    pub target_iops: Option<i64>,
510    pub target_throughput: Option<i64>,
511    pub target_volume_type: String,
512    /// `modifying` | `optimizing` | `completed` | `failed`.
513    pub state: String,
514    pub start_time: String,
515}
516
517/// An EBS volume.
518#[derive(Clone, Debug, Serialize, Deserialize)]
519pub struct Volume {
520    pub volume_id: String,
521    pub size: i64,
522    pub snapshot_id: Option<String>,
523    pub availability_zone: String,
524    /// `creating` | `available` | `in-use` | `deleting` | `deleted`.
525    pub state: String,
526    pub volume_type: String,
527    pub iops: Option<i64>,
528    pub throughput: Option<i64>,
529    pub encrypted: bool,
530    pub kms_key_id: Option<String>,
531    pub multi_attach_enabled: bool,
532    pub auto_enable_io: bool,
533    #[serde(default)]
534    pub attachments: Vec<VolumeAttachment>,
535    #[serde(default)]
536    pub in_recycle_bin: bool,
537    /// Last `ModifyVolume` request applied to this volume, if any.
538    #[serde(default)]
539    pub modification: Option<VolumeModification>,
540}
541
542/// An EBS snapshot.
543#[derive(Clone, Debug, Serialize, Deserialize)]
544pub struct Snapshot {
545    pub snapshot_id: String,
546    pub volume_id: String,
547    /// `pending` | `completed` | `error`.
548    pub state: String,
549    pub volume_size: i64,
550    pub description: String,
551    pub encrypted: bool,
552    /// `standard` | `archive`.
553    pub storage_tier: String,
554    #[serde(default)]
555    pub in_recycle_bin: bool,
556    #[serde(default)]
557    pub locked: bool,
558    pub lock_mode: Option<String>,
559    /// createVolumePermission grantees: account IDs, or the literal `all` for
560    /// the public group. Managed by ModifySnapshotAttribute.
561    #[serde(default)]
562    pub create_volume_permissions: Vec<String>,
563}
564
565/// An AMI (machine image).
566#[derive(Clone, Debug, Serialize, Deserialize)]
567pub struct Image {
568    pub image_id: String,
569    pub name: String,
570    pub description: String,
571    /// `pending` | `available` | `disabled` | `deregistered`.
572    pub state: String,
573    pub architecture: String,
574    pub public: bool,
575    pub source_instance_id: Option<String>,
576    #[serde(default)]
577    pub in_recycle_bin: bool,
578    pub deprecation_time: Option<String>,
579    #[serde(default)]
580    pub deregistration_protection: bool,
581    /// `launchPermission` — AWS account ids the AMI is explicitly shared with
582    /// (cross-account share via `ModifyImageAttribute`).
583    #[serde(default)]
584    pub launch_permission_users: Vec<String>,
585    /// `launchPermission` groups — only `all` is valid in AWS (public share).
586    #[serde(default)]
587    pub launch_permission_groups: Vec<String>,
588    /// `bootMode` — `legacy-bios` | `uefi` | `uefi-preferred`. `None` reports
589    /// the default `uefi`; settable via `ModifyImageAttribute`.
590    #[serde(default)]
591    pub boot_mode: Option<String>,
592    /// `imageOwnerId` — the AWS account that owns the AMI. `None` reports the
593    /// requesting account (a user-registered AMI is owned by its creator); the
594    /// seeded public AMIs set this to the real Amazon/Canonical/etc. owner so
595    /// `aws_ami` data sources filtering by `owners`/`owner-id` resolve them.
596    #[serde(default)]
597    pub owner_id: Option<String>,
598    /// `imageOwnerAlias` — `amazon` | `aws-marketplace` | `self` etc. Set on the
599    /// seeded public AMIs so `owner-alias` filters and `owners = ["amazon"]`
600    /// resolve them; `None` for user-registered AMIs.
601    #[serde(default)]
602    pub owner_alias: Option<String>,
603    /// `creationDate`. `None` reports the fixed fallback; the seeded catalogue
604    /// sets distinct dates so Terraform's `most_recent = true` ordering is
605    /// deterministic.
606    #[serde(default)]
607    pub creation_date: Option<String>,
608    /// `rootDeviceName` (e.g. `/dev/xvda` for Linux, `/dev/sda1` for Windows).
609    /// `None` reports the Linux default.
610    #[serde(default)]
611    pub root_device_name: Option<String>,
612    /// `platformDetails` / Windows `platform`. `None` = Linux/UNIX.
613    #[serde(default)]
614    pub platform: Option<String>,
615}
616
617/// A network ACL entry (rule).
618#[derive(Clone, Debug, Serialize, Deserialize)]
619pub struct NetworkAclEntry {
620    pub rule_number: i64,
621    pub protocol: String,
622    /// `allow` | `deny`.
623    pub rule_action: String,
624    pub egress: bool,
625    pub cidr_block: Option<String>,
626    pub ipv6_cidr_block: Option<String>,
627    /// TCP/UDP port range (from, to).
628    pub port_range: Option<(i64, i64)>,
629    /// ICMP (type, code).
630    pub icmp_type_code: Option<(i64, i64)>,
631}
632
633/// A network ACL <-> subnet association.
634#[derive(Clone, Debug, Serialize, Deserialize)]
635pub struct NetworkAclAssoc {
636    pub association_id: String,
637    pub subnet_id: String,
638}
639
640/// A network ACL.
641#[derive(Clone, Debug, Serialize, Deserialize)]
642pub struct NetworkAcl {
643    pub network_acl_id: String,
644    pub vpc_id: String,
645    pub is_default: bool,
646    #[serde(default)]
647    pub entries: Vec<NetworkAclEntry>,
648    #[serde(default)]
649    pub associations: Vec<NetworkAclAssoc>,
650}
651
652/// A VPC peering connection.
653#[derive(Clone, Debug, Serialize, Deserialize)]
654pub struct VpcPeering {
655    pub id: String,
656    pub requester_vpc_id: String,
657    pub accepter_vpc_id: String,
658    /// `pending-acceptance` | `active` | `rejected` | `deleted`.
659    pub status: String,
660    /// Requester-side DNS-resolution-from-remote-VPC option.
661    #[serde(default)]
662    pub requester_allow_dns: bool,
663    /// Accepter-side DNS-resolution-from-remote-VPC option.
664    #[serde(default)]
665    pub accepter_allow_dns: bool,
666}
667
668/// A VPC endpoint.
669#[derive(Clone, Debug, Serialize, Deserialize)]
670pub struct VpcEndpoint {
671    pub id: String,
672    /// `Interface` | `Gateway` | `GatewayLoadBalancer` | ...
673    pub endpoint_type: String,
674    pub vpc_id: String,
675    pub service_name: String,
676    pub state: String,
677    #[serde(default)]
678    pub subnet_ids: Vec<String>,
679    #[serde(default)]
680    pub route_table_ids: Vec<String>,
681    #[serde(default)]
682    pub private_dns_enabled: bool,
683    /// Security groups attached to an Interface endpoint's ENIs.
684    #[serde(default)]
685    pub security_group_ids: Vec<String>,
686    /// Who pays for the endpoint: `vpc-endpoint-account` (default) or
687    /// `vpc-endpoint-service-account`. Set by ModifyVpcEndpointPayerResponsibility.
688    #[serde(default)]
689    pub payer_responsibility: String,
690}
691
692/// A VPC endpoint service configuration (PrivateLink provider side).
693#[derive(Clone, Debug, Serialize, Deserialize)]
694pub struct EndpointService {
695    pub service_id: String,
696    pub service_name: String,
697    pub state: String,
698    pub acceptance_required: bool,
699    pub payer_responsibility: String,
700    #[serde(default)]
701    pub nlb_arns: Vec<String>,
702    #[serde(default)]
703    pub gwlb_arns: Vec<String>,
704    #[serde(default)]
705    pub supported_ip_address_types: Vec<String>,
706    #[serde(default)]
707    pub private_dns_name: Option<String>,
708    /// Principal ARNs granted access via ModifyVpcEndpointServicePermissions.
709    #[serde(default)]
710    pub allowed_principals: Vec<String>,
711}
712
713/// A VPC endpoint connection notification.
714#[derive(Clone, Debug, Serialize, Deserialize)]
715pub struct ConnectionNotification {
716    pub id: String,
717    pub arn: String,
718    pub service_id: Option<String>,
719    #[serde(default)]
720    pub events: Vec<String>,
721}
722
723/// A VPC flow log.
724#[derive(Clone, Debug, Serialize, Deserialize)]
725pub struct FlowLog {
726    pub id: String,
727    pub resource_id: String,
728    pub traffic_type: String,
729    pub log_destination_type: String,
730    pub log_group_name: Option<String>,
731    /// Destination ARN for `s3` / `kinesis-data-firehose` deliveries.
732    pub log_destination: Option<String>,
733    /// IAM role ARN used to deliver logs to CloudWatch Logs
734    /// (`iam_role_arn` on the Terraform resource).
735    #[serde(default)]
736    pub deliver_logs_permission_arn: Option<String>,
737    /// Max log aggregation interval in seconds. AWS accepts 60 or 600 and
738    /// defaults to 600 when unspecified.
739    #[serde(default = "default_max_aggregation_interval")]
740    pub max_aggregation_interval: i64,
741}
742
743fn default_max_aggregation_interval() -> i64 {
744    600
745}
746
747/// A launch template (versions tracked as monotonic counters).
748#[derive(Clone, Debug, Serialize, Deserialize)]
749pub struct LaunchTemplate {
750    pub id: String,
751    pub name: String,
752    pub default_version: i64,
753    pub latest_version: i64,
754}
755
756/// A Spot instance request.
757#[derive(Clone, Debug, Serialize, Deserialize)]
758pub struct SpotRequest {
759    pub id: String,
760    /// `open` | `active` | `cancelled` | `closed`.
761    pub state: String,
762    pub request_type: String,
763    pub spot_price: String,
764}
765
766/// A Spot fleet request.
767#[derive(Clone, Debug, Serialize, Deserialize)]
768pub struct SpotFleet {
769    pub id: String,
770    pub state: String,
771    /// `SpotFleetRequestConfig.TargetCapacity`.
772    #[serde(default)]
773    pub target_capacity: i64,
774}
775
776/// An EC2 fleet.
777#[derive(Clone, Debug, Serialize, Deserialize)]
778pub struct Fleet {
779    pub id: String,
780    pub state: String,
781    pub fleet_type: String,
782    /// `TargetCapacitySpecification.TotalTargetCapacity`.
783    #[serde(default)]
784    pub target_capacity: i64,
785}
786
787/// An on-demand capacity reservation.
788#[derive(Clone, Debug, Serialize, Deserialize)]
789pub struct CapacityReservation {
790    pub id: String,
791    pub instance_type: String,
792    pub instance_platform: String,
793    pub availability_zone: String,
794    pub tenancy: String,
795    pub total_instance_count: i64,
796    pub available_instance_count: i64,
797    /// `active` | `expired` | `cancelled` | `pending` | `failed`.
798    pub state: String,
799    pub end_date_type: String,
800    pub instance_match_criteria: String,
801}
802
803/// A Reserved Instance purchase.
804#[derive(Clone, Debug, Serialize, Deserialize)]
805pub struct ReservedInstances {
806    pub id: String,
807    pub instance_type: String,
808    pub availability_zone: String,
809    pub instance_count: i64,
810    pub product_description: String,
811    pub state: String,
812    pub duration: i64,
813    pub fixed_price: String,
814    pub usage_price: String,
815}
816
817/// A Reserved Instances listing in the Reserved Instance Marketplace.
818#[derive(Clone, Debug, Serialize, Deserialize)]
819pub struct ReservedInstancesListing {
820    pub listing_id: String,
821    pub reserved_instances_id: String,
822    pub instance_count: i64,
823    pub client_token: String,
824    /// `active` | `cancelled` | `closed`.
825    pub status: String,
826    pub status_message: String,
827}
828
829/// A Reserved Instances modification request.
830#[derive(Clone, Debug, Serialize, Deserialize)]
831pub struct ReservedInstancesModification {
832    pub modification_id: String,
833    pub reserved_instances_ids: Vec<String>,
834    /// `processing` | `fulfilled` | `failed`.
835    pub status: String,
836    pub client_token: String,
837}
838
839/// A Dedicated Host.
840#[derive(Clone, Debug, Serialize, Deserialize)]
841pub struct DedicatedHost {
842    pub id: String,
843    pub auto_placement: String,
844    pub availability_zone: String,
845    pub instance_type: String,
846    pub state: String,
847    pub host_recovery: String,
848    pub host_maintenance: String,
849}
850
851/// A Transit Gateway.
852#[derive(Clone, Debug, Serialize, Deserialize)]
853pub struct TransitGateway {
854    pub id: String,
855    pub description: String,
856    /// `pending` | `available` | `modifying` | `deleting` | `deleted`.
857    #[serde(default = "tgw_default_state")]
858    pub state: String,
859}
860
861fn tgw_default_state() -> String {
862    "available".to_string()
863}
864
865/// A Transit Gateway attachment (VPC and others).
866#[derive(Clone, Debug, Serialize, Deserialize)]
867pub struct TgwAttachment {
868    pub id: String,
869    pub tgw_id: String,
870    pub resource_id: String,
871    pub resource_type: String,
872    #[serde(default)]
873    pub subnet_ids: Vec<String>,
874    pub state: String,
875}
876
877/// A Transit Gateway route table.
878#[derive(Clone, Debug, Serialize, Deserialize)]
879pub struct TgwRouteTable {
880    pub id: String,
881    pub tgw_id: String,
882}
883
884/// A static Transit Gateway route within a route table.
885#[derive(Clone, Debug, Serialize, Deserialize)]
886pub struct TgwRoute {
887    pub cidr: String,
888    pub attachment_id: String,
889    pub state: String,
890}
891
892/// A Transit Gateway multicast domain.
893#[derive(Clone, Debug, Serialize, Deserialize)]
894pub struct TgwMulticastDomain {
895    pub id: String,
896    pub tgw_id: String,
897}
898
899/// A Transit Gateway metering policy.
900#[derive(Clone, Debug, Serialize, Deserialize)]
901pub struct TgwMeteringPolicy {
902    pub id: String,
903    pub tgw_id: String,
904}
905
906/// A customer gateway (on-prem side of a VPN).
907#[derive(Clone, Debug, Serialize, Deserialize)]
908pub struct CustomerGateway {
909    pub id: String,
910    pub state: String,
911    pub ip_address: String,
912    pub bgp_asn: String,
913}
914
915/// A virtual private gateway.
916#[derive(Clone, Debug, Serialize, Deserialize)]
917pub struct VpnGateway {
918    pub id: String,
919    pub state: String,
920    #[serde(default)]
921    pub attachments: Vec<String>,
922}
923
924/// A Site-to-Site VPN connection.
925#[derive(Clone, Debug, Serialize, Deserialize)]
926pub struct VpnConnection {
927    pub id: String,
928    pub state: String,
929    pub customer_gateway_id: String,
930    pub vpn_gateway_id: Option<String>,
931    #[serde(default)]
932    pub transit_gateway_id: Option<String>,
933    #[serde(default)]
934    pub routes: Vec<String>,
935}
936
937/// A VPN concentrator.
938#[derive(Clone, Debug, Serialize, Deserialize)]
939pub struct VpnConcentrator {
940    pub id: String,
941    pub state: String,
942}
943
944/// An IPAM (IP Address Manager).
945#[derive(Clone, Debug, Serialize, Deserialize)]
946pub struct Ipam {
947    pub id: String,
948    pub public_scope_id: String,
949    pub private_scope_id: String,
950    pub tier: String,
951    #[serde(default)]
952    pub description: String,
953}
954
955/// An IPAM scope.
956#[derive(Clone, Debug, Serialize, Deserialize)]
957pub struct IpamScope {
958    pub id: String,
959    pub ipam_id: String,
960    /// "public" or "private".
961    #[serde(default)]
962    pub scope_type: String,
963    #[serde(default)]
964    pub description: String,
965}
966
967/// An IPAM pool.
968#[derive(Clone, Debug, Serialize, Deserialize)]
969pub struct IpamPool {
970    pub id: String,
971    pub scope_id: String,
972    pub address_family: String,
973    #[serde(default)]
974    pub description: String,
975}
976
977/// An IPAM resource discovery.
978#[derive(Clone, Debug, Serialize, Deserialize)]
979pub struct IpamResourceDiscovery {
980    pub id: String,
981    #[serde(default)]
982    pub description: String,
983}
984
985/// An IPAM policy.
986#[derive(Clone, Debug, Serialize, Deserialize)]
987pub struct IpamPolicy {
988    pub id: String,
989    pub ipam_id: String,
990}
991
992/// An IPAM prefix-list resolver.
993#[derive(Clone, Debug, Serialize, Deserialize)]
994pub struct IpamPrefixListResolver {
995    pub id: String,
996    pub ipam_id: String,
997    pub address_family: String,
998    #[serde(default)]
999    pub description: String,
1000}
1001
1002/// An IPAM prefix-list resolver target.
1003#[derive(Clone, Debug, Serialize, Deserialize)]
1004pub struct IpamPrefixListResolverTarget {
1005    pub id: String,
1006    pub resolver_id: String,
1007    pub prefix_list_id: String,
1008    pub prefix_list_region: String,
1009    #[serde(default)]
1010    pub track_latest_version: bool,
1011}
1012
1013/// A Verified Access instance.
1014#[derive(Clone, Debug, Serialize, Deserialize)]
1015pub struct VerifiedAccessInstance {
1016    pub id: String,
1017    pub description: String,
1018    #[serde(default)]
1019    pub trust_providers: Vec<String>,
1020}
1021
1022/// A Verified Access trust provider.
1023#[derive(Clone, Debug, Serialize, Deserialize)]
1024pub struct VerifiedAccessTrustProvider {
1025    pub id: String,
1026    pub trust_provider_type: String,
1027    pub policy_reference_name: String,
1028    pub description: String,
1029}
1030
1031/// A Verified Access group.
1032#[derive(Clone, Debug, Serialize, Deserialize)]
1033pub struct VerifiedAccessGroup {
1034    pub id: String,
1035    pub instance_id: String,
1036    pub description: String,
1037}
1038
1039/// A Verified Access endpoint.
1040#[derive(Clone, Debug, Serialize, Deserialize)]
1041pub struct VerifiedAccessEndpoint {
1042    pub id: String,
1043    pub group_id: String,
1044    pub instance_id: String,
1045    pub endpoint_type: String,
1046    pub attachment_type: String,
1047    #[serde(default)]
1048    pub description: String,
1049}
1050
1051/// Access-logging configuration for a Verified Access instance, set by
1052/// ModifyVerifiedAccessInstanceLoggingConfiguration.
1053#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1054pub struct VerifiedAccessLoggingConfig {
1055    #[serde(default)]
1056    pub log_version: Option<String>,
1057    #[serde(default)]
1058    pub include_trust_context: bool,
1059    #[serde(default)]
1060    pub cloudwatch_logs_enabled: bool,
1061    #[serde(default)]
1062    pub cloudwatch_logs_group: Option<String>,
1063    #[serde(default)]
1064    pub kinesis_firehose_enabled: bool,
1065    #[serde(default)]
1066    pub kinesis_firehose_stream: Option<String>,
1067    #[serde(default)]
1068    pub s3_enabled: bool,
1069    #[serde(default)]
1070    pub s3_bucket_name: Option<String>,
1071    #[serde(default)]
1072    pub s3_prefix: Option<String>,
1073    #[serde(default)]
1074    pub s3_bucket_owner: Option<String>,
1075}
1076
1077/// A Network Insights reachability path.
1078#[derive(Clone, Debug, Serialize, Deserialize)]
1079pub struct NetworkInsightsPath {
1080    pub id: String,
1081    pub source: String,
1082    pub destination: String,
1083    pub protocol: String,
1084}
1085
1086/// A Network Insights path analysis.
1087#[derive(Clone, Debug, Serialize, Deserialize)]
1088pub struct NetworkInsightsAnalysis {
1089    pub id: String,
1090    pub path_id: String,
1091}
1092
1093/// A Network Insights access scope.
1094#[derive(Clone, Debug, Serialize, Deserialize)]
1095pub struct NetworkInsightsAccessScope {
1096    pub id: String,
1097}
1098
1099/// A Network Insights access-scope analysis.
1100#[derive(Clone, Debug, Serialize, Deserialize)]
1101pub struct NetworkInsightsAccessScopeAnalysis {
1102    pub id: String,
1103    pub scope_id: String,
1104}
1105
1106/// A carrier gateway (Wavelength).
1107#[derive(Clone, Debug, Serialize, Deserialize)]
1108pub struct CarrierGateway {
1109    pub id: String,
1110    pub vpc_id: String,
1111}
1112
1113/// An EC2 Instance Connect endpoint.
1114#[derive(Clone, Debug, Serialize, Deserialize)]
1115pub struct InstanceConnectEndpoint {
1116    pub id: String,
1117    pub subnet_id: String,
1118}
1119
1120/// A customer-owned IP (CoIP) pool.
1121#[derive(Clone, Debug, Serialize, Deserialize)]
1122pub struct CoipPool {
1123    pub id: String,
1124    pub route_table_id: String,
1125}
1126
1127/// A local-gateway route table.
1128#[derive(Clone, Debug, Serialize, Deserialize)]
1129pub struct LocalGatewayRouteTable {
1130    pub id: String,
1131    pub local_gateway_id: String,
1132    pub mode: String,
1133}
1134
1135/// A local-gateway route-table <-> VPC association.
1136#[derive(Clone, Debug, Serialize, Deserialize)]
1137pub struct LocalGatewayRouteTableVpcAssoc {
1138    pub id: String,
1139    pub route_table_id: String,
1140    pub vpc_id: String,
1141}
1142
1143/// A local-gateway virtual interface.
1144#[derive(Clone, Debug, Serialize, Deserialize)]
1145pub struct LocalGatewayVif {
1146    pub id: String,
1147    pub group_id: String,
1148    pub vlan: String,
1149    pub local_address: String,
1150    pub peer_address: String,
1151}
1152
1153/// A local-gateway virtual-interface group.
1154#[derive(Clone, Debug, Serialize, Deserialize)]
1155pub struct LocalGatewayVifGroup {
1156    pub id: String,
1157    pub local_gateway_id: String,
1158}
1159
1160/// A local-gateway route-table <-> virtual-interface-group association.
1161#[derive(Clone, Debug, Serialize, Deserialize)]
1162pub struct LocalGatewayRouteTableVifgAssoc {
1163    pub id: String,
1164    pub route_table_id: String,
1165    pub vif_group_id: String,
1166}
1167
1168/// A Client VPN endpoint.
1169#[derive(Clone, Debug, Serialize, Deserialize)]
1170pub struct ClientVpnEndpoint {
1171    pub id: String,
1172    pub description: String,
1173    pub status: String,
1174    pub server_cert_arn: String,
1175    pub transport_protocol: String,
1176    pub client_cidr: String,
1177    #[serde(default)]
1178    pub routes: Vec<String>,
1179    /// (association id, subnet id) for each associated target network.
1180    #[serde(default)]
1181    pub target_networks: Vec<(String, String)>,
1182    /// Ingress authorization rule target CIDRs.
1183    #[serde(default)]
1184    pub auth_rules: Vec<String>,
1185}
1186
1187/// A Transit Gateway peering attachment.
1188#[derive(Clone, Debug, Serialize, Deserialize)]
1189pub struct TgwPeering {
1190    pub id: String,
1191    pub tgw_id: String,
1192    pub peer_tgw_id: String,
1193    pub peer_account: String,
1194    pub peer_region: String,
1195    pub state: String,
1196}
1197
1198/// One entry in a customer-managed prefix list.
1199#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1200pub struct PrefixListEntry {
1201    pub cidr: String,
1202    #[serde(default)]
1203    pub description: Option<String>,
1204}
1205
1206/// A customer-managed prefix list (`CreateManagedPrefixList`). Versions are a
1207/// monotonic counter; each entry-mutating Modify bumps `version` and snapshots
1208/// the prior entries into `version_history` so `RestoreManagedPrefixListVersion`
1209/// and `GetManagedPrefixListEntries(TargetVersion)` round-trip.
1210#[derive(Clone, Debug, Serialize, Deserialize)]
1211pub struct ManagedPrefixList {
1212    pub prefix_list_id: String,
1213    pub prefix_list_name: String,
1214    pub address_family: String,
1215    pub max_entries: i64,
1216    pub version: i64,
1217    /// `create-complete` | `modify-complete`.
1218    pub state: String,
1219    #[serde(default)]
1220    pub entries: Vec<PrefixListEntry>,
1221    /// version -> entries snapshot at that version.
1222    #[serde(default)]
1223    pub version_history: BTreeMap<i64, Vec<PrefixListEntry>>,
1224}
1225
1226/// A weekly time range within an instance event window.
1227#[derive(Clone, Debug, Serialize, Deserialize)]
1228pub struct EventWindowTimeRange {
1229    pub start_week_day: String,
1230    pub start_hour: i64,
1231    pub end_week_day: String,
1232    pub end_hour: i64,
1233}
1234
1235/// An instance event window (`CreateInstanceEventWindow`).
1236#[derive(Clone, Debug, Serialize, Deserialize)]
1237pub struct InstanceEventWindow {
1238    pub id: String,
1239    #[serde(default)]
1240    pub name: Option<String>,
1241    #[serde(default)]
1242    pub cron_expression: Option<String>,
1243    #[serde(default)]
1244    pub time_ranges: Vec<EventWindowTimeRange>,
1245    /// `creating` | `active` | `deleting` | `deleted`.
1246    pub state: String,
1247    /// Association target — instance ids, dedicated-host ids, or tags
1248    /// (`AssociateInstanceEventWindow` / `DisassociateInstanceEventWindow`).
1249    #[serde(default)]
1250    pub assoc_instance_ids: Vec<String>,
1251    #[serde(default)]
1252    pub assoc_dedicated_host_ids: Vec<String>,
1253    #[serde(default)]
1254    pub assoc_tags: Vec<Tag>,
1255}
1256
1257/// A traffic-mirror target (`CreateTrafficMirrorTarget`).
1258#[derive(Clone, Debug, Serialize, Deserialize)]
1259pub struct TrafficMirrorTarget {
1260    pub id: String,
1261    pub network_interface_id: Option<String>,
1262    pub network_load_balancer_arn: Option<String>,
1263    pub gateway_lb_endpoint_id: Option<String>,
1264    /// `network-interface` | `network-load-balancer` | `gateway-load-balancer-endpoint`.
1265    pub target_type: String,
1266    pub description: Option<String>,
1267}
1268
1269/// A traffic-mirror filter (`CreateTrafficMirrorFilter`).
1270#[derive(Clone, Debug, Serialize, Deserialize)]
1271pub struct TrafficMirrorFilter {
1272    pub id: String,
1273    pub description: Option<String>,
1274    #[serde(default)]
1275    pub network_services: Vec<String>,
1276}
1277
1278/// A traffic-mirror filter rule (`CreateTrafficMirrorFilterRule`).
1279#[derive(Clone, Debug, Serialize, Deserialize)]
1280pub struct TrafficMirrorFilterRule {
1281    pub id: String,
1282    pub filter_id: String,
1283    pub traffic_direction: String,
1284    pub rule_number: i64,
1285    pub rule_action: String,
1286    pub protocol: Option<i64>,
1287    pub destination_cidr_block: Option<String>,
1288    pub source_cidr_block: Option<String>,
1289    /// (from, to) port ranges.
1290    pub destination_port_range: Option<(i64, i64)>,
1291    pub source_port_range: Option<(i64, i64)>,
1292    pub description: Option<String>,
1293}
1294
1295/// A traffic-mirror session (`CreateTrafficMirrorSession`).
1296#[derive(Clone, Debug, Serialize, Deserialize)]
1297pub struct TrafficMirrorSession {
1298    pub id: String,
1299    pub target_id: String,
1300    pub filter_id: String,
1301    pub network_interface_id: String,
1302    pub packet_length: Option<i64>,
1303    pub session_number: i64,
1304    pub virtual_network_id: Option<i64>,
1305    pub description: Option<String>,
1306}
1307
1308/// A route server (`CreateRouteServer`).
1309#[derive(Clone, Debug, Serialize, Deserialize)]
1310pub struct RouteServer {
1311    pub id: String,
1312    pub amazon_side_asn: i64,
1313    /// `available` (lowercase per the AWS state enum is uppercase; we store the
1314    /// wire value).
1315    pub state: String,
1316    /// `ENABLED` | `DISABLED` | `RESETTING` | ...
1317    pub persist_routes_state: String,
1318    pub persist_routes_duration: Option<i64>,
1319    pub sns_notifications_enabled: bool,
1320}
1321
1322/// A VPC encryption control (`CreateVpcEncryptionControl`).
1323#[derive(Clone, Debug, Serialize, Deserialize)]
1324pub struct VpcEncryptionControl {
1325    pub id: String,
1326    pub vpc_id: String,
1327    /// `monitor` | `enforce`.
1328    pub mode: String,
1329    /// `available` | `monitor_in_progress` | `enforce_in_progress` | ...
1330    pub state: String,
1331    /// Per-resource-type exclusion state: resource -> `enabled` | `disabled`.
1332    #[serde(default)]
1333    pub exclusions: BTreeMap<String, String>,
1334}
1335
1336/// Account-level VPC encryption control (`ModifyAccountVpcEncryptionControl` /
1337/// `DescribeAccountVpcEncryptionControl`). A single account-scoped singleton,
1338/// distinct from the per-VPC `VpcEncryptionControl` above.
1339#[derive(Clone, Debug, Serialize, Deserialize)]
1340pub struct AccountVpcEncryptionControl {
1341    /// `unmanaged` | `attempt-monitor` | `attempt-enforce`.
1342    pub mode: String,
1343    /// `default-state` | `transitions-in-progress` | `transitions-successful` | ...
1344    pub state: String,
1345    /// Per-resource-type exclusion state, xml-member-name -> `enabled` |
1346    /// `disabled` | `enabling` | `disabling`.
1347    #[serde(default)]
1348    pub exclusions: BTreeMap<String, String>,
1349}
1350
1351impl Default for AccountVpcEncryptionControl {
1352    fn default() -> Self {
1353        Self {
1354            mode: "unmanaged".to_string(),
1355            state: "default-state".to_string(),
1356            exclusions: BTreeMap::new(),
1357        }
1358    }
1359}
1360
1361/// A VPC block-public-access exclusion (`CreateVpcBlockPublicAccessExclusion`).
1362#[derive(Clone, Debug, Serialize, Deserialize)]
1363pub struct VpcBpaExclusion {
1364    pub id: String,
1365    /// `allow-bidirectional` | `allow-egress`.
1366    pub internet_gateway_exclusion_mode: String,
1367    pub resource_arn: Option<String>,
1368    /// `create-complete` | `update-complete` | `delete-complete`.
1369    pub state: String,
1370}
1371
1372/// An Amazon FPGA image (`CreateFpgaImage` / `CopyFpgaImage`).
1373#[derive(Clone, Debug, Serialize, Deserialize)]
1374pub struct FpgaImage {
1375    pub id: String,
1376    #[serde(default)]
1377    pub name: String,
1378    #[serde(default)]
1379    pub description: String,
1380    /// `loadPermission` users the image is shared with.
1381    #[serde(default)]
1382    pub load_permission_users: Vec<String>,
1383    /// `loadPermission` groups (`all` for public).
1384    #[serde(default)]
1385    pub load_permission_groups: Vec<String>,
1386}
1387
1388/// An IAM instance-profile association (AssociateIamInstanceProfile).
1389#[derive(Clone, Debug, Serialize, Deserialize)]
1390pub struct IamInstanceProfileAssociation {
1391    pub association_id: String,
1392    pub instance_id: String,
1393    pub iam_instance_profile_arn: String,
1394    pub iam_instance_profile_id: String,
1395    /// `associating` | `associated` | `disassociating` | `disassociated`.
1396    pub state: String,
1397}
1398
1399/// A security-group-to-VPC association (AssociateSecurityGroupVpc).
1400#[derive(Clone, Debug, Serialize, Deserialize)]
1401pub struct SecurityGroupVpcAssociation {
1402    pub group_id: String,
1403    pub vpc_id: String,
1404    /// `associating` | `associated` | `disassociating` | `disassociated`.
1405    pub state: String,
1406}
1407
1408/// A route-server endpoint (CreateRouteServerEndpoint).
1409#[derive(Clone, Debug, Serialize, Deserialize)]
1410pub struct RouteServerEndpoint {
1411    pub id: String,
1412    pub route_server_id: String,
1413    pub vpc_id: String,
1414    pub subnet_id: String,
1415    pub eni_id: String,
1416    pub eni_address: String,
1417    pub state: String,
1418}
1419
1420/// A route-server BGP peer (CreateRouteServerPeer).
1421#[derive(Clone, Debug, Serialize, Deserialize)]
1422pub struct RouteServerPeer {
1423    pub id: String,
1424    pub route_server_endpoint_id: String,
1425    pub route_server_id: String,
1426    pub vpc_id: String,
1427    pub subnet_id: String,
1428    pub endpoint_eni_id: String,
1429    pub endpoint_eni_address: String,
1430    pub peer_address: String,
1431    pub peer_asn: i64,
1432    pub state: String,
1433}
1434
1435/// A provisioned BYOIP CIDR (ProvisionByoipCidr).
1436#[derive(Clone, Debug, Serialize, Deserialize)]
1437pub struct ByoipCidr {
1438    pub cidr: String,
1439    #[serde(default)]
1440    pub description: String,
1441    /// `provisioned` | `advertised` | `pending-deprovision` | `deprovisioned`.
1442    pub state: String,
1443}
1444
1445/// A public IPv4 address pool (CreatePublicIpv4Pool).
1446#[derive(Clone, Debug, Serialize, Deserialize)]
1447pub struct PublicIpv4Pool {
1448    pub pool_id: String,
1449    #[serde(default)]
1450    pub description: String,
1451    #[serde(default)]
1452    pub network_border_group: String,
1453    /// Provisioned CIDR blocks (ProvisionPublicIpv4PoolCidr).
1454    #[serde(default)]
1455    pub cidrs: Vec<String>,
1456}
1457
1458/// A Capacity Manager data-export configuration (CreateCapacityManagerDataExport).
1459#[derive(Clone, Debug, Serialize, Deserialize)]
1460pub struct CapacityManagerDataExport {
1461    pub id: String,
1462    pub s3_bucket_name: String,
1463    #[serde(default)]
1464    pub s3_bucket_prefix: String,
1465    pub schedule: String,
1466    pub output_format: String,
1467}
1468
1469/// A Transit Gateway multicast-domain subnet association.
1470#[derive(Clone, Debug, Serialize, Deserialize)]
1471pub struct TgwMcastAssociation {
1472    pub attachment_id: String,
1473    pub subnet_id: String,
1474    pub resource_id: String,
1475    pub resource_type: String,
1476    pub state: String,
1477}
1478
1479/// A Transit Gateway multicast group member or source registration.
1480#[derive(Clone, Debug, Serialize, Deserialize)]
1481pub struct TgwMcastGroup {
1482    pub group_ip: String,
1483    pub eni_id: String,
1484    /// `true` = group member, `false` = group source.
1485    pub is_member: bool,
1486}
1487
1488/// Per-account, per-region EC2 state. Resource families are added to this
1489/// struct as their batches land.
1490#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1491pub struct Ec2State {
1492    pub account_id: String,
1493    pub region: String,
1494    /// resource-id -> tags. Shared by every Describe* `tag:` filter.
1495    #[serde(default)]
1496    pub tags: BTreeMap<String, Vec<Tag>>,
1497    #[serde(default)]
1498    pub vpcs: BTreeMap<String, Vpc>,
1499    #[serde(default)]
1500    pub dhcp_options: BTreeMap<String, DhcpOptions>,
1501    #[serde(default)]
1502    pub subnets: BTreeMap<String, Subnet>,
1503    #[serde(default)]
1504    pub subnet_cidr_reservations: BTreeMap<String, SubnetCidrReservation>,
1505    #[serde(default)]
1506    pub security_groups: BTreeMap<String, SecurityGroup>,
1507    #[serde(default)]
1508    pub route_tables: BTreeMap<String, RouteTable>,
1509    #[serde(default)]
1510    pub internet_gateways: BTreeMap<String, InternetGateway>,
1511    #[serde(default)]
1512    pub egress_only_igws: BTreeMap<String, InternetGateway>,
1513    #[serde(default)]
1514    pub nat_gateways: BTreeMap<String, NatGateway>,
1515    /// keyed by allocation id.
1516    #[serde(default)]
1517    pub elastic_ips: BTreeMap<String, ElasticIp>,
1518    /// keyed by key name.
1519    #[serde(default)]
1520    pub key_pairs: BTreeMap<String, KeyPair>,
1521    /// keyed by group name.
1522    #[serde(default)]
1523    pub placement_groups: BTreeMap<String, PlacementGroup>,
1524    #[serde(default)]
1525    pub network_interfaces: BTreeMap<String, NetworkInterface>,
1526    /// keyed by permission id.
1527    #[serde(default)]
1528    pub eni_permissions: BTreeMap<String, NetworkInterfacePermission>,
1529    #[serde(default)]
1530    pub instances: BTreeMap<String, Instance>,
1531    #[serde(default)]
1532    pub volumes: BTreeMap<String, Volume>,
1533    /// Recycle Bin retention rules by resource type. AWS's `rbin` service (a
1534    /// separate front-door not modeled in this crate) creates these; when a rule
1535    /// covers a resource type, `Delete*` routes the resource to the recycle bin
1536    /// (`in_recycle_bin = true`) instead of hard-deleting it, so the matching
1537    /// `List*InRecycleBin` / `Restore*FromRecycleBin` ops can recover it.
1538    #[serde(default)]
1539    pub recycle_bin_retention: RecycleBinRetention,
1540    /// Account-level EBS default encryption toggle.
1541    #[serde(default)]
1542    pub ebs_encryption_default: bool,
1543    /// Account-level EBS default KMS key (None = `alias/aws/ebs`).
1544    #[serde(default)]
1545    pub ebs_default_kms_key_id: Option<String>,
1546    #[serde(default)]
1547    pub snapshots: BTreeMap<String, Snapshot>,
1548    /// Account-level snapshot block-public-access state.
1549    #[serde(default)]
1550    pub snapshot_block_public_access: String,
1551    #[serde(default)]
1552    pub images: BTreeMap<String, Image>,
1553    /// Watermarks attached to AMIs: image_id -> watermark_key -> watermark_name.
1554    #[serde(default)]
1555    pub image_watermarks: BTreeMap<String, BTreeMap<String, String>>,
1556    /// Account-level image block-public-access state.
1557    #[serde(default)]
1558    pub image_block_public_access: String,
1559    /// Account-level allowed-images settings state.
1560    #[serde(default)]
1561    pub allowed_images_settings: String,
1562    /// Allowed-images `imageCriterionSet`: each criterion is its list of
1563    /// `ImageProvider`s, persisted by ReplaceImageCriteriaInAllowedImagesSettings
1564    /// and reported by GetAllowedImagesSettings.
1565    #[serde(default)]
1566    pub allowed_image_criteria: Vec<Vec<String>>,
1567    #[serde(default)]
1568    pub network_acls: BTreeMap<String, NetworkAcl>,
1569    #[serde(default)]
1570    pub vpc_peerings: BTreeMap<String, VpcPeering>,
1571    #[serde(default)]
1572    pub vpc_endpoints: BTreeMap<String, VpcEndpoint>,
1573    /// Account-level VPC encryption control singleton (None = never modified,
1574    /// reported with default `unmanaged` / `default-state`).
1575    #[serde(default)]
1576    pub account_vpc_encryption_control: Option<AccountVpcEncryptionControl>,
1577    #[serde(default)]
1578    pub endpoint_services: BTreeMap<String, EndpointService>,
1579    #[serde(default)]
1580    pub connection_notifications: BTreeMap<String, ConnectionNotification>,
1581    #[serde(default)]
1582    pub flow_logs: BTreeMap<String, FlowLog>,
1583    #[serde(default)]
1584    pub launch_templates: BTreeMap<String, LaunchTemplate>,
1585    #[serde(default)]
1586    pub spot_requests: BTreeMap<String, SpotRequest>,
1587    #[serde(default)]
1588    pub spot_fleets: BTreeMap<String, SpotFleet>,
1589    #[serde(default)]
1590    pub fleets: BTreeMap<String, Fleet>,
1591    /// Account-level spot datafeed subscription (bucket, prefix).
1592    #[serde(default)]
1593    pub spot_datafeed: Option<(String, String)>,
1594    #[serde(default)]
1595    pub capacity_reservations: BTreeMap<String, CapacityReservation>,
1596    /// Capacity reservation fleet ids (metadata-only).
1597    #[serde(default)]
1598    pub capacity_reservation_fleets: BTreeMap<String, String>,
1599    #[serde(default)]
1600    pub reserved_instances: BTreeMap<String, ReservedInstances>,
1601    #[serde(default)]
1602    pub reserved_instances_listings: BTreeMap<String, ReservedInstancesListing>,
1603    #[serde(default)]
1604    pub reserved_instances_modifications: BTreeMap<String, ReservedInstancesModification>,
1605    #[serde(default)]
1606    pub dedicated_hosts: BTreeMap<String, DedicatedHost>,
1607    #[serde(default)]
1608    pub transit_gateways: BTreeMap<String, TransitGateway>,
1609    #[serde(default)]
1610    pub tgw_attachments: BTreeMap<String, TgwAttachment>,
1611    #[serde(default)]
1612    pub tgw_route_tables: BTreeMap<String, TgwRouteTable>,
1613    /// route-table-id -> static routes.
1614    #[serde(default)]
1615    pub tgw_routes: BTreeMap<String, Vec<TgwRoute>>,
1616    /// route-table-id -> associated attachment ids.
1617    #[serde(default)]
1618    pub tgw_rt_associations: BTreeMap<String, Vec<String>>,
1619    /// route-table-id -> propagated attachment ids.
1620    #[serde(default)]
1621    pub tgw_rt_propagations: BTreeMap<String, Vec<String>>,
1622    /// route-table-id -> prefix-list ids referenced.
1623    #[serde(default)]
1624    pub tgw_prefix_list_refs: BTreeMap<String, Vec<String>>,
1625    #[serde(default)]
1626    pub tgw_peerings: BTreeMap<String, TgwPeering>,
1627    /// connect-attachment-id -> (transport attachment id, tgw id).
1628    #[serde(default)]
1629    pub tgw_connects: BTreeMap<String, (String, String)>,
1630    /// connect-peer-id -> attachment id.
1631    #[serde(default)]
1632    pub tgw_connect_peers: BTreeMap<String, String>,
1633    /// policy-table-id -> tgw id.
1634    #[serde(default)]
1635    pub tgw_policy_tables: BTreeMap<String, String>,
1636    /// policy-table-id -> associated attachment ids.
1637    #[serde(default)]
1638    pub tgw_policy_table_associations: BTreeMap<String, Vec<String>>,
1639    /// announcement-id -> (route-table id, peering-attachment id).
1640    #[serde(default)]
1641    pub tgw_announcements: BTreeMap<String, (String, String)>,
1642    #[serde(default)]
1643    pub tgw_multicast_domains: BTreeMap<String, TgwMulticastDomain>,
1644    #[serde(default)]
1645    pub tgw_metering_policies: BTreeMap<String, TgwMeteringPolicy>,
1646    #[serde(default)]
1647    pub customer_gateways: BTreeMap<String, CustomerGateway>,
1648    #[serde(default)]
1649    pub vpn_gateways: BTreeMap<String, VpnGateway>,
1650    #[serde(default)]
1651    pub vpn_connections: BTreeMap<String, VpnConnection>,
1652    #[serde(default)]
1653    pub vpn_concentrators: BTreeMap<String, VpnConcentrator>,
1654    #[serde(default)]
1655    pub client_vpn_endpoints: BTreeMap<String, ClientVpnEndpoint>,
1656    #[serde(default)]
1657    pub ipams: BTreeMap<String, Ipam>,
1658    #[serde(default)]
1659    pub ipam_scopes: BTreeMap<String, IpamScope>,
1660    #[serde(default)]
1661    pub ipam_pools: BTreeMap<String, IpamPool>,
1662    /// pool-id -> provisioned (cidr, cidr-id).
1663    #[serde(default)]
1664    pub ipam_pool_cidrs: BTreeMap<String, Vec<(String, String)>>,
1665    /// pool-id -> allocations (cidr, allocation-id).
1666    #[serde(default)]
1667    pub ipam_pool_allocations: BTreeMap<String, Vec<(String, String)>>,
1668    #[serde(default)]
1669    pub ipam_resource_discoveries: BTreeMap<String, IpamResourceDiscovery>,
1670    /// association-id -> (discovery-id, ipam-id).
1671    #[serde(default)]
1672    pub ipam_rd_associations: BTreeMap<String, (String, String)>,
1673    /// asn -> associated cidr.
1674    #[serde(default)]
1675    pub ipam_byoasns: BTreeMap<String, String>,
1676    /// external-token-id -> ipam-id.
1677    #[serde(default)]
1678    pub ipam_ext_tokens: BTreeMap<String, String>,
1679    #[serde(default)]
1680    pub ipam_policies: BTreeMap<String, IpamPolicy>,
1681    #[serde(default)]
1682    pub ipam_pl_resolvers: BTreeMap<String, IpamPrefixListResolver>,
1683    #[serde(default)]
1684    pub ipam_pl_resolver_targets: BTreeMap<String, IpamPrefixListResolverTarget>,
1685    /// policy-id -> (locale, resource-type) allocation-rule documents.
1686    #[serde(default)]
1687    pub ipam_policy_alloc_rules: BTreeMap<String, Vec<(String, String)>>,
1688    /// The single enabled IPAM policy id, if any.
1689    #[serde(default)]
1690    pub ipam_enabled_policy: Option<String>,
1691    #[serde(default)]
1692    pub va_instances: BTreeMap<String, VerifiedAccessInstance>,
1693    #[serde(default)]
1694    pub va_trust_providers: BTreeMap<String, VerifiedAccessTrustProvider>,
1695    #[serde(default)]
1696    pub va_groups: BTreeMap<String, VerifiedAccessGroup>,
1697    #[serde(default)]
1698    pub va_endpoints: BTreeMap<String, VerifiedAccessEndpoint>,
1699    /// group-id -> policy document.
1700    #[serde(default)]
1701    pub va_group_policies: BTreeMap<String, String>,
1702    /// endpoint-id -> policy document.
1703    #[serde(default)]
1704    pub va_endpoint_policies: BTreeMap<String, String>,
1705    /// instance-id -> access-logging configuration.
1706    #[serde(default)]
1707    pub va_logging: BTreeMap<String, VerifiedAccessLoggingConfig>,
1708    #[serde(default)]
1709    pub ni_paths: BTreeMap<String, NetworkInsightsPath>,
1710    #[serde(default)]
1711    pub ni_analyses: BTreeMap<String, NetworkInsightsAnalysis>,
1712    #[serde(default)]
1713    pub ni_access_scopes: BTreeMap<String, NetworkInsightsAccessScope>,
1714    #[serde(default)]
1715    pub ni_scope_analyses: BTreeMap<String, NetworkInsightsAccessScopeAnalysis>,
1716    #[serde(default)]
1717    pub carrier_gateways: BTreeMap<String, CarrierGateway>,
1718    #[serde(default)]
1719    pub coip_pools: BTreeMap<String, CoipPool>,
1720    /// coip-pool-id -> CIDRs.
1721    #[serde(default)]
1722    pub coip_pool_cidrs: BTreeMap<String, Vec<String>>,
1723    #[serde(default)]
1724    pub lg_route_tables: BTreeMap<String, LocalGatewayRouteTable>,
1725    /// route-table-id -> destination CIDRs.
1726    #[serde(default)]
1727    pub lg_routes: BTreeMap<String, Vec<String>>,
1728    #[serde(default)]
1729    pub lg_rt_vpc_assocs: BTreeMap<String, LocalGatewayRouteTableVpcAssoc>,
1730    #[serde(default)]
1731    pub lg_virtual_interfaces: BTreeMap<String, LocalGatewayVif>,
1732    #[serde(default)]
1733    pub lg_vif_groups: BTreeMap<String, LocalGatewayVifGroup>,
1734    #[serde(default)]
1735    pub lg_rt_vifg_assocs: BTreeMap<String, LocalGatewayRouteTableVifgAssoc>,
1736    #[serde(default)]
1737    pub instance_connect_endpoints: BTreeMap<String, InstanceConnectEndpoint>,
1738    /// Image ids with fast-launch enabled.
1739    #[serde(default)]
1740    pub fast_launch_images: std::collections::HashSet<String>,
1741    #[serde(default)]
1742    pub serial_console_access: bool,
1743    // ---- account/region-scoped id-format settings (ModifyIdFormat) ----
1744    /// resource type -> use-long-ids (account default).
1745    #[serde(default)]
1746    pub id_format: BTreeMap<String, bool>,
1747    /// principal ARN -> (resource type -> use-long-ids).
1748    #[serde(default)]
1749    pub identity_id_format: BTreeMap<String, BTreeMap<String, bool>>,
1750    /// burstable instance family -> CpuCredits (`standard` | `unlimited`),
1751    /// set by ModifyDefaultCreditSpecification, read by GetDefaultCreditSpecification.
1752    #[serde(default)]
1753    pub default_credit_specs: BTreeMap<String, String>,
1754    /// instance id -> CpuCredits (`standard` | `unlimited`), set by
1755    /// ModifyInstanceCreditSpecification, read by DescribeInstanceCreditSpecifications.
1756    #[serde(default)]
1757    pub instance_credit_specs: BTreeMap<String, String>,
1758    /// Account/region IMDS defaults (ModifyInstanceMetadataDefaults /
1759    /// GetInstanceMetadataDefaults). `None` reports the AWS defaults.
1760    #[serde(default)]
1761    pub instance_metadata_defaults: Option<InstanceMetadataDefaults>,
1762    /// Instance-tag keys surfaced in instance-state-change events
1763    /// (Register/Describe InstanceEventNotificationAttributes).
1764    #[serde(default)]
1765    pub event_notification_tag_keys: Vec<String>,
1766    /// When true, all instance tags are included in event notifications
1767    /// (`IncludeAllTagsOfInstance`).
1768    #[serde(default)]
1769    pub event_notification_include_all_tags: bool,
1770    /// VPC block-public-access `InternetGatewayBlockMode` (account/region
1771    /// singleton). `None` reports the default `off`.
1772    #[serde(default)]
1773    pub vpc_bpa_internet_gateway_block_mode: Option<String>,
1774    /// Managed-resource `DefaultVisibility` (`hidden` | `visible`). `None`
1775    /// reports the default `visible`.
1776    #[serde(default)]
1777    pub managed_resource_default_visibility: Option<String>,
1778    /// Availability-zone group -> opt-in status (`opted-in` | `not-opted-in`),
1779    /// set by ModifyAvailabilityZoneGroup, reflected in DescribeAvailabilityZones.
1780    #[serde(default)]
1781    pub az_group_optin: BTreeMap<String, String>,
1782    #[serde(default)]
1783    pub managed_prefix_lists: BTreeMap<String, ManagedPrefixList>,
1784    #[serde(default)]
1785    pub instance_event_windows: BTreeMap<String, InstanceEventWindow>,
1786    #[serde(default)]
1787    pub traffic_mirror_targets: BTreeMap<String, TrafficMirrorTarget>,
1788    #[serde(default)]
1789    pub traffic_mirror_filters: BTreeMap<String, TrafficMirrorFilter>,
1790    #[serde(default)]
1791    pub traffic_mirror_filter_rules: BTreeMap<String, TrafficMirrorFilterRule>,
1792    #[serde(default)]
1793    pub traffic_mirror_sessions: BTreeMap<String, TrafficMirrorSession>,
1794    #[serde(default)]
1795    pub route_servers: BTreeMap<String, RouteServer>,
1796    #[serde(default)]
1797    pub vpc_encryption_controls: BTreeMap<String, VpcEncryptionControl>,
1798    #[serde(default)]
1799    pub vpc_bpa_exclusions: BTreeMap<String, VpcBpaExclusion>,
1800    #[serde(default)]
1801    pub fpga_images: BTreeMap<String, FpgaImage>,
1802    /// IPAM pool allocation id -> description (ModifyIpamPoolAllocation), read
1803    /// back by DescribeIpamPoolAllocations.
1804    #[serde(default)]
1805    pub ipam_allocation_descriptions: BTreeMap<String, String>,
1806    /// IAM instance-profile associations, keyed by association id.
1807    #[serde(default)]
1808    pub iam_instance_profile_associations: BTreeMap<String, IamInstanceProfileAssociation>,
1809    /// Security-group-to-VPC associations, keyed by `<group-id>:<vpc-id>`.
1810    #[serde(default)]
1811    pub security_group_vpc_associations: BTreeMap<String, SecurityGroupVpcAssociation>,
1812    #[serde(default)]
1813    pub route_server_endpoints: BTreeMap<String, RouteServerEndpoint>,
1814    #[serde(default)]
1815    pub route_server_peers: BTreeMap<String, RouteServerPeer>,
1816    /// BYOIP CIDRs, keyed by CIDR.
1817    #[serde(default)]
1818    pub byoip_cidrs: BTreeMap<String, ByoipCidr>,
1819    #[serde(default)]
1820    pub public_ipv4_pools: BTreeMap<String, PublicIpv4Pool>,
1821    /// Whether Capacity Manager is enabled for this account/region.
1822    #[serde(default)]
1823    pub capacity_manager_enabled: bool,
1824    /// Capacity Manager Organizations access (`enabled` | `disabled`); `None`
1825    /// reports the default `disabled`.
1826    #[serde(default)]
1827    pub capacity_manager_org_access: Option<String>,
1828    /// Capacity Manager monitored tag keys.
1829    #[serde(default)]
1830    pub capacity_manager_monitored_tag_keys: Vec<String>,
1831    #[serde(default)]
1832    pub capacity_manager_data_exports: BTreeMap<String, CapacityManagerDataExport>,
1833    /// TGW multicast-domain subnet associations, keyed by domain id.
1834    #[serde(default)]
1835    pub tgw_mcast_associations: BTreeMap<String, Vec<TgwMcastAssociation>>,
1836    /// TGW multicast group members/sources, keyed by domain id.
1837    #[serde(default)]
1838    pub tgw_mcast_groups: BTreeMap<String, Vec<TgwMcastGroup>>,
1839}
1840
1841impl Ec2State {
1842    pub fn new(account_id: &str, region: &str) -> Self {
1843        let mut state = Self {
1844            account_id: account_id.to_string(),
1845            region: region.to_string(),
1846            ..Default::default()
1847        };
1848        // Seed the default VPC topology (VPC, IGW, subnets, route table,
1849        // security group, NACL) the way every AWS account+region ships one, so
1850        // callers that never touch the VPC APIs still launch into a real,
1851        // isolatable network. Ids are deterministic, so the throwaway empty
1852        // states the read paths build report the same ids as this one.
1853        crate::defaults::bootstrap_default_network(&mut state);
1854        // Seed the public AMI catalogue (Amazon Linux, Ubuntu, Windows) so
1855        // `aws_ami` data sources resolve, matching how every real account sees
1856        // Amazon/Canonical-owned public images.
1857        crate::defaults::seed_public_images(&mut state);
1858        state
1859    }
1860
1861    /// Idempotently (re)seed the public AMI catalogue into this account. Used on
1862    /// snapshot restore so accounts persisted by a binary that predated the
1863    /// catalogue (#1964) still get it after an upgrade+restart — without it,
1864    /// `aws_ami { owners=["amazon"] }` returns empty for legacy accounts. Seeds
1865    /// have deterministic ids, so re-seeding an already-seeded account is a no-op.
1866    pub fn ensure_public_images_seeded(&mut self) {
1867        crate::defaults::seed_public_images(self);
1868    }
1869
1870    /// Replace the tag set for `resource_id` with `tags` merged over any
1871    /// existing tags (CreateTags is upsert-by-key, matching AWS).
1872    pub fn upsert_tags(&mut self, resource_id: &str, new_tags: &[Tag]) {
1873        let entry = self.tags.entry(resource_id.to_string()).or_default();
1874        for t in new_tags {
1875            if let Some(existing) = entry.iter_mut().find(|e| e.key == t.key) {
1876                existing.value = t.value.clone();
1877            } else {
1878                entry.push(t.clone());
1879            }
1880        }
1881    }
1882
1883    /// Remove tags for `resource_id`. When a tag's value is `None`, the key is
1884    /// removed regardless of value; when `Some`, only a key+value match is
1885    /// removed (AWS DeleteTags semantics).
1886    pub fn remove_tags(&mut self, resource_id: &str, to_remove: &[(String, Option<String>)]) {
1887        if let Some(entry) = self.tags.get_mut(resource_id) {
1888            for (key, value) in to_remove {
1889                entry.retain(|e| {
1890                    if &e.key != key {
1891                        return true;
1892                    }
1893                    match value {
1894                        Some(v) => &e.value != v,
1895                        None => false,
1896                    }
1897                });
1898            }
1899            if entry.is_empty() {
1900                self.tags.remove(resource_id);
1901            }
1902        }
1903    }
1904
1905    /// Tags for `resource_id`, or an empty slice when none.
1906    pub fn tags_for(&self, resource_id: &str) -> &[Tag] {
1907        self.tags.get(resource_id).map(Vec::as_slice).unwrap_or(&[])
1908    }
1909}
1910
1911#[cfg(test)]
1912mod tests {
1913    use super::*;
1914
1915    fn tag(k: &str, v: &str) -> Tag {
1916        Tag {
1917            key: k.to_string(),
1918            value: v.to_string(),
1919        }
1920    }
1921
1922    #[test]
1923    fn upsert_tags_inserts_then_overwrites_by_key() {
1924        let mut s = Ec2State::new("123456789012", "us-east-1");
1925        s.upsert_tags("vpc-1", &[tag("Name", "a"), tag("env", "dev")]);
1926        s.upsert_tags("vpc-1", &[tag("Name", "b")]);
1927        let tags = s.tags_for("vpc-1");
1928        assert_eq!(tags.len(), 2);
1929        assert_eq!(tags.iter().find(|t| t.key == "Name").unwrap().value, "b");
1930    }
1931
1932    #[test]
1933    fn remove_tags_by_key_and_by_key_value() {
1934        let mut s = Ec2State::new("123456789012", "us-east-1");
1935        s.upsert_tags(
1936            "i-1",
1937            &[tag("Name", "x"), tag("env", "prod"), tag("team", "a")],
1938        );
1939        // key-only removal
1940        s.remove_tags("i-1", &[("Name".to_string(), None)]);
1941        // key+value removal that does NOT match -> kept
1942        s.remove_tags("i-1", &[("env".to_string(), Some("dev".to_string()))]);
1943        // key+value removal that matches -> removed
1944        s.remove_tags("i-1", &[("team".to_string(), Some("a".to_string()))]);
1945        let tags = s.tags_for("i-1");
1946        assert_eq!(tags.len(), 1);
1947        assert_eq!(tags[0].key, "env");
1948    }
1949
1950    #[test]
1951    fn empty_tag_set_drops_resource_entry() {
1952        let mut s = Ec2State::new("123456789012", "us-east-1");
1953        s.upsert_tags("sg-1", &[tag("Name", "x")]);
1954        s.remove_tags("sg-1", &[("Name".to_string(), None)]);
1955        assert!(!s.tags.contains_key("sg-1"));
1956    }
1957
1958    fn sample_instance() -> Instance {
1959        Instance {
1960            instance_id: "i-1".to_string(),
1961            image_id: "ami-1".to_string(),
1962            instance_type: "t3.micro".to_string(),
1963            state_code: 16,
1964            state_name: "running".to_string(),
1965            private_ip: "10.0.0.1".to_string(),
1966            public_ip: Some("52.0.0.1".to_string()),
1967            subnet_id: Some("subnet-1".to_string()),
1968            vpc_id: Some("vpc-1".to_string()),
1969            key_name: None,
1970            security_group_ids: vec!["sg-1".to_string()],
1971            reservation_id: "r-1".to_string(),
1972            ami_launch_index: 0,
1973            monitoring: false,
1974            az: "us-east-1a".to_string(),
1975            launch_time: "2024-01-01T00:00:00.000Z".to_string(),
1976            container_id: Some("abc".to_string()),
1977            disable_api_termination: true,
1978            disable_api_stop: true,
1979            source_dest_check: false,
1980            ebs_optimized: true,
1981            instance_initiated_shutdown_behavior: "terminate".to_string(),
1982            user_data: Some("ZWNobyBoaQ==".to_string()),
1983            metadata_options: MetadataOptions {
1984                http_tokens: "required".to_string(),
1985                ..MetadataOptions::default()
1986            },
1987            cpu_options: Some(CpuOptions {
1988                core_count: 4,
1989                threads_per_core: 2,
1990            }),
1991            bandwidth_weighting: Some("vpc-1".to_string()),
1992            maintenance_options: MaintenanceOptions::default(),
1993            placement_tenancy: Some("dedicated".to_string()),
1994            placement_affinity: None,
1995            placement_group_name: Some("cluster-1".to_string()),
1996            private_dns_hostname_type: Some("resource-name".to_string()),
1997            enable_resource_name_dns_a_record: true,
1998            enable_resource_name_dns_aaaa_record: false,
1999        }
2000    }
2001
2002    #[test]
2003    fn instance_attributes_round_trip_through_serde() {
2004        let inst = sample_instance();
2005        let json = serde_json::to_string(&inst).unwrap();
2006        let back: Instance = serde_json::from_str(&json).unwrap();
2007        assert!(back.disable_api_termination);
2008        assert!(back.disable_api_stop);
2009        assert!(!back.source_dest_check);
2010        assert!(back.ebs_optimized);
2011        assert_eq!(back.instance_initiated_shutdown_behavior, "terminate");
2012        assert_eq!(back.user_data.as_deref(), Some("ZWNobyBoaQ=="));
2013        assert_eq!(back.metadata_options.http_tokens, "required");
2014        assert_eq!(back.cpu_options.as_ref().unwrap().core_count, 4);
2015        assert_eq!(back.bandwidth_weighting.as_deref(), Some("vpc-1"));
2016        assert_eq!(back.placement_tenancy.as_deref(), Some("dedicated"));
2017        assert_eq!(back.placement_group_name.as_deref(), Some("cluster-1"));
2018    }
2019
2020    #[test]
2021    fn instance_attribute_defaults_load_from_legacy_snapshot() {
2022        // A snapshot written before the attribute fields existed (only the
2023        // pre-existing members) must deserialize, with AWS defaults filled in.
2024        let legacy = r#"{
2025            "instance_id":"i-1","image_id":"ami-1","instance_type":"t3.micro",
2026            "state_code":16,"state_name":"running","private_ip":"10.0.0.1",
2027            "public_ip":null,"subnet_id":null,"vpc_id":null,"key_name":null,
2028            "reservation_id":"r-1","ami_launch_index":0,"monitoring":false,
2029            "az":"us-east-1a","launch_time":"2024-01-01T00:00:00.000Z"
2030        }"#;
2031        let inst: Instance = serde_json::from_str(legacy).unwrap();
2032        assert!(!inst.disable_api_termination);
2033        assert!(inst.source_dest_check, "sourceDestCheck defaults to true");
2034        assert_eq!(inst.instance_initiated_shutdown_behavior, "stop");
2035        assert_eq!(inst.metadata_options.http_tokens, "optional");
2036        assert!(inst.cpu_options.is_none());
2037    }
2038}