1use chrono::{DateTime, Utc};
2use parking_lot::RwLock;
3use std::collections::{BTreeMap, HashMap};
4use std::sync::Arc;
5
6#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
7pub struct SsmParameter {
8 pub name: String,
9 pub value: String,
10 pub param_type: String, pub version: i64,
12 pub arn: String,
13 pub last_modified: DateTime<Utc>,
14 pub history: Vec<SsmParameterVersion>,
15 pub tags: HashMap<String, String>,
16 pub labels: HashMap<i64, Vec<String>>, pub description: Option<String>,
18 pub allowed_pattern: Option<String>,
19 pub key_id: Option<String>,
20 pub data_type: String, pub tier: String, pub policies: Option<String>,
23}
24
25#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
26pub struct SsmParameterVersion {
27 pub value: String,
28 pub version: i64,
29 pub last_modified: DateTime<Utc>,
30 pub param_type: String,
31 pub description: Option<String>,
32 pub key_id: Option<String>,
33 pub labels: Vec<String>,
34}
35
36#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
37pub struct SsmDocument {
38 pub name: String,
39 pub content: String,
40 pub document_type: String,
41 pub document_format: String,
42 pub target_type: Option<String>,
43 pub version_name: Option<String>,
44 pub tags: HashMap<String, String>,
45 pub versions: Vec<SsmDocumentVersion>,
46 pub default_version: String,
47 pub latest_version: String,
48 pub created_date: DateTime<Utc>,
49 pub owner: String,
50 pub status: String,
51 pub permissions: HashMap<String, Vec<String>>, #[serde(default)]
53 pub reviews: Vec<DocumentReview>,
54}
55
56#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
57pub struct DocumentReview {
58 pub reviewer: String,
59 pub action: String, pub comment: Vec<DocumentReviewComment>,
61 pub created_time: DateTime<Utc>,
62 pub updated_time: DateTime<Utc>,
63}
64
65#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
66pub struct DocumentReviewComment {
67 pub comment_type: String, pub content: String,
69}
70
71#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
72pub struct SsmDocumentVersion {
73 pub content: String,
74 pub document_version: String,
75 pub version_name: Option<String>,
76 pub created_date: DateTime<Utc>,
77 pub status: String,
78 pub document_format: String,
79 pub is_default_version: bool,
80}
81
82#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
83pub struct SsmCommand {
84 pub command_id: String,
85 pub document_name: String,
86 pub instance_ids: Vec<String>,
87 pub parameters: HashMap<String, Vec<String>>,
88 pub status: String,
89 pub requested_date_time: DateTime<Utc>,
90 pub comment: Option<String>,
91 pub output_s3_bucket_name: Option<String>,
92 pub output_s3_key_prefix: Option<String>,
93 pub output_s3_region: Option<String>,
94 pub timeout_seconds: Option<i64>,
95 pub service_role_arn: Option<String>,
96 pub notification_config: Option<serde_json::Value>,
97 pub targets: Vec<serde_json::Value>,
98 pub document_hash: Option<String>,
99 pub document_hash_type: Option<String>,
100}
101
102#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
103pub struct MaintenanceWindowTarget {
104 pub window_target_id: String,
105 pub window_id: String,
106 pub resource_type: String,
107 pub targets: Vec<serde_json::Value>,
108 pub name: Option<String>,
109 pub description: Option<String>,
110 pub owner_information: Option<String>,
111}
112
113#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
114pub struct MaintenanceWindowTask {
115 pub window_task_id: String,
116 pub window_id: String,
117 pub task_arn: String,
118 pub task_type: String,
119 pub targets: Vec<serde_json::Value>,
120 pub max_concurrency: Option<String>,
121 pub max_errors: Option<String>,
122 pub priority: i64,
123 pub service_role_arn: Option<String>,
124 pub name: Option<String>,
125 pub description: Option<String>,
126}
127
128#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
129pub struct MaintenanceWindow {
130 pub id: String,
131 pub name: String,
132 pub schedule: String,
133 pub duration: i64,
134 pub cutoff: i64,
135 pub allow_unassociated_targets: bool,
136 pub enabled: bool,
137 pub description: Option<String>,
138 pub tags: HashMap<String, String>,
139 pub targets: Vec<MaintenanceWindowTarget>,
140 pub tasks: Vec<MaintenanceWindowTask>,
141 pub schedule_timezone: Option<String>,
142 pub schedule_offset: Option<i64>,
143 pub start_date: Option<String>,
144 pub end_date: Option<String>,
145 pub client_token: Option<String>,
146}
147
148#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
149pub struct PatchBaseline {
150 pub id: String,
151 pub name: String,
152 pub operating_system: String,
153 pub description: Option<String>,
154 pub approval_rules: Option<serde_json::Value>,
155 pub approved_patches: Vec<String>,
156 pub rejected_patches: Vec<String>,
157 pub tags: HashMap<String, String>,
158 pub approved_patches_compliance_level: String,
159 pub rejected_patches_action: String,
160 pub global_filters: Option<serde_json::Value>,
161 pub sources: Vec<serde_json::Value>,
162 pub approved_patches_enable_non_security: bool,
163 pub available_security_updates_compliance_status: Option<String>,
164 pub client_token: Option<String>,
165}
166
167#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
168pub struct PatchGroup {
169 pub baseline_id: String,
170 pub patch_group: String,
171}
172
173#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
174pub struct SsmAssociation {
175 pub association_id: String,
176 pub name: String, pub targets: Vec<serde_json::Value>,
178 pub schedule_expression: Option<String>,
179 pub parameters: HashMap<String, Vec<String>>,
180 pub association_name: Option<String>,
181 pub document_version: Option<String>,
182 pub output_location: Option<serde_json::Value>,
183 pub automation_target_parameter_name: Option<String>,
184 pub max_errors: Option<String>,
185 pub max_concurrency: Option<String>,
186 pub compliance_severity: Option<String>,
187 pub sync_compliance: Option<String>,
188 pub apply_only_at_cron_interval: bool,
189 pub calendar_names: Vec<String>,
190 pub target_locations: Vec<serde_json::Value>,
191 pub schedule_offset: Option<i64>,
192 pub target_maps: Vec<serde_json::Value>,
193 pub tags: HashMap<String, String>,
194 pub status: String,
195 pub status_date: DateTime<Utc>,
196 pub overview: serde_json::Value,
197 pub created_date: DateTime<Utc>,
198 pub last_update_association_date: DateTime<Utc>,
199 pub last_execution_date: Option<DateTime<Utc>>,
200 pub instance_id: Option<String>,
201 pub versions: Vec<SsmAssociationVersion>,
202}
203
204#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
205pub struct SsmAssociationVersion {
206 pub version: i64,
207 pub name: String,
208 pub targets: Vec<serde_json::Value>,
209 pub schedule_expression: Option<String>,
210 pub parameters: HashMap<String, Vec<String>>,
211 pub document_version: Option<String>,
212 pub created_date: DateTime<Utc>,
213 pub association_name: Option<String>,
214 pub max_errors: Option<String>,
215 pub max_concurrency: Option<String>,
216 pub compliance_severity: Option<String>,
217}
218
219#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
220pub struct SsmOpsItem {
221 pub ops_item_id: String,
222 pub title: String,
223 pub description: Option<String>,
224 pub source: String,
225 pub status: String,
226 pub priority: Option<i64>,
227 pub severity: Option<String>,
228 pub category: Option<String>,
229 pub operational_data: HashMap<String, serde_json::Value>,
230 pub notifications: Vec<serde_json::Value>,
231 pub related_ops_items: Vec<serde_json::Value>,
232 pub tags: HashMap<String, String>,
233 pub created_time: DateTime<Utc>,
234 pub last_modified_time: DateTime<Utc>,
235 pub created_by: String,
236 pub last_modified_by: String,
237 pub ops_item_type: Option<String>,
238 pub planned_start_time: Option<DateTime<Utc>>,
239 pub planned_end_time: Option<DateTime<Utc>>,
240 pub actual_start_time: Option<DateTime<Utc>>,
241 pub actual_end_time: Option<DateTime<Utc>>,
242}
243
244#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
245pub struct SsmResourcePolicy {
246 pub policy_id: String,
247 pub policy_hash: String,
248 pub policy: String,
249 pub resource_arn: String,
250}
251
252#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
253pub struct SsmServiceSetting {
254 pub setting_id: String,
255 pub setting_value: String,
256 pub last_modified_date: DateTime<Utc>,
257 pub last_modified_user: String,
258 pub status: String,
259}
260
261#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
262pub struct OpsItemRelatedItem {
263 pub association_id: String,
264 pub ops_item_id: String,
265 pub association_type: String,
266 pub resource_type: String,
267 pub resource_uri: String,
268 pub created_time: DateTime<Utc>,
269 pub created_by: String,
270 pub last_modified_time: DateTime<Utc>,
271 pub last_modified_by: String,
272}
273
274#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
275pub struct OpsItemEvent {
276 pub ops_item_id: String,
277 pub event_id: String,
278 pub source: String,
279 pub detail_type: String,
280 pub created_time: DateTime<Utc>,
281 pub created_by: String,
282}
283
284#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
285pub struct OpsMetadataEntry {
286 pub ops_metadata_arn: String,
287 pub resource_id: String,
288 pub metadata: HashMap<String, serde_json::Value>,
289 pub creation_date: DateTime<Utc>,
290}
291
292#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
293pub struct AutomationExecution {
294 pub automation_execution_id: String,
295 pub document_name: String,
296 pub document_version: Option<String>,
297 pub automation_execution_status: String,
298 pub execution_start_time: DateTime<Utc>,
299 pub execution_end_time: Option<DateTime<Utc>>,
300 pub parameters: HashMap<String, Vec<String>>,
301 pub outputs: HashMap<String, Vec<String>>,
302 pub mode: String,
303 pub target: Option<String>,
304 pub targets: Vec<serde_json::Value>,
305 pub max_concurrency: Option<String>,
306 pub max_errors: Option<String>,
307 pub executed_by: String,
308 pub step_executions: Vec<AutomationStepExecution>,
309 pub automation_subtype: Option<String>,
310 pub runbooks: Vec<serde_json::Value>,
311 pub change_request_name: Option<String>,
312 pub scheduled_time: Option<DateTime<Utc>>,
313}
314
315#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
316pub struct AutomationStepExecution {
317 pub step_name: String,
318 pub action: String,
319 pub step_status: String,
320 pub execution_start_time: Option<DateTime<Utc>>,
321 pub execution_end_time: Option<DateTime<Utc>>,
322 pub inputs: HashMap<String, String>,
323 pub outputs: HashMap<String, Vec<String>>,
324 pub step_execution_id: String,
325}
326
327#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
328pub struct SsmSession {
329 pub session_id: String,
330 pub target: String,
331 pub status: String,
332 pub start_date: DateTime<Utc>,
333 pub end_date: Option<DateTime<Utc>>,
334 pub owner: String,
335 pub reason: Option<String>,
336}
337
338#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
339pub struct SsmActivation {
340 pub activation_id: String,
341 pub iam_role: String,
342 pub registration_limit: i64,
343 pub registrations_count: i64,
344 pub expiration_date: Option<DateTime<Utc>>,
345 pub description: Option<String>,
346 pub default_instance_name: Option<String>,
347 pub created_date: DateTime<Utc>,
348 pub expired: bool,
349 pub tags: HashMap<String, String>,
350}
351
352#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
353pub struct ManagedInstance {
354 pub instance_id: String,
355 pub activation_id: Option<String>,
356 pub iam_role: String,
357 pub ping_status: String,
358 pub platform_type: String,
359 pub platform_name: String,
360 pub platform_version: String,
361 pub agent_version: String,
362 pub last_ping_date_time: DateTime<Utc>,
363 pub registration_date: DateTime<Utc>,
364 pub resource_type: String,
365 pub computer_name: String,
366 pub ip_address: String,
367 pub is_latest_version: bool,
368 pub association_status: Option<String>,
369 pub source_id: Option<String>,
370 pub source_type: Option<String>,
371}
372
373#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
374pub struct ExecutionPreview {
375 pub execution_preview_id: String,
376 pub document_name: String,
377 pub status: String,
378 pub created_time: DateTime<Utc>,
379}
380
381#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
382pub struct SsmState {
383 pub account_id: String,
384 pub region: String,
385 pub parameters: BTreeMap<String, SsmParameter>, pub documents: BTreeMap<String, SsmDocument>,
387 pub commands: Vec<SsmCommand>,
388 pub maintenance_windows: HashMap<String, MaintenanceWindow>,
389 pub patch_baselines: HashMap<String, PatchBaseline>,
390 pub patch_groups: Vec<PatchGroup>,
391 pub associations: HashMap<String, SsmAssociation>,
392 pub ops_items: HashMap<String, SsmOpsItem>,
393 pub resource_policies: Vec<SsmResourcePolicy>,
394 pub service_settings: HashMap<String, SsmServiceSetting>,
395 pub default_patch_baseline_id: Option<String>,
396 pub ops_item_counter: u64,
397 pub maintenance_window_executions: Vec<MaintenanceWindowExecution>,
398 pub inventory_entries: HashMap<String, InventoryEntry>, pub inventory_deletions: Vec<InventoryDeletion>,
400 pub compliance_items: Vec<ComplianceItem>,
401 pub resource_data_syncs: HashMap<String, ResourceDataSync>,
402 pub mw_execution_counter: u64,
403 pub inventory_deletion_counter: u64,
404 pub ops_item_related_items: Vec<OpsItemRelatedItem>,
405 pub ops_item_related_item_counter: u64,
406 pub ops_item_events: Vec<OpsItemEvent>,
407 pub ops_metadata: HashMap<String, OpsMetadataEntry>,
408 pub automation_executions: HashMap<String, AutomationExecution>,
409 pub automation_execution_counter: u64,
410 pub sessions: HashMap<String, SsmSession>,
411 pub session_counter: u64,
412 pub activations: HashMap<String, SsmActivation>,
413 pub activation_counter: u64,
414 pub managed_instances: HashMap<String, ManagedInstance>,
415 pub execution_previews: HashMap<String, ExecutionPreview>,
416 pub execution_preview_counter: u64,
417}
418
419impl SsmState {
420 pub fn new(account_id: &str, region: &str) -> Self {
421 let mut state = Self {
422 account_id: account_id.to_string(),
423 region: region.to_string(),
424 parameters: BTreeMap::new(),
425 documents: BTreeMap::new(),
426 commands: Vec::new(),
427 maintenance_windows: HashMap::new(),
428 patch_baselines: HashMap::new(),
429 patch_groups: Vec::new(),
430 associations: HashMap::new(),
431 ops_items: HashMap::new(),
432 resource_policies: Vec::new(),
433 service_settings: HashMap::new(),
434 default_patch_baseline_id: None,
435 ops_item_counter: 0,
436 maintenance_window_executions: Vec::new(),
437 inventory_entries: HashMap::new(),
438 inventory_deletions: Vec::new(),
439 compliance_items: Vec::new(),
440 resource_data_syncs: HashMap::new(),
441 mw_execution_counter: 0,
442 inventory_deletion_counter: 0,
443 ops_item_related_items: Vec::new(),
444 ops_item_related_item_counter: 0,
445 ops_item_events: Vec::new(),
446 ops_metadata: HashMap::new(),
447 automation_executions: HashMap::new(),
448 automation_execution_counter: 0,
449 sessions: HashMap::new(),
450 session_counter: 0,
451 activations: HashMap::new(),
452 activation_counter: 0,
453 managed_instances: HashMap::new(),
454 execution_previews: HashMap::new(),
455 execution_preview_counter: 0,
456 };
457 state.seed_defaults();
458 state
459 }
460
461 pub fn reset(&mut self) {
462 self.parameters.clear();
463 self.documents.clear();
464 self.commands.clear();
465 self.maintenance_windows.clear();
466 self.patch_baselines.clear();
467 self.patch_groups.clear();
468 self.associations.clear();
469 self.ops_items.clear();
470 self.resource_policies.clear();
471 self.service_settings.clear();
472 self.default_patch_baseline_id = None;
473 self.ops_item_counter = 0;
474 self.maintenance_window_executions.clear();
475 self.inventory_entries.clear();
476 self.inventory_deletions.clear();
477 self.compliance_items.clear();
478 self.resource_data_syncs.clear();
479 self.mw_execution_counter = 0;
480 self.inventory_deletion_counter = 0;
481 self.ops_item_related_items.clear();
482 self.ops_item_related_item_counter = 0;
483 self.ops_item_events.clear();
484 self.ops_metadata.clear();
485 self.automation_executions.clear();
486 self.automation_execution_counter = 0;
487 self.sessions.clear();
488 self.session_counter = 0;
489 self.activations.clear();
490 self.activation_counter = 0;
491 self.managed_instances.clear();
492 self.execution_previews.clear();
493 self.execution_preview_counter = 0;
494 self.seed_defaults();
495 }
496
497 fn seed_defaults(&mut self) {
498 let now = chrono::Utc::now();
499
500 let regions: &[(&str, &str)] = &[
502 ("af-south-1", "Africa (Cape Town)"),
503 ("ap-east-1", "Asia Pacific (Hong Kong)"),
504 ("ap-northeast-1", "Asia Pacific (Tokyo)"),
505 ("ap-northeast-2", "Asia Pacific (Seoul)"),
506 ("ap-northeast-3", "Asia Pacific (Osaka)"),
507 ("ap-south-1", "Asia Pacific (Mumbai)"),
508 ("ap-south-2", "Asia Pacific (Hyderabad)"),
509 ("ap-southeast-1", "Asia Pacific (Singapore)"),
510 ("ap-southeast-2", "Asia Pacific (Sydney)"),
511 ("ap-southeast-3", "Asia Pacific (Jakarta)"),
512 ("ca-central-1", "Canada (Central)"),
513 ("eu-central-1", "Europe (Frankfurt)"),
514 ("eu-central-2", "Europe (Zurich)"),
515 ("eu-north-1", "Europe (Stockholm)"),
516 ("eu-south-1", "Europe (Milan)"),
517 ("eu-south-2", "Europe (Spain)"),
518 ("eu-west-1", "Europe (Ireland)"),
519 ("eu-west-2", "Europe (London)"),
520 ("eu-west-3", "Europe (Paris)"),
521 ("me-central-1", "Middle East (UAE)"),
522 ("me-south-1", "Middle East (Bahrain)"),
523 ("sa-east-1", "South America (Sao Paulo)"),
524 ("us-east-1", "US East (N. Virginia)"),
525 ("us-east-2", "US East (Ohio)"),
526 ("us-west-1", "US West (N. California)"),
527 ("us-west-2", "US West (Oregon)"),
528 ];
529
530 for (region_code, long_name) in regions {
531 let base_path = format!("/aws/service/global-infrastructure/regions/{region_code}");
532 self.insert_default_param(&base_path, region_code, now);
533 self.insert_default_param(&format!("{base_path}/longName"), long_name, now);
534 self.insert_default_param(&format!("{base_path}/domain"), "amazonaws.com", now);
535 self.insert_default_param(&format!("{base_path}/geolocationRegion"), region_code, now);
536 let country = match region_code.split('-').next().unwrap_or("") {
537 "us" => "US",
538 "eu" => "DE",
539 "ap" => "JP",
540 "sa" => "BR",
541 "ca" => "CA",
542 "me" => "BH",
543 "af" => "ZA",
544 "il" => "IL",
545 _ => "US",
546 };
547 self.insert_default_param(&format!("{base_path}/geolocationCountry"), country, now);
548 self.insert_default_param(&format!("{base_path}/partition"), "aws", now);
549 }
550
551 let services = [
553 "acm",
554 "apigateway",
555 "autoscaling",
556 "cloudformation",
557 "cloudfront",
558 "cloudwatch",
559 "codebuild",
560 "codecommit",
561 "codedeploy",
562 "dynamodb",
563 "ec2",
564 "ecr",
565 "ecs",
566 "eks",
567 "elasticache",
568 "elasticbeanstalk",
569 "elasticloadbalancing",
570 "es",
571 "events",
572 "firehose",
573 "iam",
574 "kinesis",
575 "kms",
576 "lambda",
577 "logs",
578 "rds",
579 "redshift",
580 "route53",
581 "s3",
582 "ses",
583 "sns",
584 "sqs",
585 "ssm",
586 "sts",
587 ];
588 for svc in &services {
589 let name = format!("/aws/service/global-infrastructure/services/{svc}");
590 self.insert_default_param(&name, svc, now);
591 }
592
593 let ami_names = [
595 "al2023-ami-kernel-default-x86_64",
596 "al2023-ami-kernel-default-arm64",
597 "al2023-ami-minimal-kernel-default-x86_64",
598 "al2023-ami-minimal-kernel-default-arm64",
599 "amzn2-ami-hvm-x86_64-gp2",
600 "amzn2-ami-hvm-arm64-gp2",
601 "amzn2-ami-kernel-5.10-hvm-x86_64-gp2",
602 "amzn2-ami-kernel-5.10-hvm-arm64-gp2",
603 "amzn2-ami-minimal-hvm-x86_64-ebs",
604 "amzn2-ami-minimal-hvm-arm64-ebs",
605 ];
606
607 for (i, ami_name) in ami_names.iter().enumerate() {
609 let name = format!("/aws/service/ami-amazon-linux-latest/{ami_name}");
610 let ami_id = format!(
611 "ami-{:017x}",
612 {
614 let mut h: u64 = 0xcbf29ce484222325;
615 for b in self.region.as_bytes() {
616 h ^= *b as u64;
617 h = h.wrapping_mul(0x100000001b3);
618 }
619 for b in ami_name.as_bytes() {
620 h ^= *b as u64;
621 h = h.wrapping_mul(0x100000001b3);
622 }
623 h.wrapping_add(i as u64)
624 }
625 );
626 self.insert_default_param(&name, &ami_id, now);
627 }
628 }
629
630 fn insert_default_param(&mut self, name: &str, value: &str, now: DateTime<Utc>) {
631 let arn = if name.starts_with('/') {
632 format!(
633 "arn:aws:ssm:{}:{}:parameter{}",
634 self.region, self.account_id, name
635 )
636 } else {
637 format!(
638 "arn:aws:ssm:{}:{}:parameter/{}",
639 self.region, self.account_id, name
640 )
641 };
642 self.parameters.insert(
643 name.to_string(),
644 SsmParameter {
645 name: name.to_string(),
646 value: value.to_string(),
647 param_type: "String".to_string(),
648 version: 1,
649 arn,
650 last_modified: now,
651 history: Vec::new(),
652 tags: HashMap::new(),
653 labels: HashMap::new(),
654 description: None,
655 allowed_pattern: None,
656 key_id: None,
657 data_type: "text".to_string(),
658 tier: "Standard".to_string(),
659 policies: None,
660 },
661 );
662 }
663}
664
665#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
666pub struct MaintenanceWindowExecution {
667 pub window_execution_id: String,
668 pub window_id: String,
669 pub status: String,
670 pub start_time: DateTime<Utc>,
671 pub end_time: Option<DateTime<Utc>>,
672 pub tasks: Vec<MaintenanceWindowExecutionTask>,
673}
674
675#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
676pub struct MaintenanceWindowExecutionTask {
677 pub task_execution_id: String,
678 pub window_execution_id: String,
679 pub task_arn: String,
680 pub task_type: String,
681 pub status: String,
682 pub start_time: DateTime<Utc>,
683 pub end_time: Option<DateTime<Utc>>,
684 pub invocations: Vec<MaintenanceWindowExecutionTaskInvocation>,
685}
686
687#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
688pub struct MaintenanceWindowExecutionTaskInvocation {
689 pub invocation_id: String,
690 pub task_execution_id: String,
691 pub window_execution_id: String,
692 pub execution_id: Option<String>,
693 pub status: String,
694 pub start_time: DateTime<Utc>,
695 pub end_time: Option<DateTime<Utc>>,
696 pub parameters: Option<String>,
697 pub owner_information: Option<String>,
698 pub window_target_id: Option<String>,
699 pub status_details: Option<String>,
700}
701
702#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
703pub struct InventoryItem {
704 pub type_name: String,
705 pub schema_version: String,
706 pub capture_time: String,
707 pub content: Vec<HashMap<String, String>>,
708 pub content_hash: Option<String>,
709 pub context: Option<HashMap<String, String>>,
710}
711
712#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
713pub struct InventoryEntry {
714 pub instance_id: String,
715 pub items: Vec<InventoryItem>,
716}
717
718#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
719pub struct InventoryDeletion {
720 pub deletion_id: String,
721 pub type_name: String,
722 pub deletion_start_time: DateTime<Utc>,
723 pub last_status: String,
724 pub last_status_message: String,
725 pub deletion_summary: serde_json::Value,
726 pub last_status_update_time: DateTime<Utc>,
727}
728
729#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
730pub struct ComplianceItem {
731 pub resource_id: String,
732 pub resource_type: String,
733 pub compliance_type: String,
734 pub severity: String,
735 pub status: String,
736 pub title: Option<String>,
737 pub id: Option<String>,
738 pub details: HashMap<String, String>,
739 pub execution_summary: serde_json::Value,
740}
741
742#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
743pub struct ResourceDataSync {
744 pub sync_name: String,
745 pub sync_type: Option<String>,
746 pub sync_source: Option<serde_json::Value>,
747 pub s3_destination: Option<serde_json::Value>,
748 pub created_date: DateTime<Utc>,
749 pub last_sync_time: Option<DateTime<Utc>>,
750 pub last_successful_sync_time: Option<DateTime<Utc>>,
751 pub last_status: String,
752 pub sync_last_modified_time: DateTime<Utc>,
753}
754
755pub type SharedSsmState = Arc<RwLock<fakecloud_core::multi_account::MultiAccountState<SsmState>>>;
756
757impl fakecloud_core::multi_account::AccountState for SsmState {
758 fn new_for_account(account_id: &str, region: &str, _endpoint: &str) -> Self {
759 Self::new(account_id, region)
760 }
761}
762
763#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
766pub struct SsmSnapshot {
767 pub schema_version: u32,
768 #[serde(default)]
769 pub accounts: Option<fakecloud_core::multi_account::MultiAccountState<SsmState>>,
770 #[serde(default)]
771 pub state: Option<SsmState>,
772}
773
774pub const SSM_SNAPSHOT_SCHEMA_VERSION: u32 = 2;
775
776#[cfg(test)]
777mod tests {
778 use super::*;
779
780 #[test]
781 fn new_initializes() {
782 let state = SsmState::new("123456789012", "us-east-1");
783 assert_eq!(state.account_id, "123456789012");
784 assert_eq!(state.region, "us-east-1");
785 }
786
787 #[test]
788 fn new_seeds_default_region_parameters() {
789 let state = SsmState::new("123456789012", "us-east-1");
790 let region_key = "/aws/service/global-infrastructure/regions/us-east-1";
791 assert!(state.parameters.contains_key(region_key));
792 let long_key = format!("{region_key}/longName");
793 assert!(state.parameters.contains_key(&long_key));
794 }
795
796 #[test]
797 fn new_seeds_default_service_parameters() {
798 let state = SsmState::new("123456789012", "us-east-1");
799 let key = "/aws/service/global-infrastructure/services/lambda";
800 assert!(state.parameters.contains_key(key));
801 }
802
803 #[test]
804 fn reset_reseeds_defaults() {
805 let mut state = SsmState::new("123456789012", "us-east-1");
806 state.parameters.clear();
807 state.documents.clear();
808 state.ops_item_counter = 42;
809 state.reset();
810 let key = "/aws/service/global-infrastructure/services/s3";
812 assert!(state.parameters.contains_key(key));
813 assert_eq!(state.ops_item_counter, 0);
814 }
815
816 #[test]
817 fn reset_clears_ephemeral_counters() {
818 let mut state = SsmState::new("123456789012", "us-east-1");
819 state.mw_execution_counter = 7;
820 state.automation_execution_counter = 3;
821 state.session_counter = 9;
822 state.activation_counter = 2;
823 state.execution_preview_counter = 5;
824 state.reset();
825 assert_eq!(state.mw_execution_counter, 0);
826 assert_eq!(state.automation_execution_counter, 0);
827 assert_eq!(state.session_counter, 0);
828 assert_eq!(state.activation_counter, 0);
829 assert_eq!(state.execution_preview_counter, 0);
830 }
831}