-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathconfigurator.html
More file actions
7009 lines (6619 loc) · 493 KB
/
configurator.html
File metadata and controls
7009 lines (6619 loc) · 493 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!-- Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -->
<!-- SPDX-License-Identifier: MIT-0 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MAP 2.0 Auto-Tagger Configurator</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, 'Amazon Ember', 'Helvetica Neue', sans-serif;
background: #f2f3f3;
color: #16191f;
line-height: 1.6;
}
.header {
background: #232f3e;
color: white;
padding: 16px 32px;
display: flex;
align-items: center;
gap: 12px;
}
.header h1 { font-size: 18px; font-weight: 600; }
.header .badge {
background: #ff9900;
color: #16191f;
font-size: 11px;
font-weight: 700;
padding: 2px 8px;
border-radius: 4px;
}
.header .version {
margin-left: auto;
font-size: 12px;
opacity: 0.7;
}
.container { max-width: 800px; margin: 32px auto; padding: 0 24px; }
.card {
background: white;
border: 1px solid #d5dbdb;
border-radius: 8px;
padding: 24px;
margin-bottom: 20px;
}
.card h2 { font-size: 18px; margin-bottom: 4px; color: #16191f; }
.card .subtitle { font-size: 13px; color: #687078; margin-bottom: 20px; }
.form-group { margin-bottom: 16px; }
label { display: block; font-size: 14px; font-weight: 600; margin-bottom: 4px; }
label .hint { font-weight: 400; color: #687078; font-size: 12px; }
input[type="text"], input[type="date"], input[type="email"], select {
width: 100%;
padding: 8px 12px;
font-size: 14px;
border: 1px solid #aab7b8;
border-radius: 4px;
background: white;
}
input:focus, select:focus { outline: none; border-color: #0073bb; box-shadow: 0 0 0 1px #0073bb; }
input.error { border-color: #d13212; }
.error-msg { color: #d13212; font-size: 12px; margin-top: 2px; display: none; }
.entry-row { display: flex; gap: 8px; margin-bottom: 8px; align-items: center; }
.entry-row input { flex: 1; }
.entry-row input.account-label { flex: 1.5; }
.btn-remove {
background: none; border: 1px solid #d5dbdb; border-radius: 4px;
padding: 6px 10px; cursor: pointer; color: #d13212; font-size: 18px; line-height: 1;
}
.btn-remove:hover { background: #fdf3f1; }
.btn-add {
background: none; border: 1px dashed #aab7b8; border-radius: 4px;
padding: 8px 16px; cursor: pointer; color: #0073bb; font-size: 13px; width: 100%;
}
.btn-add:hover { background: #f2f8fd; border-color: #0073bb; }
.actions { display: flex; gap: 12px; margin-top: 24px; }
.btn-primary {
background: #ff9900; color: #16191f; border: none; border-radius: 4px;
padding: 10px 24px; font-size: 14px; font-weight: 600; cursor: pointer;
}
.btn-primary:hover { background: #ec7211; }
.btn-secondary {
background: white; color: #16191f; border: 1px solid #aab7b8;
border-radius: 4px; padding: 10px 24px; font-size: 14px; cursor: pointer;
}
.btn-secondary:hover { background: #fafafa; }
.preview-box {
background: #1b2631; color: #89d185; font-family: 'SF Mono', 'Fira Code', monospace;
font-size: 12px; padding: 16px; border-radius: 4px; overflow-x: auto;
white-space: pre; max-height: 400px; overflow-y: auto; margin-top: 16px;
}
.info-box {
background: #f2f8fd; border-left: 4px solid #0073bb;
padding: 12px 16px; font-size: 13px; margin-bottom: 20px; border-radius: 0 4px 4px 0;
}
.warn-box {
background: #fef8f0; border-left: 4px solid #ff9900;
padding: 12px 16px; font-size: 13px; margin-top: 12px; border-radius: 0 4px 4px 0;
}
.success-box {
background: #f2fcf3; border-left: 4px solid #1d8102;
padding: 12px 16px; font-size: 13px; margin-top: 12px; border-radius: 0 4px 4px 0;
}
.step-indicator { display: flex; gap: 0; margin-bottom: 24px; }
.step {
flex: 1; text-align: center; padding: 12px 8px; font-size: 13px; font-weight: 600;
background: #f2f3f3; border-bottom: 3px solid #d5dbdb; color: #687078;
}
.step.active { background: #f2f8fd; border-bottom-color: #0073bb; color: #0073bb; }
.step.done { background: #f2fcf3; border-bottom-color: #1d8102; color: #1d8102; }
.cost-note {
background: #f2fcf3; border-left: 4px solid #1d8102;
padding: 12px 16px; font-size: 13px; margin-top: 16px; border-radius: 0 4px 4px 0;
}
.option-card {
border: 2px solid #d5dbdb; border-radius: 8px; padding: 16px; margin-bottom: 12px; cursor: pointer;
}
.option-card.selected { border-color: #0073bb; background: #f2f8fd; }
.option-card h3 { font-size: 14px; margin-bottom: 4px; }
.option-card p { font-size: 13px; color: #687078; margin: 0; }
.option-card .tag {
display: inline-block; font-size: 11px; font-weight: 600; padding: 2px 6px;
border-radius: 3px; margin-left: 8px;
}
.tag-recommended { background: #f2fcf3; color: #1d8102; }
.tag-advanced { background: #fef8f0; color: #df6e0e; }
.tag-noprereq { background: #f2f8fd; color: #0073bb; }
.hidden { display: none; }
.section-divider { border-top: 1px solid #eaeded; margin: 16px 0; }
.coverage-grid {
display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 12px;
}
.coverage-item {
font-size: 12px; padding: 6px 10px; background: #f2fcf3;
border-radius: 4px; color: #1d8102;
}
.coverage-item.limited { background: #fef8f0; color: #df6e0e; }
.steps-list { counter-reset: steps; }
.steps-list li {
counter-increment: steps;
list-style: none;
padding: 8px 0 8px 32px;
position: relative;
border-bottom: 1px solid #eaeded;
font-size: 13px;
}
.steps-list li:last-child { border-bottom: none; }
.steps-list li::before {
content: counter(steps);
position: absolute;
left: 0;
top: 8px;
width: 22px;
height: 22px;
background: #0073bb;
color: white;
border-radius: 50%;
font-size: 11px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
}
code {
background: #f2f3f3; padding: 1px 5px; border-radius: 3px;
font-family: 'SF Mono', monospace; font-size: 12px;
}
.lang-flags { display: flex; align-items: center; gap: 4px; margin-left: auto; }
.lang-flag {
font-size: 22px; cursor: pointer; opacity: 0.5;
border-radius: 4px; padding: 2px 4px; transition: opacity 0.15s, background 0.15s;
line-height: 1;
}
.lang-flag:hover { opacity: 1; background: rgba(255,255,255,0.15); }
.lang-flag.active { opacity: 1; background: rgba(255,255,255,0.2); outline: 2px solid rgba(255,255,255,0.6); }
.mode-card {
border: 2px solid #d5dbdb; border-radius: 8px; padding: 24px 28px;
margin-bottom: 16px; cursor: pointer; display: flex; align-items: flex-start; gap: 20px;
transition: border-color 0.15s, background 0.15s;
}
.mode-card:hover { border-color: #0073bb; background: #f2f8fd; }
.mode-card .mode-icon {
font-size: 32px; line-height: 1; flex-shrink: 0; margin-top: 2px;
}
.mode-card .mode-body h3 { font-size: 16px; font-weight: 600; margin-bottom: 6px; color: #16191f; }
.mode-card .mode-body p { font-size: 13px; color: #687078; margin: 0; line-height: 1.5; }
.mode-card .mode-body .mode-note {
display: inline-block; font-size: 11px; color: #687078;
background: #f2f3f3; border: 1px solid #d5dbdb;
border-radius: 3px; padding: 2px 6px; margin-top: 8px;
}
.mode-card:hover .mode-body h3 { color: #0073bb; }
</style>
</head>
<body>
<div class="header">
<h1 data-i18n="ui_title">MAP 2.0 Auto-Tagger Configurator</h1>
<span class="badge">AWS SAMPLE</span>
<div class="lang-flags">
<span class="lang-flag active" id="lang-en" onclick="setLanguage('en')" title="English">🇺🇸</span>
<span class="lang-flag" id="lang-ko" onclick="setLanguage('ko')" title="한국어">🇰🇷</span>
<span class="lang-flag" id="lang-ja" onclick="setLanguage('ja')" title="日本語">🇯🇵</span>
<span class="lang-flag" id="lang-zh" onclick="setLanguage('zh')" title="中文简体">🇨🇳</span>
<span class="lang-flag" id="lang-id" onclick="setLanguage('id')" title="Bahasa Indonesia">🇮🇩</span>
<span class="lang-flag" id="lang-th" onclick="setLanguage('th')" title="ภาษาไทย">🇹🇭</span>
<span class="lang-flag" id="lang-vi" onclick="setLanguage('vi')" title="Tiếng Việt">🇻🇳</span>
</div>
</div>
<div class="container">
<!-- Landing: mode selection -->
<div id="landing">
<div class="info-box" style="background:#fff8e1;border-left:4px solid #f9a825;font-size:13px;margin-top:8px;">
<strong data-i18n="ui_sample_notice_title">Sample Code Notice:</strong> <span data-i18n="ui_sample_notice_body">This is sample code for non-production usage. You are responsible for testing, securing, and optimizing this solution to meet your organization's security, regulatory, and compliance requirements before deployment. Deploying this solution may incur AWS charges for AWS Lambda, Amazon EventBridge, Amazon CloudWatch, Amazon SNS, Amazon SQS, and AWS Systems Manager. See the</span> <a href="https://aws.amazon.com/security/shared-responsibility/" target="_blank" rel="noopener" data-i18n="ui_shared_responsibility">AWS Shared Responsibility Model</a>.
</div>
<div class="info-box">
<span data-i18n="ui_main_desc1">Generates a CloudFormation template that auto-tags new AWS resources with</span> <code>map-migrated</code> <span data-i18n="ui_main_desc2">within</span> <strong data-i18n="ui_main_desc3">typically 60-90 seconds (up to 15 minutes during high-volume activity)</strong> <span data-i18n="ui_main_desc4">of creation.</span>
<span data-i18n="ui_main_desc5">Covers</span> <strong>140+ <span data-i18n="ui_main_desc6">resource types across all major AWS services (CT org E2E tested).</span></strong>
<span data-i18n="ui_main_desc7">Customer cost:</span> <strong>< $2/month</strong>.
</div>
<div class="card">
<h2 style="margin-bottom:6px;" data-i18n="ui_landing_title">What would you like to do?</h2>
<p class="subtitle" style="margin-bottom:24px;" data-i18n="ui_landing_subtitle">Select an option to get started</p>
<div class="mode-card" onclick="selectMode('deploy')">
<div class="mode-icon">🚀</div>
<div class="mode-body">
<h3 data-i18n="ui_mode_deploy_title">Deploy new MAP Auto-Tagger</h3>
<p data-i18n="ui_mode_deploy_desc">Generate a deployment script for a new MAP 2.0 engagement. Works for single-account and multi-account (AWS Organizations) deployments.</p>
</div>
</div>
<div class="mode-card" onclick="selectMode('edit')">
<div class="mode-icon">✏️</div>
<div class="mode-body">
<h3 data-i18n="ui_mode_edit_title">Edit existing deployment</h3>
<p data-i18n="ui_mode_edit_desc">Add or remove AWS accounts from scope on an already-deployed MAP Auto-Tagger. Generates an update script — no full redeployment needed.</p>
<span class="mode-note" data-i18n="ui_mode_edit_note">Multi-account (AWS Organizations) deployments only</span>
</div>
</div>
<div class="mode-card" onclick="selectMode('update')">
<div class="mode-icon">🔄</div>
<div class="mode-body">
<h3 data-i18n="ui_mode_update_title">Update to latest template version</h3>
<p data-i18n="ui_mode_update_desc">Upgrade Lambda code, IAM policy, and EventBridge rules to the current template version. Scope configuration (accounts, VPCs, agreement date) is preserved. Auto-detects single-account and multi-account deployments.</p>
</div>
</div>
</div>
</div>
<!-- Configurator (deploy flow) — hidden until mode selected -->
<div id="deploy-flow" class="hidden">
<div class="step-indicator">
<div class="step active" id="step1-indicator" data-i18n="ui_step1">1. Configure</div>
<div class="step" id="step2-indicator" data-i18n="ui_step2">2. Review</div>
<div class="step" id="step3-indicator" data-i18n="ui_step3">3. Download</div>
</div>
<div class="info-box" style="background:#fff8e1;border-left:4px solid #f9a825;font-size:13px;">
<strong data-i18n="ui_sample_notice_title">Sample Code Notice:</strong> <span data-i18n="ui_sample_notice_body">This is sample code for non-production usage. You are responsible for testing, securing, and optimizing this solution to meet your organization's security, regulatory, and compliance requirements before deployment. Deploying this solution may incur AWS charges for AWS Lambda, Amazon EventBridge, Amazon CloudWatch, Amazon SNS, Amazon SQS, and AWS Systems Manager. See the</span> <a href="https://aws.amazon.com/security/shared-responsibility/" target="_blank" rel="noopener" data-i18n="ui_shared_responsibility">AWS Shared Responsibility Model</a>.
</div>
<div class="info-box">
<span data-i18n="ui_main_desc1">Generates a CloudFormation template that auto-tags new AWS resources with</span> <code>map-migrated</code> <span data-i18n="ui_main_desc2">within</span> <strong data-i18n="ui_main_desc3">typically 60-90 seconds (up to 15 minutes during high-volume activity)</strong> <span data-i18n="ui_main_desc4">of creation.</span>
<span data-i18n="ui_main_desc5">Covers</span> <strong>140+ <span data-i18n="ui_main_desc6">resource types across all major AWS services (CT org E2E tested).</span></strong>
<span data-i18n="ui_main_desc7">Customer cost:</span> <strong>< $2/month</strong>.
</div>
<!-- Step 1: Configure -->
<div id="step1">
<div class="card">
<h2 data-i18n="ui_card_deal_title">MAP 2.0 Deal Details</h2>
<p class="subtitle" data-i18n="ui_card_deal_subtitle">Enter the MAP agreement details from AWS Investments</p>
<div class="form-group">
<label><span data-i18n="ui_customer_name">Customer Name</span> <span class="hint" data-i18n="ui_customer_name_hint">(used for filename generation only)</span></label>
<input type="text" id="customerName" data-i18n-placeholder="ui_customer_name_placeholder" placeholder="e.g., Acme Corp">
</div>
<div class="form-group">
<label><span data-i18n="ui_mpe_tag">MAP Engagement ID</span> <span class="hint" data-i18n="ui_mpe_hint">(10-character alphanumeric code from AWS Investments — e.g., A1B2C3D4E5)</span></label>
<div style="display:flex;align-items:center;gap:0;">
<span style="padding:8px 10px;background:#f2f3f3;border:1px solid #aab7b8;border-right:none;border-radius:4px 0 0 4px;font-size:14px;color:#687078;white-space:nowrap;">mig</span>
<input type="text" id="mpeId" placeholder="A1B2C3D4E5" maxlength="10" style="border-radius:0 4px 4px 0;" oninput="this.value=this.value.toUpperCase().replace(/[^A-Z0-9]/g,'')">
</div>
<div class="error-msg" id="mpeId-error" data-i18n="err_mpe_invalid">MAP Engagement ID must be exactly 10 characters, uppercase English letters (A–Z) and digits (0–9) only, with at least one letter and one number</div>
</div>
<div class="form-group">
<label><span data-i18n="ui_agree_start">Agreement Start Date</span> <span class="hint" data-i18n="ui_agree_start_hint">(Date to start tagging newly created MAP 2.0 resources)</span></label>
<input type="date" id="agreementDate" min="1900-01-01" max="9999-12-31">
<div class="error-msg" id="agreementDate-error" data-i18n="err_date_required">Agreement start date is required</div>
</div>
<div class="form-group">
<label><span data-i18n="ui_agree_end">Agreement End Date</span> <span class="hint" data-i18n="ui_agree_end_hint">(Date to stop tagging newly created resources)</span></label>
<input type="date" id="agreementEndDate" min="1900-01-01" max="9999-12-31">
<div class="error-msg" id="agreementEndDate-error" data-i18n="err_date_end">Agreement end date is required and must be after start date</div>
</div>
<div class="form-group">
<label><span data-i18n="ui_alert_email">Alert Email</span> <span class="hint" data-i18n="ui_alert_email_hint">(optional — email to receive tagging failure alerts, ex. customer ops team)</span></label>
<input type="email" id="alertEmail" placeholder="e.g., ops@customer.com">
<div class="hint" style="margin-top: 4px;" data-i18n="ui_email_confirm_hint">Customer must confirm the AWS subscription email after deployment.</div>
</div>
</div>
<div class="card">
<h2 data-i18n="ui_deploy_mode">Deployment Mode</h2>
<p class="subtitle" data-i18n="ui_deploy_mode_subtitle">How many AWS accounts are used for migration?</p>
<div class="option-card" id="opt-single" onclick="selectDeployMode('single')">
<h3>
<input type="radio" name="deployMode" value="single">
<span data-i18n="ui_single_title">Single Account</span>
</h3>
<p data-i18n="ui_single_desc">Migration workloads in one AWS account. Deploy the stack once. Done in 2 minutes.</p>
</div>
<!-- Single-account: region + VPC scope -->
<div id="single-account-options" class="hidden">
<div class="section-divider"></div>
<div class="form-group">
<label><span data-i18n="ui_deployment_regions">Deployment Regions</span> <span class="hint" data-i18n="ui_deployment_regions_hint">(regions where the customer creates resources)</span></label>
<div id="singleRegionList">
<div class="entry-row">
<select class="region-select">
<option value="">Select a region...</option>
<option value="us-east-1">US East (N. Virginia) — us-east-1</option>
<option value="us-east-2">US East (Ohio) — us-east-2</option>
<option value="us-west-1">US West (N. California) — us-west-1</option>
<option value="us-west-2">US West (Oregon) — us-west-2</option>
<option value="af-south-1">Africa (Cape Town) — af-south-1</option>
<option value="ap-east-1">Asia Pacific (Hong Kong) — ap-east-1</option>
<option value="ap-east-2">Asia Pacific (Taipei) — ap-east-2</option>
<option value="ap-northeast-1">Asia Pacific (Tokyo) — ap-northeast-1</option>
<option value="ap-northeast-2">Asia Pacific (Seoul) — ap-northeast-2</option>
<option value="ap-northeast-3">Asia Pacific (Osaka) — ap-northeast-3</option>
<option value="ap-south-1">Asia Pacific (Mumbai) — ap-south-1</option>
<option value="ap-south-2">Asia Pacific (Hyderabad) — ap-south-2</option>
<option value="ap-southeast-1">Asia Pacific (Singapore) — ap-southeast-1</option>
<option value="ap-southeast-2">Asia Pacific (Sydney) — ap-southeast-2</option>
<option value="ap-southeast-3">Asia Pacific (Jakarta) — ap-southeast-3</option>
<option value="ap-southeast-4">Asia Pacific (Melbourne) — ap-southeast-4</option>
<option value="ap-southeast-5">Asia Pacific (Malaysia) — ap-southeast-5</option>
<option value="ap-southeast-6">Asia Pacific (New Zealand) — ap-southeast-6</option>
<option value="ap-southeast-7">Asia Pacific (Thailand) — ap-southeast-7</option>
<option value="ca-central-1">Canada (Central) — ca-central-1</option>
<option value="ca-west-1">Canada West (Calgary) — ca-west-1</option>
<option value="eu-central-1">Europe (Frankfurt) — eu-central-1</option>
<option value="eu-central-2">Europe (Zurich) — eu-central-2</option>
<option value="eu-north-1">Europe (Stockholm) — eu-north-1</option>
<option value="eu-south-1">Europe (Milan) — eu-south-1</option>
<option value="eu-south-2">Europe (Spain) — eu-south-2</option>
<option value="eu-west-1">Europe (Ireland) — eu-west-1</option>
<option value="eu-west-2">Europe (London) — eu-west-2</option>
<option value="eu-west-3">Europe (Paris) — eu-west-3</option>
<option value="il-central-1">Israel (Tel Aviv) — il-central-1</option>
<option value="me-central-1">Middle East (UAE) — me-central-1</option>
<option value="me-south-1">Middle East (Bahrain) — me-south-1</option>
<option value="mx-central-1">Mexico (Central) — mx-central-1</option>
<option value="sa-east-1">South America (Sao Paulo) — sa-east-1</option>
</select>
<button class="btn-remove" onclick="removeEntry(this)" title="Remove">×</button>
</div>
</div>
<button class="btn-add" onclick="addSingleRegion()" data-i18n="ui_add_region">+ Add another region</button>
</div>
<div class="section-divider"></div>
<div class="form-group">
<label><span data-i18n="ui_tagging_scope">Tagging Scope</span> <span class="hint" data-i18n="ui_optional">(optional)</span></label>
<div class="hint" style="margin-bottom: 8px;" data-i18n="ui_vpc_scope_hint">By default all new resources in the account are tagged. Use VPC scoping if migration and non-migration workloads share the same account.</div>
</div>
<div class="form-group">
<label>
<input type="checkbox" id="useVpcScope" onchange="toggleVpcScope()">
<span data-i18n="ui_vpc_limit">Limit tagging to specific VPCs only</span>
</label>
</div>
<div id="vpc-scope-fields" class="hidden">
<div class="form-group">
<label>
<input type="checkbox" id="tagNonVpcServices" checked>
<span data-i18n="ui_tag_non_vpc">Also tag non-VPC resources (S3, DynamoDB, Lambda, SNS, SQS, etc.)</span>
</label>
<div class="hint" style="margin-left: 22px; margin-top: 2px;" data-i18n="ui_tag_non_vpc_hint">
These services have no VPC association. Check to tag them all regardless of VPC scope (recommended for MAP eligibility).
</div>
</div>
<label><span data-i18n="ui_vpc_ids">VPC IDs</span> <span class="hint" data-i18n="ui_vpc_ids_hint">(only VPC-bound resources in these VPCs are tagged)</span></label>
<div id="vpcList">
<div class="entry-row">
<input type="text" class="vpc-input" placeholder="vpc-0abc1234def56789">
<button class="btn-remove" onclick="removeEntry(this)" title="Remove">×</button>
</div>
</div>
<button class="btn-add" onclick="addVpc()" data-i18n="ui_add_vpc">+ Add another VPC</button>
<div class="error-msg" id="vpc-error" data-i18n="err_vpc_required">Please enter at least one VPC ID.</div>
</div>
</div>
<div class="option-card" id="opt-multi" onclick="selectDeployMode('multi')">
<h3>
<input type="radio" name="deployMode" value="multi">
<span data-i18n="ui_multi_title">Multiple Accounts (AWS Organizations)</span>
</h3>
<p data-i18n="ui_multi_desc">Customer has multiple linked accounts under an AWS Organization. One StackSet command deploys to all accounts simultaneously.</p>
</div>
<!-- Multi-account options -->
<div id="multi-account-options" class="hidden">
<div class="section-divider"></div>
<!-- Account Scoping -->
<div id="stackset-options" style="margin-top: 12px;">
<div class="form-group">
<div class="info-box" style="margin-bottom:10px;" data-i18n="ui_stackset_note">
The Lambda is deployed to every account in your org via the root OU. By default, all accounts' resources are tagged. You can optionally limit tagging to specific accounts below.
</div>
<div class="warn-box" style="margin-bottom:10px;font-size:12px;" data-i18n="ui_stackset_bucket_note">
ⓘ This deployment creates a temporary S3 bucket (<code>auto-map-tagger-{account-id}</code>) to stage CloudFormation templates for the StackSet. The bucket is retained after deployment as the StackSet requires it to deploy to new accounts that join your organization in the future.
</div>
<label>
<input type="checkbox" id="useAccountScope" onchange="toggleAccountScope()">
<span data-i18n="ui_account_limit">Limit tagging to specific accounts only</span>
</label>
</div>
<div id="account-scope-fields" class="hidden">
<div id="stacksetAccountList">
<div class="entry-row">
<input type="text" class="stackset-account-id" placeholder="123456789012" maxlength="12">
<input type="text" class="stackset-account-label account-label" placeholder="e.g., Migration Account">
<button class="btn-remove" onclick="removeEntry(this)" title="Remove">×</button>
</div>
</div>
<button class="btn-add" onclick="addStacksetAccount()" data-i18n="ui_add_account">+ Add another account</button>
<div class="error-msg" id="account-scope-error" data-i18n="err_account_scope_required">Please enter at least one account ID.</div>
</div>
</div>
<!-- Deployment region -->
<div class="section-divider"></div>
<div class="form-group">
<label><span data-i18n="ui_deployment_regions">Deployment Regions</span> <span class="hint" data-i18n="ui_deployment_regions_hint">(regions where the customer creates resources)</span></label>
<div id="regionList">
<div class="entry-row">
<select class="region-select">
<option value="">Select a region...</option>
<option value="us-east-1">US East (N. Virginia) — us-east-1</option>
<option value="us-east-2">US East (Ohio) — us-east-2</option>
<option value="us-west-1">US West (N. California) — us-west-1</option>
<option value="us-west-2">US West (Oregon) — us-west-2</option>
<option value="af-south-1">Africa (Cape Town) — af-south-1</option>
<option value="ap-east-1">Asia Pacific (Hong Kong) — ap-east-1</option>
<option value="ap-east-2">Asia Pacific (Taipei) — ap-east-2</option>
<option value="ap-northeast-1">Asia Pacific (Tokyo) — ap-northeast-1</option>
<option value="ap-northeast-2">Asia Pacific (Seoul) — ap-northeast-2</option>
<option value="ap-northeast-3">Asia Pacific (Osaka) — ap-northeast-3</option>
<option value="ap-south-1">Asia Pacific (Mumbai) — ap-south-1</option>
<option value="ap-south-2">Asia Pacific (Hyderabad) — ap-south-2</option>
<option value="ap-southeast-1">Asia Pacific (Singapore) — ap-southeast-1</option>
<option value="ap-southeast-2">Asia Pacific (Sydney) — ap-southeast-2</option>
<option value="ap-southeast-3">Asia Pacific (Jakarta) — ap-southeast-3</option>
<option value="ap-southeast-4">Asia Pacific (Melbourne) — ap-southeast-4</option>
<option value="ap-southeast-5">Asia Pacific (Malaysia) — ap-southeast-5</option>
<option value="ap-southeast-6">Asia Pacific (New Zealand) — ap-southeast-6</option>
<option value="ap-southeast-7">Asia Pacific (Thailand) — ap-southeast-7</option>
<option value="ca-central-1">Canada (Central) — ca-central-1</option>
<option value="ca-west-1">Canada West (Calgary) — ca-west-1</option>
<option value="eu-central-1">Europe (Frankfurt) — eu-central-1</option>
<option value="eu-central-2">Europe (Zurich) — eu-central-2</option>
<option value="eu-north-1">Europe (Stockholm) — eu-north-1</option>
<option value="eu-south-1">Europe (Milan) — eu-south-1</option>
<option value="eu-south-2">Europe (Spain) — eu-south-2</option>
<option value="eu-west-1">Europe (Ireland) — eu-west-1</option>
<option value="eu-west-2">Europe (London) — eu-west-2</option>
<option value="eu-west-3">Europe (Paris) — eu-west-3</option>
<option value="il-central-1">Israel (Tel Aviv) — il-central-1</option>
<option value="me-central-1">Middle East (UAE) — me-central-1</option>
<option value="me-south-1">Middle East (Bahrain) — me-south-1</option>
<option value="mx-central-1">Mexico (Central) — mx-central-1</option>
<option value="sa-east-1">South America (Sao Paulo) — sa-east-1</option>
</select>
<button class="btn-remove" onclick="removeEntry(this)" title="Remove">×</button>
</div>
</div>
<button class="btn-add" onclick="addRegion()" data-i18n="ui_add_region">+ Add another region</button>
</div>
</div>
</div>
<!-- Backfill option -->
<div class="card">
<h2><span data-i18n="ui_backfill_title">One-Time Backfill</span> <span class="hint" style="font-weight:normal;font-size:13px;" data-i18n="ui_optional">(optional)</span></h2>
<label style="display:flex;align-items:flex-start;gap:10px;cursor:pointer;">
<input type="checkbox" id="includeBackfill" style="margin-top:3px;flex-shrink:0;">
<div>
<strong data-i18n="ui_backfill_strong">Tag existing resources created since agreement start date</strong>
<p class="hint" style="margin-top:4px;" data-i18n="ui_backfill_desc">Deploys a one-time Lambda that queries CloudTrail history from your agreement start date and tags all matching resources that were created before this solution was deployed. Runs automatically ~5 minutes after deployment. Results appear in CloudWatch Logs (map-auto-tagger-backfill).</p>
<p class="hint" style="margin-top:4px;color:#d13212;" data-i18n="ui_backfill_note">Note: CloudTrail history is limited to the last 90 days. Resources created before that window must be tagged manually.</p>
<p class="hint" id="backfill-scope-note" style="margin-top:4px;display:none;" data-i18n="ui_backfill_scope_note"></p>
</div>
</label>
</div>
<!-- Service coverage summary -->
<div class="card">
<h2 data-i18n="ui_coverage_title">What Gets Tagged</h2>
<p class="subtitle" data-i18n="ui_coverage_subtitle">140+ resource types with explicit handlers — verified across 9 accounts</p>
<div class="coverage-grid">
<div class="coverage-item">✅ Amazon EC2, AWS Lambda, Amazon ECS, Amazon EKS, Auto Scaling, Amazon EMR, EMR Serverless, Elastic Beanstalk, GameLift</div>
<div class="coverage-item">✅ Amazon S3, Amazon EFS, Amazon FSx, Amazon ECR, Amazon EBS Volumes & Snapshots, AMIs, AWS Backup, Storage Gateway</div>
<div class="coverage-item">✅ Amazon RDS (all engines + snapshots + replicas + Proxy), Amazon Aurora, Amazon Neptune, Amazon DynamoDB, DynamoDB DAX, Amazon DocumentDB</div>
<div class="coverage-item">✅ Amazon Redshift (Provisioned + Serverless), Amazon MemoryDB, Amazon OpenSearch Service, Amazon MSK (Provisioned + Serverless), Amazon ElastiCache (incl. Serverless)</div>
<div class="coverage-item">✅ Amazon VPC, Subnets, Security Groups, Elastic Load Balancing, AWS Transit Gateway, Amazon VPC Lattice, VPN, VPC Endpoints</div>
<div class="coverage-item">✅ Amazon CloudFront, Amazon Route 53, AWS Global Accelerator, AWS Network Firewall, AWS Direct Connect, AWS App Mesh, Cloud Map</div>
<div class="coverage-item">✅ Amazon Kinesis Data Streams, Amazon Kinesis Data Firehose, Amazon Kinesis Analytics, Amazon Kinesis Video Streams, AWS Glue (incl. DataBrew), Amazon Athena</div>
<div class="coverage-item">✅ Amazon SNS, Amazon SQS, AWS Step Functions, Amazon EventBridge, Amazon API Gateway (REST + HTTP + WebSocket), AWS AppSync, Amazon MQ</div>
<div class="coverage-item">✅ Amazon SageMaker (Notebooks, Domains, Pipelines, Feature Groups), Amazon Comprehend, Amazon Kendra</div>
<div class="coverage-item">✅ Amazon Bedrock (Agents, Guardrails, Flows, Prompts, Inference Profiles, Knowledge Bases)</div>
<div class="coverage-item">✅ AWS CodeBuild, AWS CodeDeploy, AWS CodePipeline, AWS CloudFormation, Service Catalog</div>
<div class="coverage-item">✅ Amazon Cognito, AWS Security Hub, AWS WAF, AWS KMS, AWS Certificate Manager, AWS Private CA, AWS Secrets Manager</div>
<div class="coverage-item">✅ Amazon WorkSpaces, Amazon QuickSight, Amazon AppStream 2.0, Amazon GameLift, Amazon Timestream</div>
<div class="coverage-item">✅ AWS Transfer Family, AWS DataSync, AWS DMS (incl. Serverless), AWS Elastic Disaster Recovery</div>
<div class="coverage-item">✅ AWS IoT Core (Topic Rules), AWS IoT SiteWise (Assets, Models, Gateways, Portals)</div>
<div class="coverage-item">✅ AWS Elemental MediaConvert, AWS Elemental MediaLive, AWS Elemental MediaPackage</div>
<div class="coverage-item">✅ AWS Deadline Cloud, Amazon CloudWatch, AWS Systems Manager, AWS HealthLake</div>
<div class="coverage-item limited">❌ IoT Things, Lambda Layers/Aliases, Keyspaces Tables, Glue Tables, API Gateway API Keys — not taggable (AWS platform limitation)</div>
<div class="coverage-item">✅ NAT Gateways, ElastiCache, Aurora — tagged within 5–15 min of creation (provisioning delay handled automatically via retry)</div>
</div>
</div>
<div class="actions">
<button class="btn-secondary" onclick="selectMode(null)" style="margin-right:auto;">← Back</button>
<button class="btn-primary" onclick="reviewConfig()" data-i18n="ui_btn_next">Review Configuration →</button>
</div>
</div>
<!-- Step 2: Review -->
<div id="step2" class="hidden">
<div class="card">
<h2 data-i18n="ui_review_title">Review Configuration</h2>
<p class="subtitle" data-i18n="ui_review_subtitle">Verify settings before generating the template</p>
<table style="width: 100%; font-size: 14px; border-collapse: collapse;" id="reviewTable"></table>
</div>
<div class="card">
<h2 data-i18n="ui_what_deployed">What Gets Deployed</h2>
<p class="subtitle" id="deployDescription"></p>
<div id="templateList"></div>
<div class="cost-note">
<span data-i18n="ui_cost_note">Estimated monthly cost: </span><strong>< $2/month</strong><span data-i18n="ui_cost_note2"> per account (Lambda invocations + CloudWatch). No AWS Config required.</span>
</div>
</div>
<div class="actions">
<button class="btn-secondary" onclick="backToConfig()" data-i18n="ui_btn_back">← Back</button>
<button class="btn-primary" onclick="generateAndDownload()" data-i18n="ui_btn_generate">Generate & Download →</button>
</div>
</div>
<!-- Step 3: Download -->
<div id="step3" class="hidden">
<div style="position:relative;">
<!-- Card (original full width, unchanged) -->
<div class="card">
<h2 data-i18n="ui_template_ready">Template Ready</h2>
<p class="subtitle" data-i18n="ui_template_ready_subtitle">Share this with the customer along with the deployment instructions below</p>
<div id="downloadButtons" style="margin:16px 0;display:flex;gap:12px;flex-wrap:wrap;"></div>
<h3 style="margin:20px 0 8px;font-size:14px;" data-i18n="ui_deploy_instructions">Customer Deployment Instructions</h3>
<div class="preview-box" id="instructions" style="color:#d4d4d4;"></div>
<div style="margin-top:40px;display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
<h2 style="margin:0;" data-i18n="ui_template_preview">Template Preview</h2>
<button class="btn-secondary" id="downloadTemplateBtn" onclick="downloadFile('')">
<span data-i18n="ui_download_template">Download CloudFormation Template</span><br>
<span style="font-size:11px;color:#687078;font-weight:400;" data-i18n="ui_download_template_note">Optional — for customers who would like to review the CloudFormation code before deploying.</span>
</button>
</div>
<div class="preview-box" id="templatePreview" style="margin-top:12px;"></div>
</div>
<!-- Right: green hint + prerequisites + SCP, outside the card -->
<div style="position:absolute;top:0;left:calc(100% + 20px);width:380px;display:flex;flex-direction:column;gap:12px;">
<div id="deployHint"></div>
<div class="info-box" style="border-left:4px solid #d13212;background:#fdf3f1;margin:0;">
<strong data-i18n="ui_prereq_title">Customer Prerequisites</strong> — <span data-i18n="ui_prereq_subtitle">the generated deploy.sh verifies these automatically before deploying:</span>
<ul style="margin:8px 0 0;padding-left:20px;">
<li data-i18n="ui_prereq_cloudtrail">CloudTrail must be enabled in the target region(s)</li>
<li data-i18n="ui_prereq_perms">Deployer must have AWS CloudFormation permissions (IAM, AWS Lambda, Amazon EventBridge, AWS Systems Manager, Amazon SNS, Amazon SQS, Amazon CloudWatch)</li>
<li id="prereq-multi" style="display:none;" data-i18n="ui_prereq_org">Management account must have AWS Organizations enabled with CloudFormation StackSets trusted access</li>
</ul>
</div>
<div class="info-box" style="border-left:4px solid #f90;background:#fffbf0;margin:0;">
<strong data-i18n="ui_scp_title">⚠️ Service Control Policy (SCP) Advisory</strong><br>
<span data-i18n="ui_scp_intro">If the customer's organization uses SCPs, verify the following before deployment:</span>
<ul style="margin:8px 0 0;padding-left:20px;">
<li data-i18n="ui_scp_tagging"></li>
<li data-i18n="ui_scp_mandatory"></li>
</ul>
</div>
</div>
</div>
<div class="actions">
<button class="btn-secondary" onclick="backToConfig()" data-i18n="ui_start_over">Start Over</button>
</div>
</div>
</div> <!-- end deploy-flow -->
<!-- Editor flow — hidden until mode selected -->
<div id="edit-flow" class="hidden">
<!-- Step indicator -->
<div class="step-indicator" style="margin-top:8px;">
<div class="step active" id="estep1-indicator" data-i18n="ui_estep1">1. Configure</div>
<div class="step" id="estep2-indicator" data-i18n="ui_estep2">2. Review</div>
<div class="step" id="estep3-indicator" data-i18n="ui_estep3">3. Download</div>
</div>
<div class="warn-box" style="font-size:12px;">
<strong>Sample Code Notice:</strong> This is sample code for non-production usage. You are responsible for testing, securing, and optimizing this solution to meet your organization's security, regulatory, and compliance requirements before deployment. See the <a href="https://aws.amazon.com/compliance/shared-responsibility-model/" style="color:#0073bb;">AWS Shared Responsibility Model</a>.
</div>
<div class="info-box">
<span data-i18n="ui_editor_info">This tool modifies an existing deployment created by the MAP Auto-Tagger Configurator. It generates an <code>update.sh</code> script that updates the account scope without redeploying.</span>
</div>
<!-- Editor Step 1: Configure -->
<div id="estep1">
<div class="card">
<h2 data-i18n="ui_editor_step1_title">Deployment Details</h2>
<p class="subtitle" data-i18n="ui_editor_step1_subtitle">Enter the details of your existing MAP Auto-Tagger deployment</p>
<div class="form-group">
<label><span data-i18n="ui_mpe_tag">MAP Engagement ID</span> <span class="hint" data-i18n="ui_editor_mpe_hint">— 10-character alphanumeric code, must match the original deployment</span></label>
<div style="display:flex;align-items:center;gap:0;">
<span style="padding:8px 10px;background:#f2f3f3;border:1px solid #aab7b8;border-right:none;border-radius:4px 0 0 4px;font-size:14px;color:#687078;white-space:nowrap;">mig</span>
<input type="text" id="editor-mpeId" placeholder="A1B2C3D4E5" maxlength="10" style="border-radius:0 4px 4px 0;" oninput="this.value=this.value.toUpperCase().replace(/[^A-Z0-9]/g,'')">
</div>
<div class="error-msg" id="editor-mpeId-error" data-i18n="err_mpe_invalid">MAP Engagement ID must be exactly 10 characters, uppercase English letters (A–Z) and digits (0–9) only, with at least one letter and one number</div>
</div>
<div class="form-group">
<label><span data-i18n="ui_editor_region">Deployment Region</span> <span class="hint" data-i18n="ui_editor_region_hint">— region where the management stack was deployed</span></label>
<select id="editor-region">
<option value="" disabled selected data-i18n="ui_editor_region_placeholder">Select a region...</option>
<option value="us-east-1">US East (N. Virginia) — us-east-1</option>
<option value="us-east-2">US East (Ohio) — us-east-2</option>
<option value="us-west-1">US West (N. California) — us-west-1</option>
<option value="us-west-2">US West (Oregon) — us-west-2</option>
<option value="af-south-1">Africa (Cape Town) — af-south-1</option>
<option value="ap-east-1">Asia Pacific (Hong Kong) — ap-east-1</option>
<option value="ap-east-2">Asia Pacific (Taipei) — ap-east-2</option>
<option value="ap-northeast-1">Asia Pacific (Tokyo) — ap-northeast-1</option>
<option value="ap-northeast-2">Asia Pacific (Seoul) — ap-northeast-2</option>
<option value="ap-northeast-3">Asia Pacific (Osaka) — ap-northeast-3</option>
<option value="ap-south-1">Asia Pacific (Mumbai) — ap-south-1</option>
<option value="ap-south-2">Asia Pacific (Hyderabad) — ap-south-2</option>
<option value="ap-southeast-1">Asia Pacific (Singapore) — ap-southeast-1</option>
<option value="ap-southeast-2">Asia Pacific (Sydney) — ap-southeast-2</option>
<option value="ap-southeast-3">Asia Pacific (Jakarta) — ap-southeast-3</option>
<option value="ap-southeast-4">Asia Pacific (Melbourne) — ap-southeast-4</option>
<option value="ap-southeast-5">Asia Pacific (Malaysia) — ap-southeast-5</option>
<option value="ap-southeast-6">Asia Pacific (New Zealand) — ap-southeast-6</option>
<option value="ap-southeast-7">Asia Pacific (Thailand) — ap-southeast-7</option>
<option value="ca-central-1">Canada (Central) — ca-central-1</option>
<option value="ca-west-1">Canada West (Calgary) — ca-west-1</option>
<option value="eu-central-1">Europe (Frankfurt) — eu-central-1</option>
<option value="eu-central-2">Europe (Zurich) — eu-central-2</option>
<option value="eu-north-1">Europe (Stockholm) — eu-north-1</option>
<option value="eu-south-1">Europe (Milan) — eu-south-1</option>
<option value="eu-south-2">Europe (Spain) — eu-south-2</option>
<option value="eu-west-1">Europe (Ireland) — eu-west-1</option>
<option value="eu-west-2">Europe (London) — eu-west-2</option>
<option value="eu-west-3">Europe (Paris) — eu-west-3</option>
<option value="il-central-1">Israel (Tel Aviv) — il-central-1</option>
<option value="me-central-1">Middle East (UAE) — me-central-1</option>
<option value="me-south-1">Middle East (Bahrain) — me-south-1</option>
<option value="mx-central-1">Mexico (Central) — mx-central-1</option>
<option value="sa-east-1">South America (Sao Paulo) — sa-east-1</option>
</select>
<div class="error-msg" id="editor-region-error" data-i18n="err_editor_region">Please select a deployment region</div>
</div>
</div>
<div class="card">
<h2 data-i18n="ui_editor_action_title">Action</h2>
<p class="subtitle" data-i18n="ui_editor_action_subtitle">Choose whether to add or remove accounts from the MAP scope</p>
<div class="option-card" id="editor-opt-add" onclick="editorSelectAction('add')">
<h3><input type="radio" name="editor-action" value="add"> <span data-i18n="ui_editor_add_title">Add accounts to MAP scope</span></h3>
<p data-i18n="ui_editor_add_desc">Selected accounts will start being tagged with the MAP tag.</p>
</div>
<div id="editor-add-accounts-section" class="hidden">
<div class="section-divider"></div>
<div class="form-group">
<label><span data-i18n="ui_editor_add_accounts_label">Account IDs to add</span> <span class="hint" data-i18n="ui_editor_add_accounts_hint">— only accounts not currently in scope</span></label>
<div id="editor-add-accountList">
<div class="entry-row">
<input type="text" class="editor-account-input" placeholder="123456789012" maxlength="12">
<button class="btn-remove" onclick="editorRemoveRow(this)" title="Remove">×</button>
</div>
</div>
<button class="btn-add" onclick="editorAddAccount('add')" data-i18n="ui_add_account">+ Add another account</button>
<div class="error-msg" id="editor-account-error" data-i18n="err_editor_account">Please enter at least one valid 12-digit account ID</div>
</div>
<div class="form-group" style="margin-bottom:4px;">
<label style="display:flex;align-items:flex-start;gap:10px;cursor:pointer;">
<input type="checkbox" id="editor-includeBackfill" checked style="margin-top:3px;flex-shrink:0;">
<div>
<strong data-i18n="ui_editor_backfill_title">Re-run backfill for newly added accounts</strong>
<p class="hint" style="margin-top:4px;" data-i18n="ui_editor_backfill_desc">Queries CloudTrail history from the agreement start date and tags existing resources in the newly added accounts. Runs automatically during the StackSet update. Results appear in CloudWatch Logs (<code>map-auto-tagger-backfill</code>).</p>
<p class="hint" style="margin-top:4px;color:#d13212;" data-i18n="ui_editor_backfill_note">Note: CloudTrail history is limited to the last 90 days. Resources created before that window must be tagged manually.</p>
</div>
</label>
</div>
<div class="section-divider"></div>
</div>
<div class="option-card" id="editor-opt-remove" onclick="editorSelectAction('remove')">
<h3><input type="radio" name="editor-action" value="remove"> <span data-i18n="ui_editor_remove_title">Remove accounts from MAP scope</span></h3>
<p data-i18n="ui_editor_remove_desc">Selected accounts will stop being tagged. Existing tags are not removed. The Lambda remains deployed but inactive for these accounts.</p>
</div>
<div id="editor-remove-accounts-section" class="hidden">
<div class="section-divider"></div>
<div class="form-group" style="margin-bottom:4px;">
<label><span data-i18n="ui_editor_remove_accounts_label">Account IDs to remove</span> <span class="hint" data-i18n="ui_editor_remove_accounts_hint">— only accounts currently in scope</span></label>
<div id="editor-remove-accountList">
<div class="entry-row">
<input type="text" class="editor-account-input" placeholder="123456789012" maxlength="12">
<button class="btn-remove" onclick="editorRemoveRow(this)" title="Remove">×</button>
</div>
</div>
<button class="btn-add" onclick="editorAddAccount('remove')" data-i18n="ui_add_account">+ Add another account</button>
<div class="error-msg" id="editor-remove-account-error" data-i18n="err_editor_account">Please enter at least one valid 12-digit account ID</div>
</div>
<div class="section-divider"></div>
</div>
</div>
<div class="actions">
<button class="btn-secondary" onclick="selectMode(null)" data-i18n="ui_editor_back">← Back</button>
<button class="btn-primary" onclick="editorReview()" style="margin-left:auto;" data-i18n="ui_editor_review">Review →</button>
</div>
</div>
<!-- Editor Step 2: Review -->
<div id="estep2" class="hidden">
<div class="card">
<h2 data-i18n="ui_editor_review_title">Review Configuration</h2>
<p class="subtitle" data-i18n="ui_editor_review_subtitle">Verify your settings before generating the update script</p>
<table style="width:100%;font-size:14px;border-collapse:collapse;" id="editor-reviewTable"></table>
</div>
<div class="card" style="border-left:4px solid #d13212;background:#fdf3f1;">
<div class="form-group" style="margin-bottom:0;">
<label style="display:flex;align-items:flex-start;gap:10px;cursor:pointer;">
<input type="checkbox" id="editor-confirm" style="margin-top:3px;flex-shrink:0;">
<span>
<span data-i18n="ui_editor_confirm_text">I confirm that a MAP Auto-Tagger has already been deployed in the selected region using the specified MPE ID, and that both values are correct.</span>
<strong data-i18n="ui_editor_confirm_risk">Generating and running this script with incorrect information may update the wrong StackSet, corrupt the account scope, or cause resources to be tagged with the wrong MAP engagement ID — resulting in lost MAP credits that cannot be recovered retroactively.</strong>
</span>
</label>
<div class="error-msg" id="editor-confirm-error" style="margin-top:8px;" data-i18n="err_editor_confirm">You must confirm before generating the update script</div>
</div>
</div>
<div class="actions">
<button class="btn-secondary" onclick="editorSetStep(1)" data-i18n="ui_btn_back">← Back</button>
<button class="btn-primary" onclick="editorGenerate()" style="margin-left:auto;" data-i18n="ui_editor_generate">Generate update.sh →</button>
</div>
</div>
<!-- Editor Step 3: Download -->
<div id="estep3" class="hidden">
<div class="card">
<h2 data-i18n="ui_editor_step3_title">Script Ready</h2>
<p class="subtitle" data-i18n="ui_editor_step3_subtitle">Run this in AWS CloudShell from the management account</p>
<div style="margin:16px 0;display:flex;gap:12px;flex-wrap:wrap;">
<button class="btn-primary" onclick="editorDownload()" style="font-size:16px;padding:14px 32px;" data-i18n="ui_editor_download">⬇ Download update.sh</button>
<button class="btn-secondary" onclick="editorCopyInstructions()" data-i18n="ui_btn_copy">📋 Copy Instructions</button>
</div>
<h3 style="margin:20px 0 8px;font-size:14px;" data-i18n="ui_deploy_instructions">Deployment Instructions</h3>
<div class="preview-box" id="editor-instructions" style="color:#d4d4d4;"></div>
<h3 style="margin:20px 0 8px;font-size:14px;" data-i18n="ui_editor_script_preview">Script Preview</h3>
<div class="preview-box" id="editor-scriptContent"></div>
<div class="actions" style="margin-top:20px;">
<button class="btn-secondary" onclick="editorSetStep(1)" data-i18n="ui_start_over">Start Over</button>
</div>
</div>
</div>
</div>
<!-- Update flow (upgrade template version) — hidden until mode selected -->
<div id="update-flow" class="hidden">
<!-- Step indicator -->
<div class="step-indicator" style="margin-top:8px;">
<div class="step active" id="ustep1-indicator" data-i18n="ui_ustep1">1. Configure</div>
<div class="step" id="ustep2-indicator" data-i18n="ui_ustep2">2. Review</div>
<div class="step" id="ustep3-indicator" data-i18n="ui_ustep3">3. Download</div>
</div>
<div class="warn-box" style="font-size:12px;">
<strong>Sample Code Notice:</strong> This is sample code for non-production usage. You are responsible for testing, securing, and optimizing this solution to meet your organization's security, regulatory, and compliance requirements before deployment. See the <a href="https://aws.amazon.com/compliance/shared-responsibility-model/" style="color:#0073bb;">AWS Shared Responsibility Model</a>.
</div>
<div class="info-box">
<span data-i18n="ui_update_info">This tool upgrades an existing MAP Auto-Tagger deployment to the latest template version. It generates an <code>update.sh</code> script that preserves scope configuration and auto-detects single-account and multi-account deployments.</span>
</div>
<!-- Update Step 1: Configure -->
<div id="ustep1">
<div class="card">
<h2 data-i18n="ui_update_step1_title">Deployment Details</h2>
<p class="subtitle" data-i18n="ui_update_step1_subtitle">Enter the region and optional scope for the upgrade</p>
<div class="form-group">
<label><span data-i18n="ui_editor_region">Deployment Region</span> <span class="hint" data-i18n="ui_editor_region_hint">— region where the management stack was deployed</span></label>
<select id="update-region">
<option value="" disabled selected data-i18n="ui_editor_region_placeholder">Select a region...</option>
<option value="us-east-1">US East (N. Virginia) — us-east-1</option>
<option value="us-east-2">US East (Ohio) — us-east-2</option>
<option value="us-west-1">US West (N. California) — us-west-1</option>
<option value="us-west-2">US West (Oregon) — us-west-2</option>
<option value="af-south-1">Africa (Cape Town) — af-south-1</option>
<option value="ap-east-1">Asia Pacific (Hong Kong) — ap-east-1</option>
<option value="ap-east-2">Asia Pacific (Taipei) — ap-east-2</option>
<option value="ap-northeast-1">Asia Pacific (Tokyo) — ap-northeast-1</option>
<option value="ap-northeast-2">Asia Pacific (Seoul) — ap-northeast-2</option>
<option value="ap-northeast-3">Asia Pacific (Osaka) — ap-northeast-3</option>
<option value="ap-south-1">Asia Pacific (Mumbai) — ap-south-1</option>
<option value="ap-south-2">Asia Pacific (Hyderabad) — ap-south-2</option>
<option value="ap-southeast-1">Asia Pacific (Singapore) — ap-southeast-1</option>
<option value="ap-southeast-2">Asia Pacific (Sydney) — ap-southeast-2</option>
<option value="ap-southeast-3">Asia Pacific (Jakarta) — ap-southeast-3</option>
<option value="ap-southeast-4">Asia Pacific (Melbourne) — ap-southeast-4</option>
<option value="ap-southeast-5">Asia Pacific (Malaysia) — ap-southeast-5</option>
<option value="ap-southeast-6">Asia Pacific (New Zealand) — ap-southeast-6</option>
<option value="ap-southeast-7">Asia Pacific (Thailand) — ap-southeast-7</option>
<option value="ca-central-1">Canada (Central) — ca-central-1</option>
<option value="ca-west-1">Canada West (Calgary) — ca-west-1</option>
<option value="eu-central-1">Europe (Frankfurt) — eu-central-1</option>
<option value="eu-central-2">Europe (Zurich) — eu-central-2</option>
<option value="eu-north-1">Europe (Stockholm) — eu-north-1</option>
<option value="eu-south-1">Europe (Milan) — eu-south-1</option>
<option value="eu-south-2">Europe (Spain) — eu-south-2</option>
<option value="eu-west-1">Europe (Ireland) — eu-west-1</option>
<option value="eu-west-2">Europe (London) — eu-west-2</option>
<option value="eu-west-3">Europe (Paris) — eu-west-3</option>
<option value="il-central-1">Israel (Tel Aviv) — il-central-1</option>
<option value="me-central-1">Middle East (UAE) — me-central-1</option>
<option value="me-south-1">Middle East (Bahrain) — me-south-1</option>
<option value="mx-central-1">Mexico (Central) — mx-central-1</option>
<option value="sa-east-1">South America (Sao Paulo) — sa-east-1</option>
</select>
<div class="error-msg" id="update-region-error" data-i18n="err_editor_region">Please select a deployment region</div>
</div>
<div class="info-box" style="background:#f0f7fc;border-left:4px solid #0073bb;font-size:13px;margin-top:16px;">
<span data-i18n="ui_update_info_detail">The generated <code>update.sh</code> reads the current template version from SSM Parameter Store (<code>/auto-map-tagger/<mpe>/version</code>), compares it to the target version, and refuses cross-major upgrades (e.g. v19 → v21) without <code>--force</code>. Customer action is only required for MAJOR bumps per SemVer.</span>
</div>
<details id="update-versionHistory" style="margin-top:16px;border:1px solid #eaeded;border-radius:4px;padding:12px 14px;background:#fafafa;">
<summary style="cursor:pointer;font-weight:600;color:#16191f;font-size:13px;">
<span data-i18n="ui_version_history_title">Version history — what changed?</span>
</summary>
<div id="update-versionHistoryContent" style="margin-top:10px;"></div>
</details>
<div class="form-group" style="margin-bottom:4px;margin-top:16px;">
<label style="display:flex;align-items:flex-start;gap:10px;cursor:pointer;">
<input type="checkbox" id="update-scopeToMpe" onchange="updateToggleScope()" style="margin-top:3px;flex-shrink:0;">
<div>
<strong data-i18n="ui_update_scope_title">Update only specific MAP engagement(s)</strong>
<p class="hint" style="margin-top:4px;" data-i18n="ui_update_scope_desc">Leave unchecked to update every MAP Auto-Tagger deployment the script finds in this account (both stacks and stacksets matching <code>map-auto-tagger-mig*</code>).</p>
</div>
</label>
</div>
<div id="update-mpeSection" class="hidden" style="margin-top:16px;">
<div class="form-group">
<label><span data-i18n="ui_update_mpe_label">MPE IDs to update</span> <span class="hint" data-i18n="ui_update_mpe_hint">— each must match a deployed MAP Auto-Tagger in this region</span></label>
<div id="update-mpeList">
<div class="entry-row" style="display:flex;align-items:center;gap:8px;">
<div style="display:flex;align-items:center;gap:0;flex:1;">
<span style="padding:8px 10px;background:#f2f3f3;border:1px solid #aab7b8;border-right:none;border-radius:4px 0 0 4px;font-size:14px;color:#687078;white-space:nowrap;">mig</span>
<input type="text" class="update-mpe-input" placeholder="A1B2C3D4E5" maxlength="10" style="border-radius:0 4px 4px 0;" oninput="this.value=this.value.toUpperCase().replace(/[^A-Z0-9]/g,'')">
</div>
<button class="btn-remove" onclick="editorRemoveRow(this)" title="Remove">×</button>
</div>
</div>
<button class="btn-add" onclick="updateAddMpe()" data-i18n="ui_update_add_mpe">+ Add another MPE</button>
<div class="error-msg" id="update-mpe-error" data-i18n="err_update_mpe">Please enter at least one valid 10-character MPE ID</div>
</div>
</div>
</div>
<div class="actions">
<button class="btn-secondary" onclick="selectMode(null)" data-i18n="ui_editor_back">← Back</button>
<button class="btn-primary" onclick="updateReview()" style="margin-left:auto;" data-i18n="ui_editor_review">Review →</button>
</div>
</div>
<!-- Update Step 2: Review -->
<div id="ustep2" class="hidden">
<div class="card">
<h2 data-i18n="ui_editor_review_title">Review Configuration</h2>
<p class="subtitle" data-i18n="ui_editor_review_subtitle">Verify your settings before generating the update script</p>
<table style="width:100%;font-size:14px;border-collapse:collapse;" id="update-reviewTable"></table>
</div>
<div class="card" style="border-left:4px solid #d13212;background:#fdf3f1;">
<div class="form-group" style="margin-bottom:0;">
<label style="display:flex;align-items:flex-start;gap:10px;cursor:pointer;">
<input type="checkbox" id="update-confirm" style="margin-top:3px;flex-shrink:0;">
<span>
<span data-i18n="ui_update_confirm_text">I confirm that the listed MAP Auto-Tagger deployment(s) exist in the selected region and that upgrading them to the target template version is intended.</span>
<strong data-i18n="ui_update_confirm_risk">An upgrade replaces the Lambda code, IAM policy, and EventBridge rules in every matched stack. Scope and agreement dates are preserved via <code>--use-previous-parameters</code>, but a failed rollout may leave a stack in UPDATE_ROLLBACK_COMPLETE state requiring manual recovery.</strong>
</span>
</label>
<div class="error-msg" id="update-confirm-error" style="margin-top:8px;" data-i18n="err_update_confirm">You must confirm before generating the update script</div>
</div>
</div>
<div class="actions">
<button class="btn-secondary" onclick="updateSetStep(1)" data-i18n="ui_btn_back">← Back</button>
<button class="btn-primary" onclick="updateGenerate()" style="margin-left:auto;" data-i18n="ui_editor_generate">Generate update.sh →</button>
</div>
</div>
<!-- Update Step 3: Download -->
<div id="ustep3" class="hidden">
<div class="card">
<h2 data-i18n="ui_editor_step3_title">Script Ready</h2>
<p class="subtitle" data-i18n="ui_update_step3_subtitle">Run this in AWS CloudShell from the account where the deployment was made</p>
<div style="margin:16px 0;display:flex;gap:12px;flex-wrap:wrap;">
<button class="btn-primary" onclick="updateDownload()" style="font-size:16px;padding:14px 32px;" data-i18n="ui_editor_download">⬇ Download update.sh</button>
<button class="btn-secondary" onclick="updateCopyInstructions()" data-i18n="ui_btn_copy">📋 Copy Instructions</button>
</div>
<h3 style="margin:20px 0 8px;font-size:14px;" data-i18n="ui_deploy_instructions">Deployment Instructions</h3>
<div class="preview-box" id="update-instructions" style="color:#d4d4d4;"></div>
<h3 style="margin:20px 0 8px;font-size:14px;" data-i18n="ui_editor_script_preview">Script Preview</h3>
<div class="preview-box" id="update-scriptContent"></div>
<div class="actions" style="margin-top:20px;">
<button class="btn-secondary" onclick="updateSetStep(1)" data-i18n="ui_start_over">Start Over</button>
</div>
</div>
</div>
</div>
</div>
<script>
// Template version — single source of truth for the SemVer constant.
// Must match `TEMPLATE_VERSION = 'v20.3.0'` in map2-auto-tagger-optimized.yaml (sync-check enforces this).
const TEMPLATE_VERSION = 'v20.3.0';
// Version history surfaced in the Update flow. Bullets are intentionally English-only —
// translating release notes across 7 languages for every PR is unsustainable. Labels
// (titles, buttons) go through i18n; change bullets stay in source form.
// Tags: bugfix, coverage, breaking, security, perf, other.
// sync-check.py enforces that the newest entry's version matches TEMPLATE_VERSION.
const VERSION_HISTORY = [
{
version: 'v20.3.0',
date: '2026-04-22',
changes: [
{ tag: 'coverage', text: 'Added handlers for Amazon Keyspaces (CreateKeyspace), AWS Directory Service (CreateDirectory + CreateMicrosoftAD), and AWS CloudHSM v2 (CreateCluster + CreateHsm).' },
{ tag: 'coverage', text: 'Added IAM permissions: ds:AddTagsToResource and cloudhsm:TagResource.' },
{ tag: 'other', text: 'AD Connector (ConnectDirectory) intentionally deferred — requires broader EventBridge prefix changes.' },
],
},
{
version: 'v20.2.0',
date: '2026-04-22',