name: new-exam-scaffold description: 'Scaffold a new certification exam into the LearningAzure workspace. Creates the folder structure, README files, StudyLog, Skills.psd1, and updates all cross-cutting files (scripts, governance, shared contract, top-level README). Use when asked to add a new exam, create an exam, scaffold an exam, or introduce a new certification.' user-invokable: true argument-hint: '[exam code]'
New Exam Scaffold
Creates the complete folder structure and updates all cross-cutting files needed to introduce a new certification exam into the LearningAzure workspace. Uses the AZ-305 exam as the reference template.
When to Use
- Adding a new Microsoft certification exam (e.g., AI-103, DP-100)
- Replacing a retiring exam with its successor
- Scaffolding a new exam structure from scratch
Input Required
Before scaffolding, gather the following from the user. Do not guess or infer — ask explicitly for anything not provided.
| Input | Description | Example |
|---|---|---|
| Exam Code | Uppercase exam identifier | AI-103 |
| Full Title | Official exam title | Designing and Implementing a Microsoft Azure AI Solution |
| Certification Name | Credential name from Microsoft | Azure AI Engineer Associate |
| Certification URL | Microsoft credential page URL | https://learn.microsoft.com/en-us/credentials/certifications/azure-ai-engineer/... |
| Study Guide URL | Official study guide URL | https://learn.microsoft.com/en-us/credentials/certifications/resources/study-guides/ai-103 |
| Exam Domains | Full domain → skill → task hierarchy with weights | See Domain Structure Format |
| Start Date | When study begins (or leave blank for later) | 4/1/26 |
| Expected Exam Date | Target exam date | 7/1/26 |
| Study Pace | Daily study commitment | 1.5 hrs/day, 7 days/week |
| Include Labs? | Whether to create hands-on labs structure | Yes / No |
| Lab Domain Folders | (Only if labs enabled) Subdirectory names for hands-on-labs | compute, networking, storage |
Domain Structure Format
Provide the official exam domain hierarchy. This populates the Skills.psd1 and coverage table in the exam README:
Domain 1: <Name> (<Weight>)
Skill: <Skill Name>
Task: <Task description>
Task: <Task description>
Skill: <Skill Name>
Task: <Task description>
Domain 2: <Name> (<Weight>)
...
The domain/skill/task hierarchy can be found on the official Microsoft study guide page for the exam.
Phase 1: Create Exam Folder Structure
1.1 Directory Tree
Create the following directory structure under certs/:
certs/<EXAM>/
├── README.md
├── StudyLog.md
├── Skills.psd1
└── practice-questions/
├── README.md
├── .img/
│ └── .gitkeep
└── <NN>-<domain-slug>.md ← one per domain
If labs are enabled, also create:
└── hands-on-labs/
├── README.md
└── <domain-folder>/ ← one per lab domain
└── .gitkeep
Other folders (learning-paths/, video-courses/) are not created during scaffolding. They are added later when content is available.
1.2 File Templates
certs/<EXAM>/Skills.psd1 — Domain/Skill/Task Hierarchy
This file powers Invoke-AzStudySession.ps1 skill validation and practice exam organization. Create it before the README — it is the authoritative source for the domain/skill/task hierarchy.
# -------------------------------------------------------------------------
# Data: Skills.psd1
# Description: Domain, skill, and task hierarchy for the <EXAM> exam
# Context: <EXAM> — <Full Title>
# Author: Greg Tate
# -------------------------------------------------------------------------
@{
Domains = @(
@{
Name = '<Domain 1 full name>'
Skills = @(
@{
Name = '<Skill 1 name>'
Tasks = @(
'<Task description>'
'<Task description>'
)
}
@{
Name = '<Skill 2 name>'
Tasks = @(
'<Task description>'
)
}
)
}
@{
Name = '<Domain 2 full name>'
Skills = @(
# ... same structure
)
}
)
}
Use the exact domain/skill/task wording from the official study guide — this data is the single source of truth for metadata matching.
certs/<EXAM>/README.md — Main Exam README
Sections in order:
- Title —
# <EXAM>: <Full Title> — Study Guide - Objective statement with credential name
- Links — Certification page, study guide, study log
- Study Summary —
<!-- STUDY_SUMMARY -->marker block - Coverage Dashboard —
## 📊 Exam Coveragewith dashboard and coverage table - Progress Tracker —
## 📚 Progress Trackerwith per-skill phased format
Header and links format:
# <EXAM>: <Full Title> — Study Guide
**Objective:** Achieve the **<Certification Name>** certification using Microsoft Learn, practice exams, and video courses.
- **Certification Page:** [<Certification Name>](<certification-url>)
- **Official Study Guide:** [<EXAM> Study Guide](<study-guide-url>)
- **Study Log:** [Session-by-session study time tracker](./StudyLog.md)
<!-- STUDY_SUMMARY -->
**Hours Committed:** 0h · **Days Studied:** 0
<!-- /STUDY_SUMMARY -->
The <!-- STUDY_SUMMARY --> block is maintained by Update-CoverageTable.ps1. It replaces the legacy <!-- HOURS_COMMITTED --> marker.
Coverage section preamble (varies by labs):
With labs:
## 📊 Exam Coverage
Task-level coverage from [Practice Questions](./practice-questions/README.md) and [Hands-on Labs](./hands-on-labs/README.md).
Without labs:
## 📊 Exam Coverage
Task-level coverage from [Practice Questions](./practice-questions/README.md).
Dashboard table (between <!-- BEGIN COVERAGE DASHBOARD --> and <!-- END COVERAGE DASHBOARD --> markers):
Without labs (default):
<!-- BEGIN COVERAGE DASHBOARD -->
| Domain | Weight | Skills | Qs | Tasks Covered | Status |
| :----- | :----- | -----: | -: | :------------ | :----: |
| [1. <Short Name>](#domain-1) | XX–XX% | S | 0 | 0 / N (0%) | 🔴 |
| [2. <Short Name>](#domain-2) | XX–XX% | S | 0 | 0 / N (0%) | 🔴 |
**Totals:** 0 practice questions
**Legend:** 🟢 Strong (≥66%) · 🟡 Partial (33–65%) · 🔴 Low (<33%) — "Covered" = task has ≥1 practice question
> **Note:** Practice questions are those missed or not confidently answered correctly.
<!-- END COVERAGE DASHBOARD -->
With labs:
<!-- BEGIN COVERAGE DASHBOARD -->
| Domain | Weight | Skills | Qs | Labs | Tasks Covered | Status |
| :----- | :----- | -----: | -: | ---: | :------------ | :----: |
| [1. <Short Name>](#domain-1) | XX–XX% | S | 0 | 0 | 0 / N (0%) | 🔴 |
| [2. <Short Name>](#domain-2) | XX–XX% | S | 0 | 0 | 0 / N (0%) | 🔴 |
**Totals:** 0 practice questions · 0 hands-on labs
**Legend:** 🟢 Strong (≥66%) · 🟡 Partial (33–65%) · 🔴 Low (<33%) — "Covered" = task has ≥1 practice question or ≥1 lab
> **Note:** Practice questions are those missed or not confidently answered correctly.
<!-- END COVERAGE DASHBOARD -->
S= number of skills in each domain (from the exam study guide hierarchy).N= total task count for each domain from the coverage table.<Short Name>= abbreviated domain name for the dashboard (e.g., "Identity, Governance & Monitoring").- All new exams start with
🔴status and zero counts.
Coverage table (between <!-- BEGIN COVERAGE TABLE --> and <!-- END COVERAGE TABLE --> markers):
Domain sections use the compact <details>/<summary> format (no ### headings):
Without labs (default):
<a id="domain-1"></a>
<details>
<summary><b>Domain 1: <Name> (<Weight>)</b> — N tasks · 0 Qs</summary>
| Skill | Task | Qs |
| :--- | :--- | -: |
| <Skill Name> (T tasks) | <Task description> | 0 |
| | <Task description> | 0 |
</details>
With labs:
<a id="domain-1"></a>
<details>
<summary><b>Domain 1: <Name> (<Weight>)</b> — N tasks · 0 Qs · 0 Labs</summary>
| Skill | Task | Qs | Labs |
| :--- | :--- | -: | -: |
| <Skill Name> (T tasks) | <Task description> | 0 | 0 |
| | <Task description> | 0 | 0 |
</details>
Dashboard status indicators:
- 🟢 Strong (≥66% tasks covered)
- 🟡 Partial (33–65%)
- 🔴 Low (<33%)
Progress Tracker — per-skill phased format:
---
## 📚 Progress Tracker
**Goal:** Pass <EXAM> at the <duration> mark (~<Expected Exam Date>) · **Pace:** <Study Pace>
### Per-Skill Workload & Day Allocation
| # | Domain | Skill | ML (min) | PQs | PQ (min) | Total (min) | Days |
| -: | :----- | :---- | -------: | --: | -------: | ----------: | ---: |
| 1 | <Domain 1 short name> | <Skill 1 name> | 0 | 0 | 0 | 0 | 0 |
| 2 | <Domain 1 short name> | <Skill 2 name> | 0 | 0 | 0 | 0 | 0 |
| 3 | <Domain 2 short name> | <Skill 3 name> | 0 | 0 | 0 | 0 | 0 |
| | **Totals** | | **0** | **0** | **0** | **0** | **0** |
> ML = Microsoft Learn module time · PQs = MeasureUp practice questions · PQ (min) = PQs × ~6 min
### Phase 1 — Learn + Practice per Skill (<start date> – <end date>)
| # | Domain | Skill | Tasks | Expected Start | Status | Started | Completed | Days |
| -: | :----- | :---- | ----: | :------------- | :----- | :------ | :-------- | ---: |
| 1 | <Domain 1 short name> | <Skill 1 name> | 0 | <date> | Not Started | | | |
| 2 | <Domain 1 short name> | <Skill 2 name> | 0 | <date> | Not Started | | | |
| 3 | <Domain 2 short name> | <Skill 3 name> | 0 | <date> | Not Started | | | |
### Phase 2 — Review & Practice Exams (<start date> – <end date>)
| # | Modality | Expected Start | Status | Started | Completed | Days |
| -: | :------- | :------------- | :----- | :------ | :-------- | ---: |
| N+1 | Video Course | <date> | Not Started | | | |
| N+2 | Practice Exam (Tutorials Dojo) | <date> | Not Started | | | |
| N+3 | Practice Exam (Microsoft Assessment) | <date> | Not Started | | | |
**Legend:** Not Started · In Progress · Completed
Notes on per-skill workload table:
- ML (min) — Sum of Microsoft Learn module times for the skill’s modules. Populate from the learning path module lengths.
- PQs — Estimated MeasureUp practice questions. Roughly proportional to domain weight.
- PQ (min) — PQs × ~6 min (estimated time per practice question).
- Days — Total (min) ÷ daily study minutes.
- Populate values after identifying Microsoft Learn paths and practice question sources. Use zeroes as placeholders if not yet known.
Phase 1 date calculation:
- Start from the study start date
- Add days allocated per skill sequentially
- Phase 2 begins after Phase 1 ends
Script compatibility: The per-skill progress tracker format (header
| # | Domain | Skill |) is not auto-detected byUpdate-ProgressTrackerDays.ps1, which only matches the legacy| Priority | Modality |header. Days tracking for the per-skill format is done byInvoke-AzStudySession.ps1session logging. TheTasksandHourscolumns are auto-populated byUpdate-CoverageTable.ps1fromSkills.psd1andStudyLog.md.
certs/<EXAM>/StudyLog.md — Study Time Tracker
# Study Log — <EXAM>: <Full Title>
This log tracks individual study sessions for the **<EXAM>** exam. Fill in **End**, **Duration**, and **Notes** when the session concludes.
---
## Session History
| # | Date | Start | End | Duration | Mode | Skill | Notes |
|:--|:-----|:------|:----|:---------|:-----|:------|:------|
The Mode column values correspond to Invoke-AzStudySession.ps1 modes (e.g., MSLearn, PracticeQuestion, Video, Lab). The Skill column records which specific skill was studied.
certs/<EXAM>/practice-questions/README.md — Practice Questions Index
This file serves as an index page linking to per-domain practice question files.
# Practice Exam Questions — <EXAM>
Accounts for questions missed or unsure about in the practice exams. Questions are organized into per-domain files to keep each file small and responsive.
| # | Domain | File | Qs |
| -: | :----- | :--- | -: |
| 1 | <Domain 1 name> | [<filename>.md](<filename>.md) | 0 |
| 2 | <Domain 2 name> | [<filename>.md](<filename>.md) | 0 |
...
certs/<EXAM>/practice-questions/<NN>-<domain-slug>.md — Per-Domain Practice Question Files
Create one file per exam domain, using numbered-prefix kebab-case names (e.g., 01-identity-governance-monitoring.md). Each file uses the heading hierarchy: # domain title → ## skill subheadings → ### question headings.
# Practice Questions — <Domain Name>
Accounts for questions missed or unsure about in the practice exams.
---
## <Skill 1 Name>
## <Skill 2 Name>
1.3 Optional: Hands-on Labs Structure
Created only when the user confirms labs are planned for this exam.
certs/<EXAM>/hands-on-labs/README.md — Lab Catalog
# <EXAM> Hands-on Labs
Practice labs that reinforce exam objectives through real Azure deployments.
All labs follow the governance policies in [Governance-Lab.md](../../../.assets/shared/Governance-Lab.md).
---
## 📈 Lab Statistics
- **Total Labs:** 0
<domain counts — one line per domain, all 0>
---
## 🧪 Labs
*No labs yet. Labs are built as practice exam questions reveal knowledge gaps.*
---
## 📋 Governance & Standards
- **Governance Policy:** [Governance-Lab.md](../../../.assets/shared/Governance-Lab.md)
- **Shared Contract:** [lab-shared-contract](../../../.github/skills/lab-shared-contract/SKILL.md)
Phase 2: Update Cross-Cutting Files
2.1 Top-Level README.md
Add the new exam to the Certifications table (under ## 📚 Certifications):
| 📕 [**<EXAM>**](certs/<EXAM>/README.md) | <Certification Name> | Not Started | |
- Use the appropriate emoji color (📗📙📘📕) and status text based on start date.
- If a start date is provided, use
In Progressas the status and add the start date in the Duration column. - Adding the exam to this table makes it discoverable by
Get-ActiveExam.ps1, which all maintenance scripts use for dynamic exam discovery.
2.2 Governance-Lab.md
Only update if the exam includes hands-on labs.
Add the exam code to the title scope:
Standards for all Terraform and Bicep labs (AZ-104, AZ-305, <NEW-EXAM>).
If the exam has distinct resource types, add an exam-specific resource prefix table under §2.2 Resource Naming following the format of existing prefix tables:
#### <EXAM> Prefixes
| Resource | Prefix |
| -------- | ------ |
| ... | ... |
Add the exam to the Project tag examples in §1.2 Required Tags:
| Project | AZ-104 / AZ-305 / <NEW-EXAM> |
2.3 lab-shared-contract SKILL.md
Only update if the exam includes hands-on labs.
R-001 — Add the lowercase exam code to the <exam> definition:
- `<exam>`: lowercase exam code (`az104`, `ai102`, `<new-exam-lowercase>`)
R-005 — Add the exam code to the Project tag examples:
| Project | Uppercase: `AZ-104` or `AZ-305` or `<NEW-EXAM>` |
New R-0xx (if exam has distinct resource types) — Add an exam-specific resource naming rule following the pattern of R-002 and R-003:
## R-0xx: Resource Naming — <EXAM> Prefixes
Pattern: `<prefix>-<role>[-instance]`
| Resource | Prefix | Random Suffix? |
| -------- | ------ | -------------- |
| ... | ... | ... |
Assign the next available R-0xx ID.
2.4 Scripts
All maintenance scripts use dynamic exam discovery via Get-ActiveExam.ps1, which parses the main README's certifications table. Adding the exam to the top-level README (§2.1) automatically makes it visible to these scripts:
Update-CoverageTable.ps1Invoke-PracticeExamReorganizer.ps1Update-StudyLogGaps.ps1
No ValidateSet updates or hardcoded exam lists are needed in these scripts.
Update-LabReferences.ps1
Only update if the exam includes hands-on labs. Add the exam to the $DomainConfig hashtable with domain folder→display-name mappings:
$DomainConfig = @{
'AZ-104' = [ordered]@{
'storage' = 'Storage'
'compute' = 'Compute'
'monitoring' = 'Monitoring'
'identity-governance' = 'Identity & Governance'
'networking' = 'Networking'
}
'<NEW-EXAM>' = [ordered]@{
'<domain-folder>' = '<Display Name>'
# ... one entry per lab domain folder
}
}
The domain ordering determines how labs are grouped and sequenced in the catalog.
Invoke-AzStudySession.ps1
Standard exams under certs/<EXAM>/ using StudyLog.md and Skills.psd1 require no changes — the script discovers them automatically via Get-ActiveExam.ps1 and defaults to certs\<EXAM> folder and StudyLog.md.
Only add entries if the exam uses a non-standard folder or log file:
$ExamFolderMap— Add only if the exam folder is notcerts\<EXAM>.$ExamLogFileMap— Add only if the log file is notStudyLog.md.
Update-ProgressTrackerDays.ps1
The per-skill progress tracker format (header | # | Domain | Skill |) used by new exams is not auto-detected by this script. The script only matches the legacy | Priority | Modality | header pattern. No changes are needed — days tracking for the per-skill format is handled by Invoke-AzStudySession.ps1 session logging instead. The Tasks and Hours columns are auto-populated by Update-CoverageTable.ps1.
Invoke-ContentMaintenance.ps1
No changes required. This orchestrator script runs all maintenance scripts in dependency order:
Invoke-PracticeExamReorganizer(when-Reorganizeis specified)Update-StudyLogGapsUpdate-ProgressTrackerDaysUpdate-CoverageTable(also generates the root README activity table)Update-LabReferencesInvoke-CollapseDetailBlockRemove-UnusedImages
Since child scripts use dynamic discovery, the orchestrator automatically processes new exams once they are added to the top-level README.
2.5 Skills
lab-catalog-updater/SKILL.md
Only update if the exam includes hands-on labs. Add the exam to the Scope section and define its domain ordering:
- `certs/<NEW-EXAM>/hands-on-labs/README.md`
Also add a domain ordering entry following the existing pattern:
- **<NEW-EXAM>:** <Domain1>, <Domain2>, ...
The domain ordering determines how labs are grouped and sequenced in the catalog.
exam-question-organizer/SKILL.md
Add the exam to the Per-domain exams target table:
| <NEW-EXAM> | `certs/<NEW-EXAM>/practice-questions/<NN>-<domain-slug>.md` (one row per domain file) | `certs/<NEW-EXAM>/README.md` — Practice Exam Coverage section | `certs/<NEW-EXAM>/README.md` — Practice Exam Coverage section |
Phase 3: Validation
After all files are created and updated, verify:
| Check | How |
|---|---|
| Folder structure exists | List certs/<EXAM>/ directory recursively |
| Skills.psd1 is valid | Run Import-PowerShellDataFile certs/<EXAM>/Skills.psd1 — should parse without errors |
| README has coverage markers | Confirm <!-- BEGIN COVERAGE TABLE -->, <!-- END COVERAGE TABLE -->, <!-- BEGIN COVERAGE DASHBOARD -->, and <!-- END COVERAGE DASHBOARD --> markers exist |
| README has study summary | Confirm <!-- STUDY_SUMMARY --> and <!-- /STUDY_SUMMARY --> markers exist |
| Content maintenance passes | Run Invoke-ContentMaintenance.ps1 -WhatIf — all steps should complete without errors |
| Study session works | Run Invoke-AzStudySession.ps1 -Action Start -Mode <NEW-EXAM> then -Action Stop |
| Top-level README links resolve | Confirm the certifications table links to existing certs/<EXAM>/README.md |
| Governance references exam | (Labs only) Search Governance-Lab.md for the exam code |
| Lab domain config | (Labs only) Confirm Update-LabReferences.ps1 $DomainConfig contains the new exam entry |
| Domain count matches | Count tasks in coverage table vs. <summary> tag totals |
| No raw unicode escapes | Scan all generated files for literal unicode escape sequences (\uXXXX, \u{XXXXX}, &#xNNNN;). If any are found, replace them with the actual rendered character. |
Rules
- Do not pre-populate question or lab content — those are built organically from practice exams.
- Do initialize all coverage counts to zero.
- Do use the compact
<details>/<summary>format for domain sections (no###headings). - Do preserve exact domain/skill/task wording from the official study guide — these are the single source of truth for the
exam-question-organizermetadata matching. - Do assign the next available R-0xx ID when adding shared contract rules.
- Do follow existing file formatting conventions exactly (line endings, marker comments, table alignment).
- Do not modify any existing exam's content — only add the new exam and update shared references.
- Do create
Skills.psd1before the README — it is the authoritative source for the domain/skill/task hierarchy. - Do use
<!-- STUDY_SUMMARY -->markers (not the legacy<!-- HOURS_COMMITTED -->). - Do generate per-domain practice question files (not a single combined file).
Output
After completing the scaffold, report:
- Files Created — Full list of new files and directories
- Files Updated — Each cross-cutting file modified with a one-line description of the change
- Validation Results — Pass/fail for each check in Phase 3
- Next Steps — Remind user to populate the per-skill workload table from Microsoft Learn module times and practice question estimates