-
Notifications
You must be signed in to change notification settings - Fork 86
Expand file tree
/
Copy pathbuild_pptx.py
More file actions
1543 lines (1359 loc) · 86.5 KB
/
build_pptx.py
File metadata and controls
1543 lines (1359 loc) · 86.5 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
"""
AZ-305 Comprehensive Training Presentation Builder
Generates a 97-slide professional Azure-themed PPTX deck.
"""
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE
import os
# ---------------------------------------------------------------------------
# Color palette
# ---------------------------------------------------------------------------
AZURE_BLUE = RGBColor(0x00, 0x78, 0xD4)
DARK_BLUE = RGBColor(0x00, 0x20, 0x50)
GREEN = RGBColor(0x00, 0xA3, 0x6C)
PINK = RGBColor(0xE8, 0x3E, 0x8C)
PURPLE = RGBColor(0x77, 0x19, 0xAA)
GOLD = RGBColor(0xFF, 0xB9, 0x00)
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
LIGHT_GRAY = RGBColor(0xF2, 0xF2, 0xF2)
BLACK = RGBColor(0x00, 0x00, 0x00)
MID_GRAY = RGBColor(0x44, 0x44, 0x44)
LIGHT_BLUE = RGBColor(0xE8, 0xF4, 0xFD)
LIGHT_GOLD = RGBColor(0xFF, 0xF8, 0xE1)
SLIDE_WIDTH = Inches(13.333)
SLIDE_HEIGHT = Inches(7.5)
# Segment labels for footer
SEGMENTS = {
0: "Opening",
1: "Segment 1: Identity, Governance & Monitoring",
2: "Segment 2: Data Storage Solutions",
3: "Segment 3: Business Continuity & HA",
4: "Segment 4: Compute & Application Architecture",
5: "Segment 5: Networking & Migrations",
6: "Closing",
}
prs = Presentation()
prs.slide_width = SLIDE_WIDTH
prs.slide_height = SLIDE_HEIGHT
# Use blank layout
BLANK_LAYOUT = prs.slide_layouts[6]
slide_counter = 0
# ---------------------------------------------------------------------------
# Helper functions
# ---------------------------------------------------------------------------
def _set_fill_solid(shape, color):
shape.fill.solid()
shape.fill.fore_color.rgb = color
def _add_textbox(slide, left, top, width, height, text, font_size=18,
bold=False, color=BLACK, alignment=PP_ALIGN.LEFT,
font_name="Segoe UI"):
txBox = slide.shapes.add_textbox(left, top, width, height)
tf = txBox.text_frame
tf.word_wrap = True
p = tf.paragraphs[0]
p.text = text
p.font.size = Pt(font_size)
p.font.bold = bold
p.font.color.rgb = color
p.font.name = font_name
p.alignment = alignment
return txBox
def _add_paragraph(text_frame, text, font_size=18, bold=False, color=BLACK,
alignment=PP_ALIGN.LEFT, space_before=Pt(4),
space_after=Pt(4), font_name="Segoe UI", level=0):
p = text_frame.add_paragraph()
p.text = text
p.font.size = Pt(font_size)
p.font.bold = bold
p.font.color.rgb = color
p.font.name = font_name
p.alignment = alignment
p.space_before = space_before
p.space_after = space_after
p.level = level
return p
def add_footer(slide, segment_id, slide_num):
"""Add segment label bottom-left and slide number bottom-right."""
seg_label = SEGMENTS.get(segment_id, "")
_add_textbox(slide, Inches(0.5), Inches(7.0), Inches(6), Inches(0.4),
seg_label, font_size=10, color=MID_GRAY)
_add_textbox(slide, Inches(12.0), Inches(7.0), Inches(1), Inches(0.4),
str(slide_num), font_size=10, color=MID_GRAY,
alignment=PP_ALIGN.RIGHT)
def add_header_bar(slide, title_text, subtitle_text=None):
"""Azure blue header bar across the top with title."""
bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE,
Inches(0), Inches(0),
SLIDE_WIDTH, Inches(1.2))
_set_fill_solid(bar, AZURE_BLUE)
bar.line.fill.background()
_add_textbox(slide, Inches(0.5), Inches(0.15), Inches(12), Inches(0.6),
title_text, font_size=28, bold=True, color=WHITE,
font_name="Segoe UI Semibold")
if subtitle_text:
_add_textbox(slide, Inches(0.5), Inches(0.72), Inches(12), Inches(0.4),
subtitle_text, font_size=16, color=WHITE)
def new_content_slide(title, segment_id, subtitle=None):
"""Create a standard content slide with header bar and footer."""
global slide_counter
slide_counter += 1
slide = prs.slides.add_slide(BLANK_LAYOUT)
add_header_bar(slide, title, subtitle)
add_footer(slide, segment_id, slide_counter)
return slide
def new_divider_slide(title, badge_text, segment_id):
"""Full dark-blue background divider slide."""
global slide_counter
slide_counter += 1
slide = prs.slides.add_slide(BLANK_LAYOUT)
bg = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE,
Inches(0), Inches(0),
SLIDE_WIDTH, SLIDE_HEIGHT)
_set_fill_solid(bg, DARK_BLUE)
bg.line.fill.background()
_add_textbox(slide, Inches(1), Inches(2.0), Inches(11), Inches(2),
title, font_size=44, bold=True, color=WHITE,
alignment=PP_ALIGN.CENTER, font_name="Segoe UI Semibold")
# Badge
badge = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE,
Inches(4.5), Inches(4.5),
Inches(4.3), Inches(0.8))
_set_fill_solid(badge, GOLD)
badge.line.fill.background()
tf = badge.text_frame
tf.word_wrap = True
p = tf.paragraphs[0]
p.text = badge_text
p.font.size = Pt(22)
p.font.bold = True
p.font.color.rgb = DARK_BLUE
p.font.name = "Segoe UI Semibold"
p.alignment = PP_ALIGN.CENTER
tf.paragraphs[0].space_before = Pt(8)
add_footer(slide, segment_id, slide_counter)
return slide
def new_exam_tip_slide(title, segment_id):
"""Slide with light gold background for exam tips."""
global slide_counter
slide_counter += 1
slide = prs.slides.add_slide(BLANK_LAYOUT)
bg = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE,
Inches(0), Inches(0),
SLIDE_WIDTH, SLIDE_HEIGHT)
_set_fill_solid(bg, LIGHT_GOLD)
bg.line.fill.background()
bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE,
Inches(0), Inches(0),
SLIDE_WIDTH, Inches(1.2))
_set_fill_solid(bar, PINK)
bar.line.fill.background()
_add_textbox(slide, Inches(0.5), Inches(0.15), Inches(12), Inches(0.6),
title, font_size=28, bold=True, color=WHITE,
font_name="Segoe UI Semibold")
_add_textbox(slide, Inches(0.5), Inches(0.72), Inches(12), Inches(0.4),
"EXAM TIPS", font_size=16, color=WHITE)
add_footer(slide, segment_id, slide_counter)
return slide
def add_bullet_list(slide, items, left=Inches(0.8), top=Inches(1.5),
width=Inches(11.5), height=Inches(5.2),
font_size=18, color=BLACK, bold_prefix=True):
"""Add a bulleted list to a slide."""
txBox = slide.shapes.add_textbox(left, top, width, height)
tf = txBox.text_frame
tf.word_wrap = True
for i, item in enumerate(items):
if i == 0:
p = tf.paragraphs[0]
else:
p = tf.add_paragraph()
p.font.size = Pt(font_size)
p.font.color.rgb = color
p.font.name = "Segoe UI"
p.space_before = Pt(6)
p.space_after = Pt(6)
if bold_prefix and ": " in item:
parts = item.split(": ", 1)
run1 = p.add_run()
run1.text = "\u2022 " + parts[0] + ": "
run1.font.size = Pt(font_size)
run1.font.bold = True
run1.font.color.rgb = color
run1.font.name = "Segoe UI"
run2 = p.add_run()
run2.text = parts[1]
run2.font.size = Pt(font_size)
run2.font.color.rgb = color
run2.font.name = "Segoe UI"
else:
p.text = "\u2022 " + item
return txBox
def add_table(slide, rows_data, col_widths, left=Inches(0.5),
top=Inches(1.5), row_height=Inches(0.45),
font_size=14, header_color=AZURE_BLUE):
"""Add a formatted table to a slide.
rows_data: list of lists. First row is header."""
num_rows = len(rows_data)
num_cols = len(rows_data[0])
total_width = sum(col_widths)
table_shape = slide.shapes.add_table(num_rows, num_cols,
left, top,
Emu(int(total_width)),
Emu(int(row_height * num_rows)))
table = table_shape.table
for ci, w in enumerate(col_widths):
table.columns[ci].width = Emu(int(w))
for ri, row in enumerate(rows_data):
for ci, cell_text in enumerate(row):
cell = table.cell(ri, ci)
cell.text = str(cell_text)
for paragraph in cell.text_frame.paragraphs:
paragraph.font.size = Pt(font_size)
paragraph.font.name = "Segoe UI"
if ri == 0:
paragraph.font.bold = True
paragraph.font.color.rgb = WHITE
else:
paragraph.font.color.rgb = BLACK
cell.text_frame.paragraphs[0].space_before = Pt(2)
cell.text_frame.paragraphs[0].space_after = Pt(2)
if ri == 0:
_set_cell_fill(cell, header_color)
elif ri % 2 == 0:
_set_cell_fill(cell, LIGHT_GRAY)
else:
_set_cell_fill(cell, WHITE)
return table_shape
def _set_cell_fill(cell, color):
cell.fill.solid()
cell.fill.fore_color.rgb = color
def add_review_questions(slide, questions):
"""Add numbered scenario questions."""
txBox = slide.shapes.add_textbox(Inches(0.8), Inches(1.5),
Inches(11.5), Inches(5.5))
tf = txBox.text_frame
tf.word_wrap = True
for i, (q, a) in enumerate(questions):
if i == 0:
p = tf.paragraphs[0]
else:
p = tf.add_paragraph()
p.space_before = Pt(14)
p.text = f"Q{i+1}: {q}"
p.font.size = Pt(16)
p.font.bold = True
p.font.color.rgb = DARK_BLUE
p.font.name = "Segoe UI"
pa = tf.add_paragraph()
pa.text = f" Answer: {a}"
pa.font.size = Pt(15)
pa.font.color.rgb = GREEN
pa.font.name = "Segoe UI"
pa.space_before = Pt(4)
pa.space_after = Pt(4)
def add_demo_slide(slide, demos):
"""Format demo callout items."""
# Icon-like shape
icon = slide.shapes.add_shape(MSO_SHAPE.LIGHTNING_BOLT,
Inches(0.8), Inches(1.5),
Inches(0.6), Inches(0.6))
_set_fill_solid(icon, GOLD)
icon.line.fill.background()
_add_textbox(slide, Inches(1.6), Inches(1.5), Inches(6), Inches(0.5),
"Live Demo Exercises", font_size=22, bold=True,
color=DARK_BLUE)
add_bullet_list(slide, demos, left=Inches(1.0), top=Inches(2.3),
width=Inches(11), height=Inches(4.5), font_size=18)
# ============================================================================
# BUILD ALL 97 SLIDES
# ============================================================================
# ---------- SECTION 0: OPENING (Slides 1-8) ----------
# Slide 1: Title
slide_counter += 1
s = prs.slides.add_slide(BLANK_LAYOUT)
bg = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, Inches(0), Inches(0),
SLIDE_WIDTH, SLIDE_HEIGHT)
_set_fill_solid(bg, DARK_BLUE)
bg.line.fill.background()
# Accent bar
bar = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, Inches(0), Inches(2.8),
SLIDE_WIDTH, Inches(0.06))
_set_fill_solid(bar, GOLD)
bar.line.fill.background()
_add_textbox(s, Inches(1), Inches(1.0), Inches(11), Inches(1.5),
"AZ-305: Designing Microsoft Azure\nInfrastructure Solutions",
font_size=40, bold=True, color=WHITE, alignment=PP_ALIGN.CENTER,
font_name="Segoe UI Semibold")
_add_textbox(s, Inches(1), Inches(3.2), Inches(11), Inches(1),
"Tim Warner | O'Reilly Live Learning | January 2026",
font_size=22, color=GOLD, alignment=PP_ALIGN.CENTER)
_add_textbox(s, Inches(1), Inches(4.5), Inches(11), Inches(1),
"Microsoft MVP | MCT | Azure Solutions Architect Expert",
font_size=18, color=WHITE, alignment=PP_ALIGN.CENTER)
add_footer(s, 0, slide_counter)
# Slide 2: About the Instructor
s = new_content_slide("About the Instructor", 0)
add_bullet_list(s, [
"Tim Warner: Microsoft MVP, Microsoft Certified Trainer (MCT)",
"Certification: Azure Solutions Architect Expert (AZ-305)",
"Platform: O'Reilly Live Learning instructor -- courses on Azure, DevOps, and cloud architecture",
"Experience: 20+ years in IT training and consulting",
"Contact: timothywarner316@gmail.com",
"Social: @TechTrainerTim",
"GitHub: github.com/timothywarner",
], font_size=20)
# Slide 3: Course Objectives
s = new_content_slide("Course Objectives", 0)
add_bullet_list(s, [
"Segment 1: Design identity, governance, and monitoring solutions (25-30%)",
"Segment 2: Design data storage solutions for relational, non-relational, and unstructured data (20-25%)",
"Segment 3: Design business continuity, high availability, and disaster recovery solutions (15-20%)",
"Segment 4: Design compute and application architecture solutions (~17%)",
"Segment 5: Design networking solutions and plan migrations (~18%)",
], font_size=20)
# Slide 4: Exam Overview
s = new_content_slide("Exam Overview", 0, "AZ-305 Key Facts")
add_table(s, [
["Attribute", "Detail"],
["Exam Code", "AZ-305"],
["Title", "Designing Microsoft Azure Infrastructure Solutions"],
["Pass Score", "700 / 1000"],
["Duration", "120 minutes"],
["Questions", "~40-60 (multiple choice, case studies, drag-and-drop)"],
["Last Updated", "October 18, 2024"],
["Prerequisite", "AZ-104 recommended (not required)"],
["Cost", "$165 USD"],
], col_widths=[Inches(3), Inches(8)])
# Slide 5: Agenda & Schedule
s = new_content_slide("Agenda & Schedule", 0)
add_table(s, [
["Time", "Segment", "Topic", "Weight"],
["9:00 - 9:55", "Segment 1", "Identity, Governance & Monitoring", "25-30%"],
["10:05 - 10:55", "Segment 2", "Data Storage Solutions", "20-25%"],
["11:05 - 11:55", "Segment 3", "Business Continuity & HA", "15-20%"],
["12:05 - 12:55", "Segment 4", "Compute & Application Architecture", "~17%"],
["1:05 - 1:50", "Segment 5", "Networking & Migrations", "~18%"],
], col_widths=[Inches(2.2), Inches(2), Inches(5.5), Inches(1.8)],
top=Inches(1.5))
_add_textbox(s, Inches(0.8), Inches(4.8), Inches(11), Inches(0.5),
"10-minute breaks between each segment. All times Eastern.",
font_size=16, color=MID_GRAY)
# Slide 6: Cross-Cutting Themes
s = new_content_slide("Cross-Cutting Themes", 0,
"Three pillars that appear in EVERY segment")
items = [
"Well-Architected Framework: The lens through which every architecture decision is evaluated -- reliability, security, cost, operations, performance.",
"Zero Trust: \"Never trust, always verify.\" Assume breach, verify explicitly, use least-privilege access. This model drives networking, identity, and data design.",
"Managed Identity: Eliminate secrets from code. System-assigned and user-assigned identities replace connection strings, API keys, and passwords.",
]
add_bullet_list(s, items, font_size=18, top=Inches(1.6))
# Slide 7: Well-Architected Framework
s = new_content_slide("Azure Well-Architected Framework", 0, "5 Pillars")
add_table(s, [
["Pillar", "Focus", "Key Question"],
["Reliability", "Resiliency & recovery", "Can the system recover from failures?"],
["Security", "Threat protection", "How do we protect data and systems?"],
["Cost Optimization", "Manage costs", "Are we spending only what we need?"],
["Operational Excellence", "Operations processes", "Can we monitor and improve?"],
["Performance Efficiency", "Scaling & performance", "Does it meet demand efficiently?"],
], col_widths=[Inches(2.5), Inches(3.5), Inches(5.5)],
top=Inches(1.5))
# Slide 8: How to Use This Session
s = new_content_slide("How to Use This Session", 0)
add_bullet_list(s, [
"Decision Matrices: Map directly to exam question logic -- learn the selection criteria, not just the services.",
"Architecture Diagrams: Visual patterns that appear in case studies -- understand component relationships.",
"Exam Tips (pink slides): Specific test-day tactics -- common traps, default answers, and elimination strategies.",
"Demo Callouts: Hands-on exercises you can replicate in your own Azure subscription or sandbox.",
"Review Questions: Scenario-based practice at the end of each segment -- mirrors the actual exam format.",
"Ask Questions: Use the chat throughout. Real-time Q&A helps everyone learn.",
], font_size=18)
# ---------- SECTION 1: IDENTITY, GOVERNANCE & MONITORING (Slides 9-27) ----------
# Slide 9: Divider
new_divider_slide("Identity, Governance\n& Monitoring", "25-30% of Exam", 1)
# Slide 10: Learning Objectives
s = new_content_slide("Segment 1 Learning Objectives", 1)
add_bullet_list(s, [
"Design authentication and authorization solutions using Microsoft Entra ID",
"Design governance solutions with management groups, policies, and RBAC",
"Design monitoring and logging solutions with Azure Monitor, Sentinel, and Application Insights",
"Design secure access to secrets, keys, and certificates using Key Vault with managed identities",
], font_size=20)
# Slide 11: Entra ID Core Concepts
s = new_content_slide("Microsoft Entra ID Core Concepts", 1)
add_bullet_list(s, [
"Tenant: A dedicated instance of Entra ID representing an organization. Each tenant has a unique directory.",
"Directory: The identity store within a tenant -- users, groups, applications, service principals.",
"Subscription: A billing boundary linked to one tenant. Multiple subscriptions per tenant, one tenant per subscription.",
"CRITICAL RENAME: 'Azure AD' is now 'Microsoft Entra ID'. The exam uses the NEW naming exclusively.",
"Entra External ID: Replaces Azure AD B2C for consumer-facing identity scenarios.",
], font_size=18)
# Slide 12: Authentication Decision Matrix
s = new_content_slide("Authentication Decision Matrix", 1)
add_table(s, [
["Scenario", "Solution", "Key Detail"],
["Employees only", "Microsoft Entra ID", "Cloud-native, MFA, Conditional Access"],
["Partner organizations", "Entra External ID (B2B)", "Invite external users, cross-tenant access"],
["Consumer applications", "Entra External ID (B2C)", "Custom sign-up/sign-in, social logins"],
["Hybrid identity", "Entra Connect / Cloud Sync", "Sync on-prem AD to cloud"],
["Federated / complex", "AD FS", "On-prem federation, claims-based auth"],
["Multi-tenant SaaS", "Multi-tenant app registration", "Consent framework, service principals"],
], col_widths=[Inches(2.8), Inches(3.8), Inches(5)])
# Slide 13: Hybrid Identity Options
s = new_content_slide("Hybrid Identity Options", 1)
add_table(s, [
["Method", "How It Works", "Passwords in Cloud?", "Complexity", "Recommendation"],
["Password Hash Sync (PHS)", "Hash of hash synced to Entra ID", "Yes (hashed)", "Low", "DEFAULT -- start here"],
["Pass-Through Auth (PTA)", "Auth request forwarded to on-prem", "No", "Medium", "Regulatory requirement"],
["Federation (AD FS)", "Redirect to on-prem AD FS farm", "No", "High", "Only if required"],
], col_widths=[Inches(2.2), Inches(3.3), Inches(2.2), Inches(1.5), Inches(2.5)],
font_size=13)
_add_textbox(s, Inches(0.8), Inches(4.5), Inches(11), Inches(1),
"Exam Default: PHS is the recommended starting point. Cloud Sync is the modern lightweight alternative to Entra Connect.",
font_size=16, bold=True, color=AZURE_BLUE)
# Slide 14: Conditional Access Architecture
s = new_content_slide("Conditional Access Architecture", 1,
"Policy = Assignments + Conditions + Controls")
add_bullet_list(s, [
"Signals (Inputs): User/group, Cloud app, Location (named/IP), Device platform & state, Sign-in risk level (Identity Protection)",
"Decisions (Grant Controls): Allow, Block, Require MFA, Require compliant device, Require Hybrid Entra joined, Require app protection policy",
"Session Controls: App-enforced restrictions, Conditional Access App Control (MCAS), Sign-in frequency, Persistent browser session",
"Key Principle: Policies are additive. If ANY policy blocks, access is denied. Most restrictive policy wins.",
"Report-Only Mode: Test policies without enforcement. Always use this before enabling enforcement.",
"Named Locations: Define trusted IPs/countries. Combine with policies to skip MFA on corporate networks.",
], font_size=17)
# Slide 15: Managed Identity Deep Dive
s = new_content_slide("Managed Identity Deep Dive", 1,
"Eliminate secrets from your code")
add_table(s, [
["Attribute", "System-Assigned", "User-Assigned"],
["Lifecycle", "Tied to resource (deleted together)", "Independent (manage separately)"],
["Sharing", "1:1 with resource", "1:Many across resources"],
["Use Case", "Single resource needs access", "Multiple resources share same identity"],
["Creation", "Enable on the resource", "Create as standalone resource, then assign"],
["Example", "VM accessing Key Vault", "Multiple VMs accessing same storage account"],
["Exam Default", "Start with system-assigned", "Use when sharing is needed"],
], col_widths=[Inches(2), Inches(4.8), Inches(4.8)],
font_size=13)
# Slide 16: RBAC & Authorization
s = new_content_slide("RBAC & Authorization", 1)
add_bullet_list(s, [
"Scope Hierarchy: Management Group > Subscription > Resource Group > Resource. Permissions inherit downward.",
"Built-in Roles: Owner (full + assign), Contributor (full - assign), Reader (view only), User Access Admin (assign only).",
"Custom Roles: JSON definition with Actions, NotActions, DataActions, NotDataActions. Assignable at MG, Sub, or RG scope.",
"Deny Assignments: Override RBAC allows. Created by Blueprints and managed apps. Cannot create directly.",
"Best Practice: Assign roles to GROUPS, not individual users. Use PIM for just-in-time elevation.",
"Scope Tip: Assign at the NARROWEST scope possible. If access is needed at RG level, do not assign at subscription level.",
], font_size=17)
# Slide 17: PIM & Identity Governance
s = new_content_slide("PIM & Identity Governance", 1)
add_bullet_list(s, [
"Privileged Identity Management (PIM): Just-in-time (JIT) role activation. Eligible vs Active assignments.",
"PIM Workflow: User requests activation -> Approval (optional) -> Time-limited access (e.g., 8 hours) -> Auto-expires",
"Access Reviews: Periodic review of who has access to what. Self-review, manager review, or group owner review.",
"Entitlement Management: Access packages bundle resources (groups, apps, SharePoint). Catalog + Policies + Requests.",
"Identity Governance: Lifecycle workflows for joiner/mover/leaver. Automate onboarding and offboarding.",
"Exam Key: PIM requires Entra ID P2 license. Access Reviews also require P2.",
], font_size=17)
# Slide 18: Management Group Hierarchy
s = new_content_slide("Management Group Hierarchy", 1,
"Cloud Adoption Framework Landing Zone")
add_bullet_list(s, [
"Root MG: Tenant Root Group (auto-created, do NOT apply restrictive policies here)",
" Platform MG: Shared services -- contains Identity, Management, and Connectivity subscriptions",
" Identity MG: Domain controllers, Entra Connect servers",
" Management MG: Log Analytics, Automation, Monitor resources",
" Connectivity MG: Hub VNet, VPN/ExpressRoute gateways, DNS, Firewall",
" Landing Zones MG: Workload subscriptions",
" Corp MG: Internal apps connected to corporate network",
" Online MG: Internet-facing apps",
" Sandbox MG: Experimentation subscriptions (no connectivity to corp)",
" Decommissioned MG: Subscriptions being retired",
"Depth Limit: 6 levels deep (excluding Root and subscription level)",
], font_size=16)
# Slide 19: Azure Policy Deep Dive
s = new_content_slide("Azure Policy Deep Dive", 1)
add_table(s, [
["Effect", "What It Does", "When to Use"],
["Deny", "Blocks non-compliant resource creation/update", "Enforce hard requirements"],
["Audit", "Logs non-compliance, allows creation", "Monitor before enforcing"],
["Append", "Adds fields to resource during creation", "Add tags, IP rules"],
["DeployIfNotExists", "Deploys related resource if missing", "Auto-configure diagnostics"],
["Modify", "Changes properties on existing resources", "Add/update tags, settings"],
["AuditIfNotExists", "Audits if related resource is missing", "Check for diagnostics settings"],
["Disabled", "Policy exists but not enforced", "Testing, temporary disable"],
], col_widths=[Inches(2.5), Inches(4.5), Inches(4.5)],
font_size=13)
_add_textbox(s, Inches(0.8), Inches(5.5), Inches(11), Inches(0.5),
"Initiative = Collection of policies assigned together. Use initiatives for compliance standards (e.g., CIS, NIST).",
font_size=15, bold=True, color=AZURE_BLUE)
# Slide 20: Governance Decision Matrix
s = new_content_slide("Governance Decision Matrix", 1)
add_table(s, [
["Requirement", "Tool", "Details"],
["Enforce naming conventions", "Azure Policy (Deny)", "Regex patterns on resource names"],
["Control costs", "Budgets + Cost Management + Policy", "Alerts at thresholds, deny expensive SKUs"],
["Regulatory compliance", "Policy Initiatives", "Built-in: CIS, NIST, ISO, PCI DSS"],
["Resource organization", "Tags + Management Groups", "Cost center, environment, owner tags"],
["Prevent accidental deletion", "Resource Locks", "CanNotDelete or ReadOnly locks"],
["Standardize deployments", "Blueprints / Template Specs", "Versioned, governed ARM/Bicep templates"],
["Track changes", "Activity Log + Change Analysis", "Who changed what and when"],
], col_widths=[Inches(2.8), Inches(3.5), Inches(5.2)],
font_size=13)
# Slide 21: Azure Monitor Architecture
s = new_content_slide("Azure Monitor Architecture", 1)
add_bullet_list(s, [
"Data Sources: Applications (App Insights), OS (agents), Azure resources (diagnostics), Subscriptions (activity log), Tenant (Entra logs)",
"Collection: Data Collection Rules (DCR) and Diagnostic Settings route data to destinations",
"Destinations: Log Analytics workspace (KQL queries), Metrics (near real-time), Storage (archive), Event Hub (streaming)",
"Analysis: KQL queries, Workbooks (visual dashboards), Alerts (action groups: email, SMS, webhook, Logic App, Function)",
"Azure Monitor Agent (AMA): Replaces legacy agents (MMA, Telegraf). Single agent for Windows and Linux. Uses DCRs.",
"Log Analytics Retention: 30 days free interactive, up to 730 days, archive up to 12 years.",
], font_size=17)
# Slide 22: Application Insights & Observability
s = new_content_slide("Application Insights & Observability", 1)
add_bullet_list(s, [
"APM: Application Performance Management -- automatic instrumentation for .NET, Java, Node.js, Python",
"Distributed Tracing: End-to-end transaction tracking across microservices. Correlates with Operation ID.",
"Live Metrics: Real-time stream of requests, failures, dependencies. Zero-cost when viewing.",
"Availability Tests: URL ping (global), Standard test (status + SSL + content), Custom TrackAvailability.",
"Smart Detection: AI-driven anomaly detection for failures, performance degradation, memory leaks.",
"Application Map: Visual topology of dependencies. Shows call rates, latency, failure rates between components.",
"Workspace-based: Always use workspace-based App Insights (not classic). Required for cross-resource queries.",
], font_size=17)
# Slide 23: Microsoft Sentinel
s = new_content_slide("Microsoft Sentinel", 1, "Cloud-Native SIEM + SOAR")
add_bullet_list(s, [
"SIEM: Security Information and Event Management -- collect, detect, investigate, respond",
"SOAR: Security Orchestration, Automation and Response -- playbooks (Logic Apps) for automated response",
"Data Connectors: 100+ built-in. Microsoft 365, Entra ID, Defender, AWS, GCP, firewalls, custom (CEF/Syslog)",
"Analytics Rules: Scheduled (KQL), Microsoft Security (from Defender), Fusion (ML correlation), NRT (near real-time)",
"Workbooks: Interactive dashboards for SOC analysts. Built-in templates for common scenarios.",
"When Sentinel vs Defender for Cloud: Sentinel = full SIEM for SOC teams. Defender = CSPM + workload protection. Use both together.",
], font_size=17)
# Slide 24: Key Vault Patterns
s = new_content_slide("Key Vault Patterns", 1)
add_bullet_list(s, [
"Access Model: RBAC (recommended) vs Vault Access Policies (legacy). RBAC integrates with Entra ID roles.",
"Soft Delete: Enabled by default, 7-90 day retention. CANNOT be disabled on new vaults.",
"Purge Protection: When enabled, soft-deleted items cannot be force-purged during retention period. Enable for production.",
"Managed Identity Access: Grant Key Vault Secrets User role to managed identity. Zero secrets in code.",
"Secret Rotation: Event Grid notifies when secret nears expiry -> Function rotates -> Stores new secret in Key Vault.",
"Key Types: RSA & EC keys, software or HSM-backed. Premium SKU for HSM. Managed HSM for FIPS 140-2 L3.",
"Network Security: Private Endpoint for VNet access. Firewall rules for IP restrictions. Disable public access.",
], font_size=16)
# Slide 25: Demo Callout
s = new_content_slide("Segment 1 Demos", 1)
add_demo_slide(s, [
"Conditional Access Policy: Create a policy requiring MFA for Azure portal access from non-corporate locations",
"Azure Policy Assignment: Assign 'Require tag on resource group' policy with Deny effect to a test subscription",
"Managed Identity + Key Vault: Create a VM with system-assigned identity, grant Key Vault access, retrieve secret from code",
"Log Analytics KQL: Query sign-in logs to find failed authentications and risky sign-ins",
])
# Slide 26: Exam Tips
s = new_exam_tip_slide("Segment 1 Exam Tips", 1)
add_bullet_list(s, [
"PHS is the DEFAULT hybrid identity recommendation. Choose PTA only if passwords must never leave on-prem.",
"Managed Identity eliminates secrets. If a question mentions connection strings or API keys, Managed Identity is likely the answer.",
"Policy > Locks for enforcement. Locks prevent deletion; Policies prevent non-compliant creation.",
"PIM provides JIT access. If a question asks about 'least privilege for admins', PIM is the answer.",
"Sentinel = SIEM. If the question mentions 'correlate security events across sources', think Sentinel.",
"Log Analytics retention: 30 days free interactive. Archive tier for long-term at lower cost.",
], font_size=18, top=Inches(1.5), color=DARK_BLUE)
# Slide 27: Review Questions
s = new_content_slide("Segment 1 Review Questions", 1)
add_review_questions(s, [
("Your company requires that IT admins only have elevated privileges when performing admin tasks, with approval from a security manager. What should you implement?",
"PIM (Privileged Identity Management) with eligible assignments and approval workflow. Admins request activation, security manager approves, access auto-expires."),
("A web application needs to read secrets from Key Vault without storing any credentials in code or configuration. The app runs on Azure App Service. What do you recommend?",
"Enable system-assigned managed identity on the App Service. Grant it the 'Key Vault Secrets User' RBAC role on the Key Vault. Use DefaultAzureCredential in code."),
("You need to enforce that all resources in a subscription must have a 'CostCenter' tag. Resources without the tag should be blocked from creation. What do you use?",
"Azure Policy with the 'Require a tag on resources' built-in definition, set with Deny effect. Assign at subscription scope."),
])
# ---------- SECTION 2: DATA STORAGE (Slides 28-45) ----------
# Slide 28: Divider
new_divider_slide("Data Storage Solutions", "20-25% of Exam", 2)
# Slide 29: Learning Objectives
s = new_content_slide("Segment 2 Learning Objectives", 2)
add_bullet_list(s, [
"Design storage solutions for relational data (SQL Database, SQL MI, SQL on VM)",
"Design for semi-structured and NoSQL data (Cosmos DB, Table Storage)",
"Design for unstructured data (Blob Storage, ADLS Gen2, Azure Files)",
"Design data integration and analytics architectures (ADF, Synapse, Event Hubs)",
], font_size=20)
# Slide 30: Storage Decision Tree
s = new_content_slide("Storage Decision Tree", 2)
add_table(s, [
["Data Type", "Service", "When to Use"],
["Structured / Relational", "Azure SQL Database", "Cloud-native, auto-scaling, managed"],
["Structured / High compat", "Azure SQL Managed Instance", "SQL Server compat, VNet native"],
["Structured / Full control", "SQL Server on Azure VM", "OS-level access, legacy features"],
["Semi-structured / Global", "Azure Cosmos DB", "Multi-model, global dist, low latency"],
["Semi-structured / Simple", "Table Storage", "Key-value, low cost, simple queries"],
["Unstructured / Objects", "Blob Storage", "Images, documents, backups, media"],
["Unstructured / Analytics", "ADLS Gen2", "Big data, hierarchical namespace, Spark"],
["File shares", "Azure Files", "SMB/NFS, lift-and-shift, AD DS integration"],
["High-perf file shares", "Azure NetApp Files", "SAP, HPC, Oracle, low-latency NFS"],
], col_widths=[Inches(2.5), Inches(3.5), Inches(5.5)],
font_size=13, top=Inches(1.4))
# Slide 31: Storage Redundancy
s = new_content_slide("Storage Redundancy Matrix", 2)
add_table(s, [
["Option", "Copies", "Scope", "Use Case", "Read Secondary?"],
["LRS", "3", "Single datacenter", "Dev/test, non-critical", "No"],
["ZRS", "3", "3 availability zones", "Production, zone resilience", "No"],
["GRS", "6", "2 regions (primary+secondary)", "DR, cross-region", "No (failover only)"],
["GZRS", "6", "3 zones + secondary region", "Mission-critical", "No (failover only)"],
["RA-GRS", "6", "2 regions", "DR + read from secondary", "Yes (read-only)"],
["RA-GZRS", "6", "3 zones + secondary region", "Maximum resilience + read", "Yes (read-only)"],
], col_widths=[Inches(1.5), Inches(1), Inches(3), Inches(3.5), Inches(2.5)],
font_size=13)
# Slide 32: Blob Tiers
s = new_content_slide("Blob Storage Tiers & Lifecycle", 2)
add_table(s, [
["Tier", "Access", "Min Duration", "Retrieval Cost", "Storage Cost"],
["Hot", "Frequent", "None", "Low", "Highest"],
["Cool", "Infrequent (30+ days)", "30 days", "Medium", "Lower"],
["Cold", "Rarely (90+ days)", "90 days", "Higher", "Lower still"],
["Archive", "Offline (180+ days)", "180 days", "Highest (rehydrate)", "Lowest"],
], col_widths=[Inches(1.5), Inches(2.8), Inches(2), Inches(2.5), Inches(2.5)],
font_size=14)
_add_textbox(s, Inches(0.8), Inches(4.5), Inches(11), Inches(1.5),
"Lifecycle Management: Automate tier transitions with rules.\n"
"Example: Move to Cool after 30 days, Archive after 90, delete after 365.\n"
"Rehydration: Archive requires rehydration (Standard: up to 15 hours, High priority: <1 hour).",
font_size=16, color=DARK_BLUE)
# Slide 33: Azure Files & NetApp
s = new_content_slide("Azure Files & NetApp Files", 2)
add_bullet_list(s, [
"Azure Files: Managed SMB (445) and NFS (2049) file shares in the cloud",
"Tiers: Premium (SSD, low latency), Transaction Optimized (HDD), Hot, Cool",
"AD DS Integration: Entra Domain Services or on-prem AD DS for identity-based access with NTFS permissions",
"Azure File Sync: Sync on-prem file servers with Azure Files. Cloud tiering frees local disk space. Multi-site sync.",
"Azure NetApp Files: Enterprise NAS -- sub-millisecond latency, Oracle, SAP HANA, HPC workloads",
"NetApp tiers: Standard, Premium, Ultra. Capacity pools with volume allocation.",
"Exam tip: Azure Files for general file shares; NetApp Files for high-performance or SAP/Oracle.",
], font_size=17)
# Slide 34: Relational DB Decision Matrix
s = new_content_slide("Relational Database Decision Matrix", 2)
add_table(s, [
["Feature", "SQL Database", "SQL Managed Instance", "SQL on VM"],
["Managed?", "Fully managed PaaS", "Fully managed PaaS", "IaaS (you manage OS)"],
["SQL Compat", "~95% (some gaps)", "~99% (near full)", "100% (full engine)"],
["VNet Native", "No (PE/service endpoint)", "Yes (in VNet)", "Yes (in VNet)"],
["SQL Agent", "Elastic Jobs", "Yes", "Yes"],
["Cross-DB Queries", "Elastic Query", "Yes", "Yes"],
["CLR / Linked Servers", "No", "Yes", "Yes"],
["Best For", "New cloud apps", "Migration / compat", "Legacy / full control"],
["Cost Model", "DTU or vCore", "vCore only", "VM + license"],
], col_widths=[Inches(2.3), Inches(3), Inches(3), Inches(3.2)],
font_size=12, top=Inches(1.4))
# Slide 35: Azure SQL Architecture Patterns
s = new_content_slide("Azure SQL Architecture Patterns", 2)
add_bullet_list(s, [
"Elastic Pools: Share resources across multiple databases. Ideal for multi-tenant SaaS with variable workloads.",
"Hyperscale: Up to 100 TB, rapid scale-out reads (up to 4 read replicas), fast backups regardless of size.",
"Serverless: Auto-pause after idle period, auto-scale compute. Pay only for compute used. Best for intermittent workloads.",
"DTU vs vCore: DTU = bundled (CPU+IO+memory). vCore = choose independently. vCore recommended for new deployments.",
"Azure Hybrid Benefit: Use existing SQL Server licenses for ~55% savings. Apply at server or pool level.",
"Ledger tables: Tamper-evident tables with blockchain-like verification. Regulatory and audit scenarios.",
], font_size=17)
# Slide 36: Cosmos DB Design
s = new_content_slide("Cosmos DB Design", 2, "Global Distribution & Multi-Model")
add_bullet_list(s, [
"APIs: NoSQL (native, recommended), MongoDB, Cassandra, Gremlin (graph), Table, PostgreSQL",
"Global Distribution: Multi-region writes for active-active. Single-region writes + multi-region reads for active-passive.",
"Partition Key Rules: (1) High cardinality -- many distinct values. (2) Even distribution -- no hot partitions. (3) Included in queries -- avoid cross-partition queries.",
"RU/s: Request Units per second. Provisioned (predictable) or Autoscale (variable, min 10% of max). Serverless for dev/test.",
"Item Size: Max 2 MB per item. Design for smaller documents with proper denormalization.",
"Change Feed: Stream of changes for event-driven patterns. Trigger Functions, update caches, sync data.",
], font_size=17)
# Slide 37: Cosmos DB Consistency
s = new_content_slide("Cosmos DB Consistency Spectrum", 2)
add_table(s, [
["Level", "Guarantee", "Latency", "Throughput", "Use Case"],
["Strong", "Linearizable reads", "Highest", "Lowest", "Financial transactions"],
["Bounded Staleness", "Reads lag by k versions or t time", "High", "Lower", "Leaderboards, counters"],
["Session (DEFAULT)", "Read-your-own-writes in session", "Medium", "Medium", "Most applications"],
["Consistent Prefix", "Reads never see out-of-order writes", "Lower", "Higher", "Social updates"],
["Eventual", "No ordering guarantee", "Lowest", "Highest", "Counters, likes, non-critical"],
], col_widths=[Inches(2), Inches(3), Inches(1.5), Inches(1.5), Inches(3.5)],
font_size=13)
_add_textbox(s, Inches(0.8), Inches(5.0), Inches(11), Inches(0.5),
"Exam Default: Session consistency is the default and suits most applications.",
font_size=16, bold=True, color=AZURE_BLUE)
# Slide 38: Data Platform Architecture
s = new_content_slide("Data Platform Architecture", 2, "Medallion Pattern")
add_bullet_list(s, [
"Bronze Layer (Raw): Ingest raw data as-is. ADLS Gen2 with hierarchical namespace. Parquet or Delta format.",
"Silver Layer (Cleansed): Validated, deduplicated, conformed. Schema enforcement. Business logic applied.",
"Gold Layer (Curated): Aggregated, business-ready. Star schema for BI. Served via Synapse SQL pools or Power BI.",
"ADLS Gen2: Storage foundation. Hierarchical namespace + Blob APIs. ACLs for fine-grained access.",
"Synapse Analytics: Unified analytics -- serverless SQL, dedicated SQL pools, Spark, Pipelines, Data Explorer.",
"Microsoft Fabric: Next-gen unified analytics platform. Integrates Power BI, Data Factory, Synapse, Real-Time Analytics.",
], font_size=17)
# Slide 39: Data Integration
s = new_content_slide("Data Integration", 2)
add_table(s, [
["Service", "Pattern", "Use Case"],
["Azure Data Factory", "ETL / ELT orchestration", "Batch data movement, 90+ connectors, mapping data flows"],
["Synapse Pipelines", "Same ADF engine", "Data integration within Synapse workspace"],
["Event Hubs", "Streaming ingestion", "Millions of events/sec, Kafka compatible, capture to storage"],
["Stream Analytics", "Real-time processing", "SQL-like queries on streams, windowing functions"],
["Azure Databricks", "Spark-based analytics", "ML, data engineering, collaborative notebooks"],
["Logic Apps", "Integration workflows", "B2B, SaaS connectors, low-code orchestration"],
], col_widths=[Inches(2.5), Inches(3), Inches(6)])
# Slide 40: Data Protection & Encryption
s = new_content_slide("Data Protection & Encryption", 2)
add_bullet_list(s, [
"At Rest: Storage Service Encryption (SSE) for blobs/files. TDE for SQL databases. Always on by default.",
"In Transit: TLS 1.2+ enforced. Minimum TLS version configurable on storage accounts.",
"In Use: Always Encrypted for SQL (client-side encryption). Confidential computing for VMs.",
"Key Management: Microsoft-managed keys (MMK, default) or Customer-managed keys (CMK, Key Vault).",
"CMK Benefits: You control rotation, you can revoke access. Required by some compliance standards.",
"Double Encryption: Infrastructure encryption adds second layer. Enable at storage account creation.",
], font_size=17)
# Slide 41: Private Endpoints for Data
s = new_content_slide("Private Endpoints for Data", 2,
"Zero Trust Networking for Data Services")
add_bullet_list(s, [
"Architecture: VNet -> Private Endpoint (NIC with private IP) -> Private DNS Zone -> Data Service",
"DNS Resolution: privatelink.blob.core.windows.net resolves to PE private IP, not public IP",
"Disable Public Access: After PE is configured, disable public network access on the service",
"On-prem Access: Conditional DNS forwarder forwards privatelink.* queries to Azure DNS (168.63.129.16)",
"Supported Services: Storage, SQL, Cosmos DB, Key Vault, App Config, Event Hubs, Service Bus, and 60+ more",
"Cost: ~$7.30/month per PE + data processing charges. Small cost for significant security improvement.",
], font_size=17)
# Slide 42: Data Residency & Purview
s = new_content_slide("Data Residency & Microsoft Purview", 2)
add_bullet_list(s, [
"Sovereign Regions: Azure Government (US), Azure China (21Vianet). Data stays within sovereign boundary.",
"Data Residency: Choose regions that meet regulatory requirements (GDPR, LGPD, etc.). Paired regions for DR.",
"Microsoft Purview: Unified data governance -- data catalog, lineage tracking, classifications, access policies.",
"Data Catalog: Scan and classify data across Azure, on-prem, and multi-cloud. Automatic sensitivity labels.",
"Data Lineage: Visual tracking of data flow from source to consumption. Supports ADF, Synapse, Power BI.",
"Exam Context: Know when Purview is the answer -- 'discover, classify, govern data estate' keywords.",
], font_size=17)
# Slide 43: Demo
s = new_content_slide("Segment 2 Demos", 2)
add_demo_slide(s, [
"Storage Lifecycle: Configure blob lifecycle management to transition from Hot to Cool to Archive",
"Cosmos DB Configuration: Create a Cosmos DB account with NoSQL API, select partition key, set consistency",
"Private Endpoint for SQL: Create a PE for Azure SQL Database with private DNS zone integration",
"Data Factory Pipeline: Build a simple copy pipeline from Blob Storage to SQL Database",
])
# Slide 44: Exam Tips
s = new_exam_tip_slide("Segment 2 Exam Tips", 2)
add_bullet_list(s, [
"ZRS for zone resilience in production. LRS only for dev/test or non-critical data.",
"SQL Managed Instance is the migration sweet spot -- near 100% SQL Server compatibility with full PaaS management.",
"Session consistency is the Cosmos DB default. Strong only when absolutely required (higher latency, lower throughput).",
"Private Endpoints = Zero Trust for data. If the question mentions 'secure access' to a data service, think PE.",
"Archive tier = offline. You CANNOT read archived blobs without rehydrating first. Plan for up to 15 hours.",
"Partition key rules: High cardinality, even distribution, included in WHERE clause of queries.",
], font_size=18, top=Inches(1.5), color=DARK_BLUE)
# Slide 45: Review
s = new_content_slide("Segment 2 Review Questions", 2)
add_review_questions(s, [
("A global e-commerce app needs sub-10ms read latency worldwide. Data is semi-structured product catalog. Users mostly read their own recent changes. Which database and consistency?",
"Cosmos DB with NoSQL API, multi-region reads, Session consistency (default). Partition key = productCategory or productId depending on query patterns."),
("You are migrating a SQL Server 2019 database that uses SQL Agent jobs, cross-database queries, and CLR assemblies. The team wants full PaaS management. What do you recommend?",
"Azure SQL Managed Instance. It supports SQL Agent, cross-DB queries, and CLR -- features not available in SQL Database. Fully managed PaaS."),
("A healthcare company must store patient images for 7 years at minimum cost, with rare access. Images must never leave the US East region. What storage design?",
"Blob Storage in US East, LRS or ZRS. Lifecycle policy: move to Archive tier after 30 days. RA-GRS NOT needed (single region requirement). Use immutability policies for retention."),
])
# ---------- SECTION 3: BCDR (Slides 46-60) ----------
new_divider_slide("Business Continuity\n& High Availability", "15-20% of Exam", 3)
# Slide 47
s = new_content_slide("Segment 3 Learning Objectives", 3)
add_bullet_list(s, [
"Design for high availability using availability zones, sets, and load balancing",
"Design backup and recovery solutions with Azure Backup and site recovery",
"Design disaster recovery strategies for multi-region architectures",
"Calculate composite SLAs and map RTO/RPO to Azure services",
], font_size=20)
# Slide 48
s = new_content_slide("RTO vs RPO Fundamentals", 3)
add_bullet_list(s, [
"RPO (Recovery Point Objective): Maximum acceptable DATA LOSS measured in time. How much data can you afford to lose?",
"RTO (Recovery Time Objective): Maximum acceptable DOWNTIME measured in time. How quickly must you recover?",
"Example: RPO = 1 hour means you can lose up to 1 hour of data. RTO = 4 hours means you must recover within 4 hours.",
"Cost Relationship: Lower RPO/RTO = higher cost. Near-zero RPO requires synchronous replication. Near-zero RTO requires hot standby.",
"Business drives the numbers: Finance and stakeholders define acceptable RPO/RTO, architects design to meet them.",
"Exam Pattern: Scenario gives RPO/RTO requirements -> you select the appropriate Azure service/configuration.",
], font_size=17)
# Slide 49
s = new_content_slide("RTO / RPO Decision Matrix", 3)
add_table(s, [
["RPO Requirement", "Solution", "RTO Requirement", "Solution"],
["RPO = 0 (zero loss)", "Sync replication / AZ", "RTO < 1 min", "Auto-failover (SQL FG, Cosmos)"],
["RPO < 15 min", "Continuous replication / ASR", "RTO < 1 hour", "Hot standby / ASR failover"],
["RPO < 1 hour", "Frequent backups / log shipping", "RTO < 4 hours", "Warm standby / ASR"],
["RPO < 24 hours", "Daily backups", "RTO < 24 hours", "Cold / backup restore"],
["RPO = days", "Weekly backups / geo-restore", "RTO = days", "Rebuild from backup"],
], col_widths=[Inches(2.5), Inches(3.5), Inches(2.5), Inches(3)])
# Slide 50
s = new_content_slide("Availability Zones vs Availability Sets", 3)
add_table(s, [
["Attribute", "Availability Zones", "Availability Sets"],
["Protection Level", "Datacenter failure", "Rack/hardware failure"],
["SLA", "99.99%", "99.95%"],
["Spread", "3 physically separate datacenters", "Up to 3 fault + 20 update domains"],
["Network Latency", "< 2 ms between zones", "N/A (same datacenter)"],
["Cost", "Cross-zone data transfer charged", "No extra cost"],
["Example", "VMs in Zone 1, 2, 3 + zone LB", "VMs in FD 0, 1, 2 behind LB"],
["Recommendation", "DEFAULT for production", "Legacy or regions without zones"],
], col_widths=[Inches(2.5), Inches(4.5), Inches(4.5)],
font_size=13)
# Slide 51
s = new_content_slide("SLA Calculation", 3)
add_bullet_list(s, [
"Single VM SLAs: Premium SSD = 99.9%, Availability Set = 99.95%, Availability Zones = 99.99%",
"Composite SLA (serial): Multiply individual SLAs. Web (99.95%) x DB (99.99%) = 99.94%",
"Composite SLA (parallel/redundant): 1 - (1-SLA_A) x (1-SLA_B). Two 99.9% instances = 99.9999%",
"Example: Web tier (2 VMs in zones, 99.99%) x App tier (99.95%) x SQL (99.99%) = 99.93%",
"Impact: 99.9% = ~8.76 hrs/year downtime. 99.99% = ~52 min/year. 99.95% = ~4.38 hrs/year.",
"Strategy: Add redundancy to the WEAKEST component. That is where you get the most SLA improvement.",
], font_size=17)
# Slide 52
s = new_content_slide("Azure Backup Architecture", 3)
add_bullet_list(s, [
"Recovery Services Vault: Central management for backup and ASR. Supports VMs, SQL, Files, SAP, on-prem.",
"Backup Vault: Newer vault type for Azure Disks, Blobs, PostgreSQL, AKS. Simpler management.",
"Agents: VM Extension (Azure VMs), MARS (files/folders to cloud), MABS/DPM (on-prem workloads).",
"Policies: Daily/weekly/monthly/yearly retention. Customize per workload. Instant restore from snapshots.",
"Storage Redundancy: LRS (default), ZRS (zone protection), GRS (cross-region). Choose at vault creation.",
"Soft Delete: 14 additional days to recover deleted backup data. Enhanced soft delete for ransomware protection.",
"Cross-Region Restore: Enable on GRS vaults to restore in paired region even when primary is healthy.",
], font_size=16)
# Slide 53
s = new_content_slide("Azure Site Recovery (ASR)", 3)
add_bullet_list(s, [
"Azure-to-Azure: Replicate VMs between Azure regions. RPO ~30 seconds for VMs.",
"On-prem-to-Azure: VMware VMs, Hyper-V VMs, physical servers. Process Server handles replication.",
"Test Failover: Failover to isolated VNet. NO impact to production. Validate DR plan safely.",
"Planned Failover: Zero data loss. Shut down source, replicate final changes, bring up target.",
"Unplanned Failover: Source is down. Use latest recovery point. Some data loss possible (within RPO).",
"Recovery Plans: Group VMs, define startup order, add scripts/manual actions. Automate entire DR sequence.",
"Key Distinction: ASR = DR (disaster recovery), NOT backup. Use Azure Backup for backup.",
], font_size=17)
# Slide 54
s = new_content_slide("SQL BCDR Options", 3)
add_table(s, [
["Option", "RPO", "Scope", "Auto Failover?", "Best For"],
["Zone Redundant", "0", "Same region, 3 zones", "Automatic", "HA within region"],
["Active Geo-Replication", "~5 seconds", "Up to 4 secondaries, any region", "Manual", "Read scale, custom DR"],
["Auto-Failover Groups", "~5 seconds", "1 secondary region", "Automatic (DNS)", "Production DR"],
["Geo-Restore", "~1 hour", "Paired region", "Manual (restore)", "Budget DR"],
["PITR", "5 min (up to 35 days)", "Same region", "Manual (restore)", "Accidental changes"],
], col_widths=[Inches(2.2), Inches(1.3), Inches(3.5), Inches(2), Inches(2.5)],
font_size=13)
# Slide 55
s = new_content_slide("Multi-Region Patterns", 3)
add_table(s, [
["Pattern", "RTO", "RPO", "Cost", "Complexity", "Use Case"],
["Active-Active", "~0", "~0", "Highest", "Highest", "Global apps, zero downtime"],
["Active-Passive (Hot)", "Minutes", "Near-zero", "High", "Medium", "Critical apps"],
["Active-Passive (Warm)", "Minutes-hours", "Minutes", "Medium", "Medium", "Important apps"],
["Active-Passive (Cold)", "Hours", "Hours", "Low", "Low", "Non-critical"],
], col_widths=[Inches(2.2), Inches(1.3), Inches(1.5), Inches(1.5), Inches(1.5), Inches(3.5)],
font_size=14)
_add_textbox(s, Inches(0.8), Inches(4.5), Inches(11), Inches(1),
"Traffic Routing: Azure Front Door (global HTTP) or Traffic Manager (DNS-based) for multi-region failover.",
font_size=16, bold=True, color=AZURE_BLUE)
# Slide 56
s = new_content_slide("HA Decision Matrix by Service", 3)
add_table(s, [
["Service", "HA Mechanism", "SLA", "Key Configuration"],
["VMs", "Availability Zones + Load Balancer", "99.99%", "Zone-redundant deployment"],
["Azure SQL", "Auto-Failover Groups", "99.99%", "Automatic DNS failover"],
["Cosmos DB", "Multi-region writes", "99.999%", "5-nines with multi-write"],