fedramp-20x-mcp 0.4.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. fedramp_20x_mcp/__init__.py +14 -0
  2. fedramp_20x_mcp/__main__.py +12 -0
  3. fedramp_20x_mcp/data_loader.py +673 -0
  4. fedramp_20x_mcp/prompts/__init__.py +62 -0
  5. fedramp_20x_mcp/prompts/api_design_guide.txt +432 -0
  6. fedramp_20x_mcp/prompts/ato_package_checklist.txt +75 -0
  7. fedramp_20x_mcp/prompts/audit_preparation.txt +592 -0
  8. fedramp_20x_mcp/prompts/authorization_boundary_review.txt +76 -0
  9. fedramp_20x_mcp/prompts/azure_ksi_automation.txt +997 -0
  10. fedramp_20x_mcp/prompts/continuous_monitoring_setup.txt +61 -0
  11. fedramp_20x_mcp/prompts/documentation_generator.txt +499 -0
  12. fedramp_20x_mcp/prompts/gap_analysis.txt +25 -0
  13. fedramp_20x_mcp/prompts/initial_assessment_roadmap.txt +202 -0
  14. fedramp_20x_mcp/prompts/ksi_implementation_priorities.txt +283 -0
  15. fedramp_20x_mcp/prompts/migration_from_rev5.txt +440 -0
  16. fedramp_20x_mcp/prompts/quarterly_review_checklist.txt +231 -0
  17. fedramp_20x_mcp/prompts/significant_change_assessment.txt +50 -0
  18. fedramp_20x_mcp/prompts/vendor_evaluation.txt +349 -0
  19. fedramp_20x_mcp/prompts/vulnerability_remediation_timeline.txt +45 -0
  20. fedramp_20x_mcp/server.py +270 -0
  21. fedramp_20x_mcp/templates/__init__.py +75 -0
  22. fedramp_20x_mcp/templates/bicep/afr.txt +33 -0
  23. fedramp_20x_mcp/templates/bicep/cna.txt +48 -0
  24. fedramp_20x_mcp/templates/bicep/generic.txt +47 -0
  25. fedramp_20x_mcp/templates/bicep/iam.txt +211 -0
  26. fedramp_20x_mcp/templates/bicep/mla.txt +82 -0
  27. fedramp_20x_mcp/templates/bicep/rpl.txt +44 -0
  28. fedramp_20x_mcp/templates/bicep/svc.txt +54 -0
  29. fedramp_20x_mcp/templates/code/generic_csharp.txt +65 -0
  30. fedramp_20x_mcp/templates/code/generic_powershell.txt +65 -0
  31. fedramp_20x_mcp/templates/code/generic_python.txt +63 -0
  32. fedramp_20x_mcp/templates/code/iam_csharp.txt +150 -0
  33. fedramp_20x_mcp/templates/code/iam_powershell.txt +162 -0
  34. fedramp_20x_mcp/templates/code/iam_python.txt +224 -0
  35. fedramp_20x_mcp/templates/code/mla_python.txt +124 -0
  36. fedramp_20x_mcp/templates/terraform/afr.txt +29 -0
  37. fedramp_20x_mcp/templates/terraform/cna.txt +50 -0
  38. fedramp_20x_mcp/templates/terraform/generic.txt +40 -0
  39. fedramp_20x_mcp/templates/terraform/iam.txt +219 -0
  40. fedramp_20x_mcp/templates/terraform/mla.txt +29 -0
  41. fedramp_20x_mcp/templates/terraform/rpl.txt +32 -0
  42. fedramp_20x_mcp/templates/terraform/svc.txt +46 -0
  43. fedramp_20x_mcp/tools/__init__.py +167 -0
  44. fedramp_20x_mcp/tools/definitions.py +154 -0
  45. fedramp_20x_mcp/tools/documentation.py +155 -0
  46. fedramp_20x_mcp/tools/enhancements.py +2256 -0
  47. fedramp_20x_mcp/tools/evidence.py +701 -0
  48. fedramp_20x_mcp/tools/export.py +753 -0
  49. fedramp_20x_mcp/tools/ksi.py +90 -0
  50. fedramp_20x_mcp/tools/requirements.py +163 -0
  51. fedramp_20x_mcp-0.4.8.dist-info/METADATA +877 -0
  52. fedramp_20x_mcp-0.4.8.dist-info/RECORD +55 -0
  53. fedramp_20x_mcp-0.4.8.dist-info/WHEEL +4 -0
  54. fedramp_20x_mcp-0.4.8.dist-info/entry_points.txt +2 -0
  55. fedramp_20x_mcp-0.4.8.dist-info/licenses/LICENSE +27 -0
@@ -0,0 +1,753 @@
1
+ """
2
+ FedRAMP 20x MCP Server - Export Tools
3
+
4
+ This module contains tool implementation functions for export.
5
+ """
6
+ import json
7
+ import logging
8
+ from typing import Any, Optional
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ async def export_to_excel(
13
+ export_type: str,
14
+ output_path: Optional[str] = None
15
+ ) -> str:
16
+ """
17
+ Export FedRAMP 20x data to an Excel file.
18
+
19
+ Args:
20
+ export_type: Type of data to export. Options:
21
+ - "ksi" - All 72 Key Security Indicators
22
+ - "all_requirements" - All 329 requirements across all families
23
+ - "definitions" - All FedRAMP definitions
24
+ output_path: Optional custom output path. If not provided, saves to Downloads folder
25
+
26
+ Returns:
27
+ Path to the generated Excel file
28
+ """
29
+ # Import openpyxl here to avoid import errors if not installed
30
+ try:
31
+ from openpyxl import Workbook
32
+ from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
33
+ except ImportError:
34
+ return "Error: openpyxl package is required for Excel export. Install with: pip install openpyxl"
35
+
36
+ await data_loader.load_data()
37
+
38
+ # Determine output path
39
+ if output_path is None:
40
+ downloads_folder = str(Path.home() / "Downloads")
41
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
42
+ filename = f"FedRAMP_20x_{export_type}_{timestamp}.xlsx"
43
+ output_path = os.path.join(downloads_folder, filename)
44
+
45
+ # Create workbook
46
+ wb = Workbook()
47
+ if wb.active:
48
+ wb.remove(wb.active) # Remove default sheet
49
+
50
+ # Define styles
51
+ header_font = Font(bold=True, color="FFFFFF", size=11)
52
+ header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")
53
+ header_alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
54
+ cell_alignment = Alignment(vertical="top", wrap_text=True)
55
+ border = Border(
56
+ left=Side(style='thin'),
57
+ right=Side(style='thin'),
58
+ top=Side(style='thin'),
59
+ bottom=Side(style='thin')
60
+ )
61
+
62
+ if export_type == "ksi":
63
+ # Export all KSIs
64
+ ws = wb.create_sheet("Key Security Indicators")
65
+
66
+ # Headers
67
+ headers = ["KSI ID", "Name", "Category", "Status", "Statement", "Note", "NIST 800-53 Controls", "Reference", "Reference URL", "Impact Levels"]
68
+ ws.append(headers)
69
+
70
+ # Style headers
71
+ for col_num, header in enumerate(headers, 1):
72
+ cell = ws.cell(row=1, column=col_num)
73
+ cell.font = header_font
74
+ cell.fill = header_fill
75
+ cell.alignment = header_alignment
76
+ cell.border = border
77
+
78
+ # Get all KSIs
79
+ all_ksi = data_loader.list_all_ksi()
80
+
81
+ for ksi in all_ksi:
82
+ ksi_id = ksi.get('id', '')
83
+ name = ksi.get('name', '')
84
+ category = ksi.get('category', '')
85
+ retired = ksi.get('retired', False)
86
+ status = 'Retired' if retired else 'Active'
87
+ statement = ksi.get('statement', '')
88
+ note = ksi.get('note', '')
89
+
90
+ # Format controls
91
+ controls = ksi.get('controls', [])
92
+ if controls:
93
+ control_list = [f"{c.get('control_id', '').upper()} - {c.get('title', '')}" for c in controls]
94
+ controls_str = '; '.join(control_list)
95
+ else:
96
+ controls_str = ''
97
+
98
+ reference = ksi.get('reference', '')
99
+ reference_url = ksi.get('reference_url', '')
100
+ impact = ksi.get('impact', {})
101
+ impact_levels = ', '.join([k.title() for k, v in impact.items() if v]) if impact else ''
102
+
103
+ row = [ksi_id, name, category, status, statement, note, controls_str, reference, reference_url, impact_levels]
104
+ ws.append(row)
105
+
106
+ # Style data rows
107
+ for col_num in range(1, len(headers) + 1):
108
+ cell = ws.cell(row=ws.max_row, column=col_num)
109
+ cell.alignment = cell_alignment
110
+ cell.border = border
111
+
112
+ # Adjust column widths
113
+ ws.column_dimensions['A'].width = 15 # KSI ID
114
+ ws.column_dimensions['B'].width = 40 # Name
115
+ ws.column_dimensions['C'].width = 30 # Category
116
+ ws.column_dimensions['D'].width = 10 # Status
117
+ ws.column_dimensions['E'].width = 60 # Statement
118
+ ws.column_dimensions['F'].width = 40 # Note
119
+ ws.column_dimensions['G'].width = 70 # NIST 800-53 Controls
120
+ ws.column_dimensions['H'].width = 30 # Reference
121
+ ws.column_dimensions['I'].width = 50 # Reference URL
122
+ ws.column_dimensions['J'].width = 20 # Impact Levels
123
+
124
+ # Freeze header row
125
+ ws.freeze_panes = 'A2'
126
+
127
+ elif export_type == "all_requirements":
128
+ # Export all requirements
129
+ ws = wb.create_sheet("All Requirements")
130
+
131
+ headers = ["Requirement ID", "Family", "Term/Name", "Description", "Document"]
132
+ ws.append(headers)
133
+
134
+ # Style headers
135
+ for col_num, header in enumerate(headers, 1):
136
+ cell = ws.cell(row=1, column=col_num)
137
+ cell.font = header_font
138
+ cell.fill = header_fill
139
+ cell.alignment = header_alignment
140
+ cell.border = border
141
+
142
+ # Get all requirements
143
+ if not data_loader._data_cache:
144
+ return "Error: Data not loaded. Please try again."
145
+ all_reqs = data_loader._data_cache["requirements"]
146
+
147
+ for req_id, req in sorted(all_reqs.items()):
148
+ family = req_id.split('-')[0] if '-' in req_id else ''
149
+ term = req.get('term', req.get('name', ''))
150
+ description = req.get('description', req.get('definition', ''))
151
+ document = req.get('document_name', '')
152
+
153
+ row = [req_id, family, term, description, document]
154
+ ws.append(row)
155
+
156
+ # Style data rows
157
+ for col_num in range(1, len(headers) + 1):
158
+ cell = ws.cell(row=ws.max_row, column=col_num)
159
+ cell.alignment = cell_alignment
160
+ cell.border = border
161
+
162
+ # Adjust column widths
163
+ ws.column_dimensions['A'].width = 18 # ID
164
+ ws.column_dimensions['B'].width = 12 # Family
165
+ ws.column_dimensions['C'].width = 40 # Term
166
+ ws.column_dimensions['D'].width = 60 # Description
167
+ ws.column_dimensions['E'].width = 30 # Document
168
+
169
+ ws.freeze_panes = 'A2'
170
+
171
+ elif export_type == "definitions":
172
+ # Export all definitions
173
+ ws = wb.create_sheet("FedRAMP Definitions")
174
+
175
+ headers = ["Term", "Definition", "Notes", "References"]
176
+ ws.append(headers)
177
+
178
+ # Style headers
179
+ for col_num, header in enumerate(headers, 1):
180
+ cell = ws.cell(row=1, column=col_num)
181
+ cell.font = header_font
182
+ cell.fill = header_fill
183
+ cell.alignment = header_alignment
184
+ cell.border = border
185
+
186
+ # Get all definitions
187
+ all_defs = data_loader.list_all_definitions()
188
+
189
+ for defn in sorted(all_defs, key=lambda x: x.get('term', '')):
190
+ term = defn.get('term', '')
191
+ definition = defn.get('definition', '')
192
+ notes = defn.get('notes', '')
193
+ references = defn.get('references', '')
194
+
195
+ row = [term, definition, notes, references]
196
+ ws.append(row)
197
+
198
+ # Style data rows
199
+ for col_num in range(1, len(headers) + 1):
200
+ cell = ws.cell(row=ws.max_row, column=col_num)
201
+ cell.alignment = cell_alignment
202
+ cell.border = border
203
+
204
+ # Adjust column widths
205
+ ws.column_dimensions['A'].width = 30 # Term
206
+ ws.column_dimensions['B'].width = 60 # Definition
207
+ ws.column_dimensions['C'].width = 40 # Notes
208
+ ws.column_dimensions['D'].width = 30 # References
209
+
210
+ ws.freeze_panes = 'A2'
211
+
212
+ else:
213
+ return f"Error: Unknown export_type '{export_type}'. Valid options: ksi, all_requirements, definitions"
214
+
215
+ # Save workbook
216
+ wb.save(output_path)
217
+
218
+ return f"Excel file created successfully at: {output_path}"
219
+
220
+
221
+
222
+ async def export_to_csv(
223
+ export_type: str,
224
+ output_path: Optional[str] = None
225
+ ) -> str:
226
+ """
227
+ Export FedRAMP 20x data to a CSV file.
228
+
229
+ Args:
230
+ export_type: Type of data to export. Options:
231
+ - "ksi" - All 72 Key Security Indicators
232
+ - "all_requirements" - All 329 requirements across all families
233
+ - "definitions" - All FedRAMP definitions
234
+ output_path: Optional custom output path. If not provided, saves to Downloads folder
235
+
236
+ Returns:
237
+ Path to the generated CSV file
238
+ """
239
+ await data_loader.load_data()
240
+
241
+ # Determine output path
242
+ if output_path is None:
243
+ downloads_folder = str(Path.home() / "Downloads")
244
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
245
+ filename = f"FedRAMP_20x_{export_type}_{timestamp}.csv"
246
+ output_path = os.path.join(downloads_folder, filename)
247
+
248
+ if export_type == "ksi":
249
+ # Export all KSIs
250
+ headers = ["KSI ID", "Name", "Category", "Status", "Statement", "Note", "NIST 800-53 Controls", "Reference", "Reference URL", "Impact Levels"]
251
+
252
+ with open(output_path, 'w', newline='', encoding='utf-8') as csvfile:
253
+ writer = csv.writer(csvfile)
254
+ writer.writerow(headers)
255
+
256
+ # Get all KSIs
257
+ all_ksi = data_loader.list_all_ksi()
258
+
259
+ for ksi in all_ksi:
260
+ ksi_id = ksi.get('id', '')
261
+ name = ksi.get('name', '')
262
+ category = ksi.get('category', '')
263
+ retired = ksi.get('retired', False)
264
+ status = 'Retired' if retired else 'Active'
265
+ statement = ksi.get('statement', '')
266
+ note = ksi.get('note', '')
267
+
268
+ # Format controls
269
+ controls = ksi.get('controls', [])
270
+ if controls:
271
+ control_list = [f"{c.get('control_id', '').upper()} - {c.get('title', '')}" for c in controls]
272
+ controls_str = '; '.join(control_list)
273
+ else:
274
+ controls_str = ''
275
+
276
+ reference = ksi.get('reference', '')
277
+ reference_url = ksi.get('reference_url', '')
278
+ impact = ksi.get('impact', {})
279
+ impact_levels = ', '.join([k.title() for k, v in impact.items() if v]) if impact else ''
280
+
281
+ row = [ksi_id, name, category, status, statement, note, controls_str, reference, reference_url, impact_levels]
282
+ writer.writerow(row)
283
+
284
+ elif export_type == "all_requirements":
285
+ # Export all requirements
286
+ headers = ["Requirement ID", "Family", "Term/Name", "Description", "Document"]
287
+
288
+ with open(output_path, 'w', newline='', encoding='utf-8') as csvfile:
289
+ writer = csv.writer(csvfile)
290
+ writer.writerow(headers)
291
+
292
+ # Get all requirements
293
+ if not data_loader._data_cache:
294
+ return "Error: Data not loaded. Please try again."
295
+ all_reqs = data_loader._data_cache["requirements"]
296
+
297
+ for req_id, req in sorted(all_reqs.items()):
298
+ family = req_id.split('-')[0] if '-' in req_id else ''
299
+ term = req.get('term', req.get('name', ''))
300
+ description = req.get('description', req.get('definition', ''))
301
+ document = req.get('document_name', '')
302
+
303
+ row = [req_id, family, term, description, document]
304
+ writer.writerow(row)
305
+
306
+ elif export_type == "definitions":
307
+ # Export all definitions
308
+ headers = ["Term", "Definition", "Notes", "References"]
309
+
310
+ with open(output_path, 'w', newline='', encoding='utf-8') as csvfile:
311
+ writer = csv.writer(csvfile)
312
+ writer.writerow(headers)
313
+
314
+ # Get all definitions
315
+ all_defs = data_loader.list_all_definitions()
316
+
317
+ for defn in sorted(all_defs, key=lambda x: x.get('term', '')):
318
+ term = defn.get('term', '')
319
+ definition = defn.get('definition', '')
320
+ notes = defn.get('notes', '')
321
+ references = defn.get('references', '')
322
+
323
+ row = [term, definition, notes, references]
324
+ writer.writerow(row)
325
+
326
+ else:
327
+ return f"Error: Unknown export_type '{export_type}'. Valid options: ksi, all_requirements, definitions"
328
+
329
+ return f"CSV file created successfully at: {output_path}"
330
+
331
+
332
+
333
+ async def generate_ksi_specification(
334
+ ksi_id: str,
335
+ evidence_collection_strategy: str,
336
+ output_path: Optional[str] = None
337
+ ) -> str:
338
+ """
339
+ Generate a product specification document for a KSI aligned with FedRAMP 20x requirements.
340
+
341
+ Args:
342
+ ksi_id: The KSI identifier (e.g., "KSI-AFR-01")
343
+ evidence_collection_strategy: High-level evidence collection strategy description
344
+ output_path: Optional custom output path. If not provided, saves to Downloads folder
345
+
346
+ Returns:
347
+ Path to the generated Word document
348
+ """
349
+ # Import python-docx here to avoid import errors if not installed
350
+ try:
351
+ from docx import Document
352
+ from docx.shared import Pt, Inches, RGBColor
353
+ from docx.enum.text import WD_ALIGN_PARAGRAPH
354
+ from docx.enum.style import WD_STYLE_TYPE
355
+ except ImportError:
356
+ return "Error: python-docx package is required for Word document generation. Install with: pip install python-docx"
357
+
358
+ await data_loader.load_data()
359
+
360
+ # Get the KSI
361
+ ksi = data_loader.get_ksi(ksi_id.upper())
362
+ if not ksi:
363
+ return f"Error: KSI '{ksi_id}' not found"
364
+
365
+ # Determine output path
366
+ if output_path is None:
367
+ downloads_folder = str(Path.home() / "Downloads")
368
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
369
+ safe_name = ksi_id.replace('/', '_').replace('\\', '_')
370
+ filename = f"KSI_Spec_{safe_name}_{timestamp}.docx"
371
+ output_path = os.path.join(downloads_folder, filename)
372
+
373
+ # Create document
374
+ doc = Document()
375
+
376
+ # Set up styles
377
+ styles = doc.styles
378
+
379
+ # Document title
380
+ title = doc.add_heading(f"Product Specification: {ksi.get('name', ksi_id)}", 0)
381
+ title.alignment = WD_ALIGN_PARAGRAPH.CENTER
382
+
383
+ # Subtitle with KSI ID
384
+ subtitle = doc.add_paragraph(f"KSI ID: {ksi_id}")
385
+ subtitle.alignment = WD_ALIGN_PARAGRAPH.CENTER
386
+ subtitle_run = subtitle.runs[0]
387
+ subtitle_run.font.size = Pt(14)
388
+ subtitle_run.font.color.rgb = RGBColor(54, 96, 146) # FedRAMP blue
389
+
390
+ # Add metadata table
391
+ doc.add_paragraph()
392
+ metadata_heading = doc.add_heading('Document Information', 2)
393
+
394
+ table = doc.add_table(rows=5, cols=2)
395
+ table.style = 'Light Grid Accent 1'
396
+
397
+ # Populate metadata
398
+ metadata = [
399
+ ('KSI ID', ksi_id),
400
+ ('Category', ksi.get('category', 'N/A')),
401
+ ('Impact Levels', ', '.join([k.title() for k, v in ksi.get('impact', {}).items() if v]) or 'N/A'),
402
+ ('Status', 'Retired' if ksi.get('retired', False) else 'Active'),
403
+ ('Document Date', datetime.now().strftime('%B %d, %Y'))
404
+ ]
405
+
406
+ for idx, (label, value) in enumerate(metadata):
407
+ table.rows[idx].cells[0].text = label
408
+ table.rows[idx].cells[1].text = str(value)
409
+ # Bold the labels
410
+ table.rows[idx].cells[0].paragraphs[0].runs[0].font.bold = True
411
+
412
+ # Overview section
413
+ doc.add_page_break()
414
+ doc.add_heading('1. Overview', 1)
415
+
416
+ doc.add_heading('1.1 Purpose', 2)
417
+ doc.add_paragraph(
418
+ f"This document provides a comprehensive product specification for implementing "
419
+ f"{ksi.get('name', ksi_id)} in compliance with FedRAMP 20x Key Security Indicators. "
420
+ f"It is designed to guide engineering teams through planning, implementation, and "
421
+ f"evidence collection activities."
422
+ )
423
+
424
+ doc.add_heading('1.2 Scope', 2)
425
+ impact = ksi.get('impact', {})
426
+ impact_text = "This KSI applies to "
427
+ if impact.get('low') and impact.get('moderate'):
428
+ impact_text += "both Low and Moderate impact systems."
429
+ elif impact.get('low'):
430
+ impact_text += "Low impact systems."
431
+ elif impact.get('moderate'):
432
+ impact_text += "Moderate impact systems."
433
+ else:
434
+ impact_text += "systems as defined by FedRAMP authorization requirements."
435
+ doc.add_paragraph(impact_text)
436
+
437
+ # Requirement Statement
438
+ doc.add_heading('2. Requirement Statement', 1)
439
+ statement = ksi.get('statement', '')
440
+ if statement:
441
+ doc.add_paragraph(statement)
442
+ else:
443
+ doc.add_paragraph("No statement available for this KSI.")
444
+
445
+ # Check for retired status
446
+ if ksi.get('retired', False):
447
+ note_para = doc.add_paragraph()
448
+ note_run = note_para.add_run(f"⚠ NOTE: {ksi.get('note', 'This KSI has been retired.')}")
449
+ note_run.font.color.rgb = RGBColor(192, 0, 0)
450
+ note_run.font.bold = True
451
+
452
+ # Related NIST 800-53 Controls
453
+ doc.add_heading('3. Related NIST 800-53 Controls', 1)
454
+ controls = ksi.get('controls', [])
455
+ if controls:
456
+ doc.add_paragraph(
457
+ "This KSI aligns with the following NIST 800-53 Rev 5 security controls. "
458
+ "Implementation must address these control requirements:"
459
+ )
460
+ for control in controls:
461
+ control_id = control.get('control_id', '').upper()
462
+ control_title = control.get('title', 'N/A')
463
+ p = doc.add_paragraph(style='List Bullet')
464
+ p.add_run(f"{control_id}: ").bold = True
465
+ p.add_run(control_title)
466
+ else:
467
+ doc.add_paragraph("No specific NIST 800-53 controls mapped to this KSI.")
468
+
469
+ # Reference Documentation
470
+ reference = ksi.get('reference')
471
+ reference_url = ksi.get('reference_url')
472
+ if reference or reference_url:
473
+ doc.add_heading('4. Reference Documentation', 1)
474
+ if reference:
475
+ doc.add_paragraph(f"Primary Reference: {reference}")
476
+ if reference_url:
477
+ doc.add_paragraph(f"Documentation URL: {reference_url}")
478
+
479
+ # Azure-First Implementation Guidance
480
+ doc.add_heading('5. Azure-First Implementation Guidance', 1)
481
+
482
+ doc.add_heading('5.1 Recommended Azure Services', 2)
483
+ doc.add_paragraph(
484
+ "Based on FedRAMP 20x requirements and Azure Government compliance capabilities, "
485
+ "consider the following Azure services for implementation:"
486
+ )
487
+
488
+ # Add Azure-specific recommendations based on category
489
+ category = ksi.get('category', '')
490
+ azure_recommendations = _get_azure_recommendations(category, controls)
491
+
492
+ for service, description in azure_recommendations:
493
+ p = doc.add_paragraph(style='List Bullet')
494
+ p.add_run(f"{service}: ").bold = True
495
+ p.add_run(description)
496
+
497
+ doc.add_heading('5.2 Infrastructure as Code', 2)
498
+ doc.add_paragraph(
499
+ "Implement using Azure Bicep or Terraform with Azure Provider for repeatable, "
500
+ "auditable deployments. Store IaC in version control (Azure Repos or GitHub) "
501
+ "with branch protection and approval workflows."
502
+ )
503
+
504
+ doc.add_heading('5.3 Automation and Monitoring', 2)
505
+ automation_items = [
506
+ "Use Azure Policy for continuous compliance monitoring and enforcement",
507
+ "Implement Azure Monitor and Log Analytics for centralized logging",
508
+ "Configure Azure Security Center for security posture management",
509
+ "Enable Microsoft Defender for Cloud for threat protection",
510
+ "Use Azure Automation for remediation workflows"
511
+ ]
512
+ for item in automation_items:
513
+ doc.add_paragraph(item, style='List Bullet')
514
+
515
+ # Evidence Collection Strategy
516
+ doc.add_heading('6. Evidence Collection Strategy', 1)
517
+
518
+ doc.add_heading('6.1 User-Defined Strategy', 2)
519
+ doc.add_paragraph(evidence_collection_strategy)
520
+
521
+ doc.add_heading('6.2 Recommended Evidence Types', 2)
522
+ evidence_items = [
523
+ "Configuration screenshots from Azure Portal",
524
+ "Azure Policy compliance reports exported as JSON/CSV",
525
+ "Azure Monitor query results and dashboards",
526
+ "IaC templates (Bicep/Terraform) with commit history",
527
+ "Azure DevOps/GitHub Actions pipeline logs",
528
+ "Microsoft Entra ID audit logs for access control",
529
+ "Azure Resource Graph queries demonstrating compliance",
530
+ "Automated test results from security validation pipelines"
531
+ ]
532
+ for item in evidence_items:
533
+ doc.add_paragraph(item, style='List Bullet')
534
+
535
+ doc.add_heading('6.3 Evidence Collection Schedule', 2)
536
+ doc.add_paragraph(
537
+ "Evidence should be collected regularly to align with FedRAMP "
538
+ "Collaborative Continuous Monitoring (CCM) requirements. Automate evidence "
539
+ "collection where possible using Azure Functions, Logic Apps, or Azure Automation. "
540
+ "Engineering teams should determine the appropriate collection frequency based on "
541
+ "system criticality and organizational requirements."
542
+ )
543
+
544
+ # Implementation Plan Template
545
+ doc.add_heading('7. Implementation Plan Template', 1)
546
+
547
+ phases = [
548
+ {
549
+ 'phase': 'Phase 1: Requirements Analysis',
550
+ 'activities': [
551
+ 'Review NIST 800-53 control requirements',
552
+ 'Map controls to Azure services and features',
553
+ 'Identify gaps in current implementation',
554
+ 'Document assumptions and dependencies'
555
+ ]
556
+ },
557
+ {
558
+ 'phase': 'Phase 2: Design',
559
+ 'activities': [
560
+ 'Create Azure architecture diagrams',
561
+ 'Design IaC templates (Bicep/Terraform)',
562
+ 'Define Azure Policy rules and initiatives',
563
+ 'Design monitoring and alerting strategy',
564
+ 'Plan evidence collection automation'
565
+ ]
566
+ },
567
+ {
568
+ 'phase': 'Phase 3: Implementation',
569
+ 'activities': [
570
+ 'Deploy Azure infrastructure using IaC',
571
+ 'Configure Azure services and policies',
572
+ 'Implement monitoring and logging',
573
+ 'Set up automated evidence collection',
574
+ 'Configure security controls and access policies'
575
+ ]
576
+ },
577
+ {
578
+ 'phase': 'Phase 4: Testing and Validation',
579
+ 'activities': [
580
+ 'Validate Azure Policy compliance',
581
+ 'Test evidence collection automation',
582
+ 'Perform security scanning and assessment',
583
+ 'Validate control implementation against requirements',
584
+ 'Document test results'
585
+ ]
586
+ },
587
+ {
588
+ 'phase': 'Phase 5: Documentation and Evidence',
589
+ 'activities': [
590
+ 'Collect and organize all evidence',
591
+ 'Document implementation details',
592
+ 'Create operational runbooks',
593
+ 'Prepare for FedRAMP assessment',
594
+ 'Update SSP (System Security Plan) as needed'
595
+ ]
596
+ }
597
+ ]
598
+
599
+ for phase_info in phases:
600
+ doc.add_heading(phase_info['phase'], 2)
601
+ doc.add_paragraph("Activities:")
602
+ for activity in phase_info['activities']:
603
+ doc.add_paragraph(activity, style='List Bullet')
604
+
605
+ # Team Roles and Responsibilities
606
+ doc.add_heading('8. Team Roles and Responsibilities', 1)
607
+
608
+ roles = [
609
+ ('Cloud Architect', 'Design Azure architecture and services selection'),
610
+ ('DevOps Engineer', 'Implement IaC templates and CI/CD pipelines'),
611
+ ('Security Engineer', 'Configure security controls and monitoring'),
612
+ ('Compliance Specialist', 'Ensure FedRAMP requirements are met'),
613
+ ('Product Owner', 'Prioritize requirements and acceptance criteria'),
614
+ ('QA Engineer', 'Validate implementation and test evidence collection')
615
+ ]
616
+
617
+ for role, responsibility in roles:
618
+ p = doc.add_paragraph(style='List Bullet')
619
+ p.add_run(f"{role}: ").bold = True
620
+ p.add_run(responsibility)
621
+
622
+ # Success Criteria
623
+ doc.add_heading('9. Success Criteria', 1)
624
+
625
+ success_items = [
626
+ 'All NIST 800-53 controls implemented and validated in Azure',
627
+ 'Azure Policy reports 100% compliance for defined policies',
628
+ 'Evidence collection automation operational and tested',
629
+ 'IaC templates reviewed and approved',
630
+ 'Security scanning shows no high/critical vulnerabilities',
631
+ 'Documentation complete and reviewed',
632
+ 'Team training completed on operations and maintenance',
633
+ 'Quarterly evidence collection process validated'
634
+ ]
635
+
636
+ for item in success_items:
637
+ doc.add_paragraph(item, style='List Bullet')
638
+
639
+ # Risks and Mitigation
640
+ doc.add_heading('10. Risks and Mitigation Strategies', 1)
641
+
642
+ risks = [
643
+ {
644
+ 'risk': 'Azure service limitations may not fully satisfy control requirements',
645
+ 'mitigation': 'Identify gaps early, use compensating controls, engage Azure support for guidance'
646
+ },
647
+ {
648
+ 'risk': 'Evidence collection automation failures',
649
+ 'mitigation': 'Implement monitoring and alerting, maintain manual collection procedures as backup'
650
+ },
651
+ {
652
+ 'risk': 'Compliance drift over time',
653
+ 'mitigation': 'Use Azure Policy in enforcement mode, implement continuous monitoring'
654
+ },
655
+ {
656
+ 'risk': 'Resource constraints or timeline delays',
657
+ 'mitigation': 'Prioritize critical controls, use phased implementation approach'
658
+ }
659
+ ]
660
+
661
+ for risk_info in risks:
662
+ doc.add_heading(f"Risk: {risk_info['risk']}", 2)
663
+ doc.add_paragraph(f"Mitigation: {risk_info['mitigation']}")
664
+
665
+ # Appendix
666
+ doc.add_page_break()
667
+ doc.add_heading('Appendix A: Additional Resources', 1)
668
+
669
+ resources = [
670
+ 'FedRAMP 20x Requirements: https://fedramp.gov/docs/',
671
+ 'NIST 800-53 Rev 5: https://csrc.nist.gov/publications/detail/sp/800-53/rev-5/final',
672
+ 'Azure Security Documentation: https://learn.microsoft.com/azure/security/',
673
+ 'Azure Compliance: https://learn.microsoft.com/azure/compliance/',
674
+ 'Azure Government: https://azure.microsoft.com/explore/global-infrastructure/government/',
675
+ 'Microsoft Entra ID: https://learn.microsoft.com/entra/',
676
+ 'Azure Policy: https://learn.microsoft.com/azure/governance/policy/'
677
+ ]
678
+
679
+ for resource in resources:
680
+ doc.add_paragraph(resource, style='List Bullet')
681
+
682
+ # Save document
683
+ doc.save(output_path)
684
+
685
+ return f"Word document created successfully at: {output_path}"
686
+
687
+
688
+ def _get_azure_recommendations(category: str, controls: list) -> list:
689
+ """Get Azure service recommendations based on KSI category and controls."""
690
+ recommendations = []
691
+
692
+ # Default recommendations for all KSIs
693
+ recommendations.extend([
694
+ ('Microsoft Entra ID', 'Identity and access management, conditional access, MFA'),
695
+ ('Azure Policy', 'Compliance enforcement and continuous monitoring'),
696
+ ('Azure Monitor', 'Centralized logging, alerting, and diagnostics')
697
+ ])
698
+
699
+ # Category-specific recommendations
700
+ category_lower = category.lower()
701
+
702
+ if 'education' in category_lower or 'training' in category_lower:
703
+ recommendations.extend([
704
+ ('Microsoft Viva Learning', 'Security awareness training platform'),
705
+ ('Azure AD Access Reviews', 'Regular access certification and training tracking')
706
+ ])
707
+
708
+ if 'vulnerability' in category_lower or 'assessment' in category_lower:
709
+ recommendations.extend([
710
+ ('Microsoft Defender for Cloud', 'Vulnerability scanning and security recommendations'),
711
+ ('Azure Security Center', 'Security posture management and compliance tracking')
712
+ ])
713
+
714
+ if 'incident' in category_lower or 'response' in category_lower:
715
+ recommendations.extend([
716
+ ('Microsoft Sentinel', 'SIEM for incident detection and response'),
717
+ ('Azure Logic Apps', 'Automated incident response workflows')
718
+ ])
719
+
720
+ if 'data' in category_lower or 'encryption' in category_lower:
721
+ recommendations.extend([
722
+ ('Azure Key Vault', 'Cryptographic key and secret management'),
723
+ ('Azure Storage encryption', 'Data at rest encryption with customer-managed keys')
724
+ ])
725
+
726
+ if 'network' in category_lower or 'boundary' in category_lower:
727
+ recommendations.extend([
728
+ ('Azure Firewall', 'Network security and filtering'),
729
+ ('Azure DDoS Protection', 'DDoS mitigation'),
730
+ ('Azure Virtual Network', 'Network segmentation and isolation')
731
+ ])
732
+
733
+ # Control-based recommendations
734
+ control_ids = [c.get('control_id', '').upper() for c in controls]
735
+
736
+ if any(c.startswith('AU-') for c in control_ids): # Audit controls
737
+ recommendations.append(('Azure Log Analytics', 'Centralized audit log collection and analysis'))
738
+
739
+ if any(c.startswith('CM-') for c in control_ids): # Configuration Management
740
+ recommendations.append(('Azure Automation State Configuration', 'Desired state configuration management'))
741
+
742
+ if any(c.startswith('SC-') for c in control_ids): # System and Communications Protection
743
+ recommendations.append(('Azure Front Door', 'Web application firewall and CDN'))
744
+
745
+ # Remove duplicates while preserving order
746
+ seen = set()
747
+ unique_recommendations = []
748
+ for rec in recommendations:
749
+ if rec[0] not in seen:
750
+ seen.add(rec[0])
751
+ unique_recommendations.append(rec)
752
+
753
+ return unique_recommendations