Azure Policy
vinayaklatthe/microsoft-security-skills
Guidance for Azure Policy — enforcing and auditing governance and security guardrails at scale across Azure with definitions, initiatives, assignments, and remediation tasks.
A skill your agent uses when asked to investigate a computer, device, endpoint, or machine for security issues, suspicious activity, malware, or compliance review.
The automated check flagged lines worth reading first. See the safety section below.
$ npx skills add SCStelz/security-investigator --skill computer-investigation -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install SCStelz/security-investigator computer-investigation --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/SCStelz/security-investigator.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.github/skills/computer-investigation .claude/skills/computer-investigation && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "computer-investigation" agent skill from https://github.com/SCStelz/security-investigator/tree/main/.github/skills/computer-investigation into .claude/skills/computer-investigation/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "computer-investigation", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/SCStelz/security-investigator/tree/main/.github/skills/computer-investigationType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add SCStelz/security-investigator --skill computer-investigation -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install SCStelz/security-investigator computer-investigation --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/SCStelz/security-investigator.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.github/skills/computer-investigation .agents/skills/computer-investigation && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "computer-investigation" agent skill from https://github.com/SCStelz/security-investigator/tree/main/.github/skills/computer-investigation into .agents/skills/computer-investigation/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "computer-investigation", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add SCStelz/security-investigator --skill computer-investigation -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install SCStelz/security-investigator computer-investigation --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/SCStelz/security-investigator.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.github/skills/computer-investigation .cursor/skills/computer-investigation && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "computer-investigation" agent skill from https://github.com/SCStelz/security-investigator/tree/main/.github/skills/computer-investigation into .cursor/skills/computer-investigation/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "computer-investigation", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/SCStelz/security-investigator.git --path .github/skills/computer-investigation--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add SCStelz/security-investigator --skill computer-investigation -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install SCStelz/security-investigator computer-investigation --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/SCStelz/security-investigator.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.github/skills/computer-investigation .gemini/skills/computer-investigation && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "computer-investigation" agent skill from https://github.com/SCStelz/security-investigator/tree/main/.github/skills/computer-investigation into .gemini/skills/computer-investigation/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "computer-investigation", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install SCStelz/security-investigator computer-investigationInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add SCStelz/security-investigator --skill computer-investigation -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/SCStelz/security-investigator.git skills-src && mkdir -p .github/skills && cp -r skills-src/.github/skills/computer-investigation .github/skills/computer-investigation && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "computer-investigation" agent skill from https://github.com/SCStelz/security-investigator/tree/main/.github/skills/computer-investigation into .github/skills/computer-investigation/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "computer-investigation", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add SCStelz/security-investigator --skill computer-investigation -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install SCStelz/security-investigator computer-investigation --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/SCStelz/security-investigator.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.github/skills/computer-investigation .opencode/skills/computer-investigation && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "computer-investigation" agent skill from https://github.com/SCStelz/security-investigator/tree/main/.github/skills/computer-investigation into .opencode/skills/computer-investigation/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "computer-investigation", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
computer-investigationA skill your agent uses when asked to investigate a computer, device, endpoint, or machine for security issues, suspicious activity, malware, or compliance review.
Computer Investigation is an agent skill from SCStelz/security-investigator. Use this skill when asked to investigate a computer, device, endpoint, or machine for security issues, suspicious activity, malware, or compliance review. Triggers on keywords like "investigate computer", "investigate device", "investigate endpoint", "check machine", "device security", "endpoint investigation", or when a device name/hostname is mentioned with investigation context. This skill provides comprehensive device security analysis including Defender alerts, sign-in patterns, logged-on users…
Its SKILL.md is about 15k tokens, which your agent loads only when the skill is triggered. The skill folder holds 1 other file (for example `svg-widgets.yaml`).
It sits in Legal & Compliance, covering Regulatory compliance. It works with Microsoft Entra ID and Microsoft Defender. The repository describes itself as: Automated security investigation tool using Microsoft MCP Servers, GitHub Copilot, Python Modules and custom copilot-instructions. The licence is MIT.
12 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 51e1385. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
pythonFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
learn.microsoft.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Computer Investigation loads about 15k tokens when it runs. Until then it costs about 176 tokens; SKILL.md has 3,485 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found patterns that need a careful read before installing.
- ❌ Selecting a workspace without user consent when multiple existAutomated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from SCStelz/security-investigator at commit 51e1385, republished under its MIT licence (© SCStelz). 3,485 words, ~15,183 tokens.
.claude/skills/computer-investigation/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.This skill performs comprehensive security investigations on Windows, macOS, and Linux devices registered in Microsoft Entra ID and/or managed by Microsoft Defender for Endpoint. It analyzes Defender alerts, device compliance, sign-in patterns, logged-on users, installed software, vulnerabilities, network connections, and automated investigation results for:
Investigation shortcuts:
⛔ Shortcut Default Rule: When a matching shortcut exists for the investigation context, use it — don't run the full workflow. Only run the full query set when the user explicitly requests "full investigation", "comprehensive", or "deep dive". Shortcuts render only the report sections relevant to their query chain (plus Executive Summary and Recommendations, always).
Before starting ANY computer investigation:
create_file for JSON export and markdown reports (NEVER use PowerShell terminal commands)This skill requires a Sentinel workspace to execute queries. Follow these rules STRICTLY:
SELECTED_WORKSPACE_IDS passed from the parent skilllist_sentinel_workspaces MCP tool FIRSTIF query returns "Failed to resolve table" or similar error:
- STOP IMMEDIATELY
- Report: "⚠️ Query failed on workspace [NAME] ([ID]). Error: [ERROR_MESSAGE]"
- Display: "Available workspaces: [LIST_ALL_WORKSPACES]"
- ASK: "Which workspace should I use instead?"
- WAIT for explicit user response
- DO NOT retry with a different workspace automatically🔴 PROHIBITED ACTIONS:
Device ID Types:
Date Range Rules:
datetime(2026-01-16) to datetime(2026-01-25)AzureAdServerAd (indicates hybrid join with on-premises AD)WorkplaceWhen to use: General security reviews, routine investigations
Example prompts:
When to use: Urgent cases, active malware alerts, recent suspicious activity
Example prompts:
When to use: Deep-dive analysis, lateral movement detection, thorough forensics
Example prompts:
All types include: Defender alerts, device compliance, sign-in patterns from device, logged-on users, software inventory, vulnerabilities, network connections, file activities, automated investigation status, and security recommendations.
This skill supports three output modes. ASK the user which they prefer if not explicitly specified. Multiple modes may be selected simultaneously.
reports/computer-investigations/computer_investigation_<device_name>_<YYYYMMDD_HHMMSS>.mdcreate_file tool — NEVER use terminal commands for file outputcomputer_investigation_<device_name>_YYYYMMDD_HHMMSS.md (lowercase device name, replace spaces/special chars with underscores)█ full block, ─ box-drawing horizontal) display correctly in monospaced fonts| col |) render as formatted tablesWhen a user requests a computer security investigation:
Get Device IDs:
# First, find the device and get both Entra ID and Defender ID
mcp_microsoft_mcp_microsoft_graph_get("/v1.0/devices?$filter=displayName eq '<DEVICE_NAME>'&$select=id,deviceId,displayName,operatingSystem,trustType,isCompliant,isManaged")
# Then get Defender device ID from MDE
Use Defender `ListDefenderMachines` or Advanced Hunting to find by device nameRun Parallel Queries:
Export & Report (Mode-Dependent):
reports/computer-investigations/temp/investigation_device_<device_name>_<timestamp>.jsonGenerate Summary Report: Provide investigation summary with key findings, risk assessment, and recommendations.
Track time after each major step and report to user
YOU MUST TRACK AND REPORT TIME AFTER EVERY MAJOR STEP:
[MM:SS] ✓ Step description (XX seconds)Required Reporting Points:
Step 1a: Get Entra Device ID from Microsoft Graph
/v1.0/devices?$filter=displayName eq '<DEVICE_NAME>'&$select=id,deviceId,displayName,operatingSystem,operatingSystemVersion,trustType,isCompliant,isManaged,registrationDateTime,approximateLastSignInDateTime,mdmAppId,profileTypeStep 1b: Get Defender Device ID Use Advanced Hunting or Defender API to find the MDE device ID:
DeviceInfo
| where DeviceName startswith '<DEVICE_NAME>' // Use startswith to match both hostname and FQDN
| summarize arg_max(TimeGenerated, *) by DeviceName
| project DeviceId, DeviceName, OSPlatform, OSVersion, MachineGroup, OnboardingStatus, ExposureLevel, SensorHealthState, DeviceManualTags, DeviceDynamicTags, RegistryDeviceTagNote: RiskScore is NOT in DeviceInfo - use GetDefenderMachine API to get riskScore and exposureLevel.
Why BOTH IDs are required:
Device Type Determination:
trustType field from Graph API response:AzureAd = Entra JoinedServerAd = Hybrid JoinedWorkplace = Entra RegisteredCRITICAL: Use create_file tool to create JSON - NEVER use PowerShell terminal commands!
GetDefenderMachine) - Device info from MDEGetDefenderMachineLoggedOnUsers) - Recent usersGetDefenderMachineAlerts) - MDE alertsAssess IP enrichment needs:
python enrich_ips.py <ip1> <ip2> ... for threat intelligence enrichmentBuild the markdown report using the Markdown Report Template below
Save the report:
create_file("reports/computer-investigations/computer_investigation_<device_name>_YYYYMMDD_HHMMSS.md", markdown_content)create_file tool — NEVER use terminal commands for file outputExport to JSON:
create_file("temp/investigation_device_<device_name>_<timestamp>.json", json_content)Merge all results into one dict structure (see JSON Export Structure section below)
/v1.0/devices?$filter=displayName eq '<DEVICE_NAME>'&$select=id,deviceId,displayName,operatingSystem,operatingSystemVersion,trustType,isCompliant,isManaged,registrationDateTime,approximateLastSignInDateTime,mdmAppId,profileType,manufacturer,model,enrollmentType,deviceOwnershiptrustType determines device join typeisCompliant and isManaged indicate MDM statusUse the Defender GetDefenderMachine MCP tool with Defender Device ID:
Use these exact patterns with the appropriate MCP tool. Replace <DEVICE_NAME>, <DEVICE_ID>, <StartDate>, <EndDate>.
⚠️ CRITICAL: START WITH THESE EXACT QUERY PATTERNS These queries have been tested and validated. Use them as your PRIMARY reference.
CRITICAL: Use the correct parameter names for each tool!
query_lake toolqueryTimeGeneratedExample invocation:
query_lake(
query="DeviceInfo | where DeviceName startswith 'DEVICENAME' | summarize arg_max(TimeGenerated, *) by DeviceId",
workspaceId="<WORKSPACE_ID>"
)RunAdvancedHuntingQuery toolkqlQuery (NOT query!)Timestamp for XDR-native tables (Device*, Email*, etc.); TimeGenerated for LA/Sentinel tables (SigninLogs, SecurityAlert, etc.) — even in AHDeviceTvmSoftwareInventory, DeviceTvmSoftwareVulnerabilities) which don't exist in Data Lake.Example invocation:
RunAdvancedHuntingQuery(
kqlQuery="DeviceTvmSoftwareVulnerabilities | where DeviceName startswith 'DEVICENAME' | take 30"
)Follow the global Tool Selection Rule in .github/copilot-instructions.md (Data Lake vs Advanced Hunting). This skill does NOT override the global default — use Advanced Hunting first for ≤30d lookbacks (free for Analytics-tier tables), and fall back to Data Lake only for >30d windows or when AH is blocked by the safety filter.
| Table | Tool (lookback ≤30d) | Tool (lookback >30d) | Time Column |
|---|---|---|---|
Device* (DeviceInfo, DeviceProcessEvents, DeviceNetworkEvents, DeviceFileEvents, DeviceLogonEvents, DeviceRegistryEvents) | Advanced Hunting (free) | Data Lake | AH: Timestamp / DL: TimeGenerated |
SecurityAlert, SecurityIncident | Advanced Hunting | Data Lake | TimeGenerated (both tools) |
SigninLogs, AuditLogs, AADNonInteractiveUserSignInLogs | Advanced Hunting | Data Lake | TimeGenerated (both tools) |
DeviceTvmSoftwareInventory, DeviceTvmSoftwareVulnerabilities | Advanced Hunting only | Advanced Hunting only | Timestamp (snapshot, no time filter needed) |
When adapting the sample queries below: they are written with TimeGenerated for Data Lake compatibility. For Advanced Hunting on Device* tables, swap TimeGenerated → Timestamp. For SecurityAlert/SecurityIncident/SigninLogs in AH, keep TimeGenerated (LA/Sentinel tables retain their column name in AH).
Schema differences: Some MDE columns (e.g., SentBytes, ReceivedBytes in DeviceNetworkEvents) may not be available in Data Lake. If a column fails in one tool, try the other.
🔴 STEP 0: GET CURRENT DATE FIRST (MANDATORY) 🔴
RULE 1: Real-Time/Recent Searches (Current Activity)
datetime(2026-01-25) as end dateRULE 2: Historical Searches (User-Specified Dates)
Examples Table (Assuming Current Date = January 23, 2026):
| User Request | <StartDate> | <EndDate> | Rule Applied |
|---|---|---|---|
| "Last 7 days" | 2026-01-16 | 2026-01-25 | Rule 1 (+2) |
| "Last 30 days" | 2025-12-24 | 2026-01-25 | Rule 1 (+2) |
| "Jan 15 to Jan 20" | 2026-01-15 | 2026-01-21 | Rule 2 (+1) |
Note: DeviceDetail is dynamic in SigninLogs but string in AADNonInteractiveUserSignInLogs. Query SigninLogs only for device context (interactive sign-ins contain device info). Do NOT use union with DeviceDetail filtering - causes schema conflicts in Sentinel Data Lake.
let start = datetime(<StartDate>);
let end = datetime(<EndDate>);
let deviceName = '<DEVICE_NAME>';
SigninLogs
| where TimeGenerated between (start .. end)
| extend DeviceDetailStr = tostring(DeviceDetail)
| where DeviceDetailStr has deviceName
| extend ParsedDevice = parse_json(DeviceDetailStr)
| extend DeviceName = tostring(ParsedDevice.displayName)
| extend DeviceId = tostring(ParsedDevice.deviceId)
| extend DeviceOS = tostring(ParsedDevice.operatingSystem)
| extend DeviceTrustType = tostring(ParsedDevice.trustType)
| extend DeviceCompliant = tostring(ParsedDevice.isCompliant)
| summarize
SignInCount = count(),
SuccessCount = countif(ResultType == '0'),
FailureCount = countif(ResultType != '0'),
UniqueUsers = dcount(UserPrincipalName),
Users = make_set(UserPrincipalName, 10),
Applications = make_set(AppDisplayName, 10),
IPAddresses = make_set(IPAddress, 10),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by DeviceName, DeviceOS, DeviceTrustType, DeviceCompliant
| order by SignInCount desclet start = datetime(<StartDate>);
let end = datetime(<EndDate>);
let deviceName = '<DEVICE_NAME>';
SecurityAlert
| where TimeGenerated between (start .. end)
| where Entities has deviceName or CompromisedEntity has deviceName
| summarize arg_max(TimeGenerated, *) by SystemAlertId
| project
TimeGenerated,
AlertName,
AlertSeverity,
Status,
Description,
ProviderName,
Tactics,
Techniques,
CompromisedEntity,
RemediationSteps
| order by TimeGenerated desc
| take 20let start = datetime(<StartDate>);
let end = datetime(<EndDate>);
let deviceName = '<DEVICE_NAME>';
DeviceProcessEvents
| where TimeGenerated between (start .. end)
| where DeviceName startswith deviceName // Use startswith to match both hostname and FQDN
| where ActionType in ("ProcessCreated", "ProcessCreatedUsingWmiQuery")
| extend CommandLineLength = strlen(ProcessCommandLine)
| extend IsSuspicious = case(
ProcessCommandLine has_any ("powershell", "cmd", "wscript", "cscript") and ProcessCommandLine has_any ("-enc", "-e ", "bypass", "hidden", "downloadstring", "invoke-expression", "iex"), true,
ProcessCommandLine has_any ("certutil", "bitsadmin") and ProcessCommandLine has_any ("download", "transfer", "urlcache"), true,
ProcessCommandLine has_any ("reg", "registry") and ProcessCommandLine has_any ("add", "delete") and ProcessCommandLine has_any ("run", "runonce"), true,
FileName in~ ("mimikatz.exe", "procdump.exe", "psexec.exe", "cobaltstrike", "beacon.exe"), true,
CommandLineLength > 500, true,
false)
| summarize
ProcessCount = count(),
SuspiciousCount = countif(IsSuspicious),
UniqueProcesses = dcount(FileName),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
SampleCommands = make_set(ProcessCommandLine, 5)
by FileName, FolderPath, AccountName, AccountDomain
| where SuspiciousCount > 0 or ProcessCount > 50
| order by SuspiciousCount desc, ProcessCount desc
| take 20let start = datetime(<StartDate>);
let end = datetime(<EndDate>);
let deviceName = '<DEVICE_NAME>';
DeviceNetworkEvents
| where TimeGenerated between (start .. end)
| where DeviceName startswith deviceName // Use startswith to match both hostname and FQDN
| where ActionType == "ConnectionSuccess"
| where RemoteIPType != "Private" // Focus on public IPs
| summarize
ConnectionCount = count(),
UniqueRemoteIPs = dcount(RemoteIP),
UniqueRemotePorts = dcount(RemotePort),
Protocols = make_set(Protocol, 5),
InitiatingProcesses = make_set(InitiatingProcessFileName, 10),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by RemoteIP, RemotePort, RemoteUrl
| order by ConnectionCount desc
| take 30let start = datetime(<StartDate>);
let end = datetime(<EndDate>);
let deviceName = '<DEVICE_NAME>';
DeviceFileEvents
| where TimeGenerated between (start .. end)
| where DeviceName startswith deviceName // Use startswith to match both hostname and FQDN
| where ActionType in ("FileCreated", "FileModified", "FileDeleted", "FileRenamed")
| extend FileExtension = tostring(split(FileName, ".")[-1])
| extend IsSuspicious = case(
FileExtension in~ ("exe", "dll", "bat", "cmd", "ps1", "vbs", "js", "hta", "scr", "pif"), true,
FolderPath has_any ("\\temp\\", "\\tmp\\", "\\appdata\\local\\temp", "\\programdata\\", "\\users\\public\\"), true,
false)
| summarize
FileEventCount = count(),
SuspiciousCount = countif(IsSuspicious),
CreatedCount = countif(ActionType == "FileCreated"),
ModifiedCount = countif(ActionType == "FileModified"),
DeletedCount = countif(ActionType == "FileDeleted"),
UniqueFiles = dcount(FileName),
FileExtensions = make_set(FileExtension, 10),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by FolderPath, InitiatingProcessFileName
| where SuspiciousCount > 0 or FileEventCount > 100
| order by SuspiciousCount desc, FileEventCount desc
| take 20let start = datetime(<StartDate>);
let end = datetime(<EndDate>);
let deviceName = '<DEVICE_NAME>';
DeviceRegistryEvents
| where TimeGenerated between (start .. end)
| where DeviceName startswith deviceName // Use startswith to match both hostname and FQDN
| where ActionType in ("RegistryValueSet", "RegistryKeyCreated")
| extend IsPersistence = case(
RegistryKey has_any ("\\CurrentVersion\\Run", "\\CurrentVersion\\RunOnce", "\\CurrentVersion\\RunServices"), true,
RegistryKey has_any ("\\Policies\\Explorer\\Run", "\\Active Setup\\Installed Components"), true,
RegistryKey has_any ("\\Image File Execution Options\\", "\\Winlogon\\", "\\BootExecute"), true,
RegistryKey has_any ("\\Services\\", "\\Drivers\\"), true,
false)
| summarize
RegistryEventCount = count(),
PersistenceCount = countif(IsPersistence),
UniqueKeys = dcount(RegistryKey),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by RegistryKey, RegistryValueName, InitiatingProcessFileName
| where PersistenceCount > 0
| order by PersistenceCount desc, RegistryEventCount desc
| take 20let deviceName = '<DEVICE_NAME>';
let deviceId = '<DEVICE_ID>';
let start = datetime(<StartDate>);
let end = datetime(<EndDate>);
let relevantAlerts = SecurityAlert
| where TimeGenerated between (start .. end)
| where Entities has deviceName or Entities has deviceId or CompromisedEntity has deviceName
| summarize arg_max(TimeGenerated, *) by SystemAlertId
| project SystemAlertId, AlertName, AlertSeverity, ProviderName, Tactics;
SecurityIncident
| where CreatedTime between (start .. end)
| summarize arg_max(TimeGenerated, *) by IncidentNumber
| where not(tostring(Labels) has "Redirected")
| mv-expand AlertId = AlertIds
| extend AlertId = tostring(AlertId)
| join kind=inner relevantAlerts on $left.AlertId == $right.SystemAlertId
| extend ProviderIncidentUrl = tostring(AdditionalData.providerIncidentUrl)
| extend OwnerUPN = tostring(Owner.userPrincipalName)
| summarize
Title = any(Title),
Severity = any(Severity),
Status = any(Status),
Classification = any(Classification),
CreatedTime = any(CreatedTime),
LastModifiedTime = any(LastModifiedTime),
OwnerUPN = any(OwnerUPN),
ProviderIncidentUrl = any(ProviderIncidentUrl),
AlertCount = count(),
Tactics = make_set(Tactics)
by ProviderIncidentId
| order by LastModifiedTime desc
| take 10Note: RiskScore is NOT in DeviceInfo - use GetDefenderMachine API for risk/exposure scores.
let start = datetime(<StartDate>);
let end = datetime(<EndDate>);
let deviceName = '<DEVICE_NAME>';
DeviceInfo
| where TimeGenerated between (start .. end)
| where DeviceName startswith deviceName // Use startswith to match both hostname and FQDN
| summarize arg_max(TimeGenerated, *) by DeviceId
| project
TimeGenerated,
DeviceId,
DeviceName,
OSPlatform,
OSVersion,
OSBuild,
OSArchitecture,
LoggedOnUsers,
MachineGroup,
DeviceCategory,
OnboardingStatus,
SensorHealthState,
ExposureLevel,
IsAzureADJoined,
IsInternetFacing,
JoinType,
PublicIP,
DeviceManualTags,
DeviceDynamicTags,
RegistryDeviceTag⚠️ DO NOT use Sentinel Data Lake MCP (query_lake) for this query. The DeviceTvmSoftwareInventory table is NOT available in the Sentinel Data Lake. Use Advanced Hunting MCP (RunAdvancedHuntingQuery) only. TVM tables use snapshot ingestion with no TimeGenerated filtering.
let deviceName = '<DEVICE_NAME>';
DeviceTvmSoftwareInventory
| where DeviceName startswith deviceName // Use startswith to match both hostname and FQDN
| project
DeviceName,
SoftwareVendor,
SoftwareName,
SoftwareVersion,
EndOfSupportStatus,
EndOfSupportDate
| summarize by SoftwareVendor, SoftwareName, SoftwareVersion, EndOfSupportStatus, EndOfSupportDate
| order by NumberOfWeaknesses desc
| take 30⚠️ DO NOT use Sentinel Data Lake MCP (query_lake) for this query. The DeviceTvmSoftwareVulnerabilities table is NOT available in the Sentinel Data Lake. Use Advanced Hunting MCP (RunAdvancedHuntingQuery) only. TVM tables use snapshot ingestion with no TimeGenerated filtering.
let deviceName = '<DEVICE_NAME>';
DeviceTvmSoftwareVulnerabilities
| where DeviceName startswith deviceName // Use startswith to match both hostname and FQDN
| project
CveId,
VulnerabilitySeverityLevel,
SoftwareVendor,
SoftwareName,
SoftwareVersion,
RecommendedSecurityUpdate,
RecommendedSecurityUpdateId
| summarize by CveId, VulnerabilitySeverityLevel, SoftwareVendor, SoftwareName, SoftwareVersion, RecommendedSecurityUpdate, RecommendedSecurityUpdateId
| order by case(VulnerabilitySeverityLevel == "Critical", 1, VulnerabilitySeverityLevel == "High", 2, VulnerabilitySeverityLevel == "Medium", 3, 4) asc
| take 30let start = datetime(<StartDate>);
let end = datetime(<EndDate>);
let deviceName = '<DEVICE_NAME>';
DeviceLogonEvents
| where TimeGenerated between (start .. end)
| where DeviceName startswith deviceName // Use startswith to match both hostname and FQDN
| summarize
LogonCount = count(),
SuccessCount = countif(ActionType == "LogonSuccess"),
FailureCount = countif(ActionType == "LogonFailed"),
UniqueAccounts = dcount(AccountName),
LogonTypes = make_set(LogonType, 5),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
RemoteIPs = make_set(RemoteIP, 10)
by AccountName, AccountDomain, LogonType
| order by LogonCount desc
| take 20Performance notes: ThreatIntelIndicators can be large (100K+ rows). Filter IsActive/ValidUntil before string transformations per KQL best practices — reduce data first, transform later. The triple replace_string was replaced with direct array indexing split(...)[0] which returns a clean string.
let start = datetime(<StartDate>);
let end = datetime(<EndDate>);
let deviceName = '<DEVICE_NAME>';
let device_ips = DeviceNetworkEvents
| where TimeGenerated between (start .. end)
| where DeviceName startswith deviceName // Use startswith to match both hostname and FQDN
| where RemoteIPType != "Private"
| distinct RemoteIP;
ThreatIntelIndicators
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
| where tostring(split(ObservableKey, ":")[0]) in ("ipv4-addr", "ipv6-addr", "network-traffic")
| where ObservableValue in (device_ips)
| extend Description = tostring(parse_json(Data).description)
| where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;"
| summarize arg_max(TimeGenerated, *) by ObservableValue
| project
TimeGenerated,
IPAddress = ObservableValue,
ThreatDescription = Description,
Confidence,
ValidUntil,
IsActive
| order by Confidence desc
| take 20Use these Graph API queries in Phase 2 (Batch 3) of investigation workflow
mcp_microsoft_mcp_microsoft_graph_get("/v1.0/devices?$filter=displayName eq '<DEVICE_NAME>'&$select=id,deviceId,displayName,operatingSystem,operatingSystemVersion,trustType,isCompliant,isManaged,registrationDateTime,approximateLastSignInDateTime,mdmAppId,profileType,manufacturer,model,enrollmentType,deviceOwnership")mcp_microsoft_mcp_microsoft_graph_get("/v1.0/devices/<DEVICE_OBJECT_ID>/registeredOwners?$select=id,displayName,userPrincipalName")mcp_microsoft_mcp_microsoft_graph_get("/v1.0/devices/<DEVICE_OBJECT_ID>/registeredUsers?$select=id,displayName,userPrincipalName")mcp_microsoft_mcp_microsoft_graph_get("/v1.0/informationProtection/bitlocker/recoveryKeys?$filter=deviceId eq '<DEVICE_ID>'")NOTE: Requires BitLockerKey.Read.All permission
mcp_microsoft_mcp_microsoft_graph_get("/v1.0/deviceManagement/managedDevices?$filter=deviceName eq '<DEVICE_NAME>'&$select=id,deviceName,managedDeviceOwnerType,complianceState,managementAgent,lastSyncDateTime,osVersion,azureADRegistered,azureADDeviceId,deviceEnrollmentType,deviceCategoryDisplayName,serialNumber,userPrincipalName")Use these MDE API queries in Phase 2 (Batch 2) of investigation workflow
GetDefenderMachine(id="<DEFENDER_DEVICE_ID>")Returns: id, computerDnsName, osPlatform, osVersion, healthStatus, onboardingStatus, riskScore, exposureLevel, lastSeen, lastIpAddress, lastExternalIpAddress, rbacGroupName, machineTags (API field — maps to DeviceManualTags in AH)
GetDefenderMachineLoggedOnUsers(id="<DEFENDER_DEVICE_ID>")Returns: Array of users with accountName, accountDomain, firstSeen, lastSeen, logonTypes
Use the ListAlerts MCP tool filtered by device:
ListAlerts with machineId filterListDefenderInvestigationsFilter results by machineId to find investigations related to the device
ListDefenderRemediationActivitiesFilter results by machineId to find remediation tasks for the device
When outputting to markdown file (Mode 2), use this template. Populate ALL sections with actual query data. For sections with no data, use the explicit absence confirmation pattern.
Filename pattern: reports/computer-investigations/computer_investigation_<device_name>_YYYYMMDD_HHMMSS.md
# Computer Security Investigation Report
**Generated:** YYYY-MM-DD HH:MM UTC
**Workspace:** <workspace_name>
**Device:** `<DEVICE_NAME>`
**OS:** <operating_system> <os_version>
**Trust Type:** <Entra Joined / Hybrid Joined / Entra Registered> (`<trustType>`)
**Compliance:** <Compliant/Non-Compliant> | **Managed:** <Yes/No> | **MDM:** <Intune/None>
**Investigation Period:** <start_date> → <end_date> (<N> days)
**Investigation Type:** <Standard (7d) / Quick (1d) / Comprehensive (30d)>
**Data Sources:** DeviceInfo, DeviceProcessEvents, DeviceNetworkEvents, DeviceFileEvents, DeviceRegistryEvents, DeviceLogonEvents, SigninLogs, SecurityAlert, SecurityIncident, DeviceTvmSoftwareVulnerabilities, DeviceTvmSoftwareInventory, ThreatIntelIndicators, Microsoft Graph API, Defender for Endpoint API
---
## Executive Summary
<2-4 sentence summary: overall device risk level, key findings, most significant alerts or vulnerabilities, and primary recommendation. Ground every claim in evidence from query results.>
**Overall Risk Level:** 🔴 CRITICAL / 🔴 HIGH / 🟠 MEDIUM / 🟡 LOW / 🟢 INFORMATIONAL
---
## Device Profile
| Property | Value |
|----------|-------|
| **Device Name** | `<device_name>` |
| **OS** | <os_platform> <os_version> (<os_build>) |
| **Architecture** | <os_architecture> |
| **Trust Type** | <Entra Joined / Hybrid Joined / Entra Registered> |
| **Compliant** | 🟢 Yes / 🔴 No |
| **Managed** | 🟢 Yes / 🔴 No |
| **Manufacturer** | <manufacturer> |
| **Model** | <model> |
| **Registration Date** | <datetime> |
| **Last Sign-in** | <datetime> |
| **Internet Facing** | 🔴 Yes / 🟢 No |
### Defender for Endpoint Status
| Property | Value |
|----------|-------|
| **Onboarding Status** | 🟢 Onboarded / 🔴 Not Onboarded |
| **Sensor Health** | 🟢 Active / 🟠 Inactive / 🔴 Misconfigured |
| **Health Status** | <health_status> |
| **Risk Score** | 🔴/🟠/🟡/🟢 <None/Low/Medium/High> |
| **Exposure Level** | 🔴/🟠/🟡/🟢 <None/Low/Medium/High> |
| **Last Seen** | <datetime> |
| **Last Internal IP** | <ip_address> |
| **Last External IP** | <ip_address> |
| **Machine Group** | <group_name> |
| **Device Tags** | <comma-separated list from DeviceManualTags + DeviceDynamicTags, or "None"> |
### Device Owners & Registered Users
<If owners/users found:>
| User | UPN | Role |
|------|-----|------|
| <display_name> | <upn> | Owner / Registered User |
<If no owners/users:>
✅ No registered owners or users found for this device.
---
## Key Metrics
| Metric | Value |
|--------|-------|
| **Security Alerts** | <count> (Critical: <n>, High: <n>, Medium: <n>, Low: <n>) |
| **Security Incidents** | <count> (Open: <n>, Closed: <n>) |
| **Logged-On Users** | <count> unique users |
| **Sign-ins from Device** | <count> (Success: <n>, Failed: <n>) |
| **Vulnerabilities** | <count> (Critical: <n>, High: <n>, Medium: <n>) |
| **Suspicious Processes** | <count> flagged |
| **Network Connections** | <count> external IPs |
| **TI Matches** | <count> threat intel hits |
| **End-of-Support Software** | <count> |
---
## Security Alerts
<If alerts found:>
| Time | Alert Name | Severity | Status | Provider | Tactics | Compromised Entity |
|------|-----------|----------|--------|----------|---------|---------------------|
| <datetime> | <alert_name> | 🔴/🟠/🟡 <severity> | <status> | <provider> | <tactics> | <entity> |
**Alert Summary:**
- <X> total alerts (<breakdown by severity>)
- <Brief description of most critical alert(s)>
- Remediation steps: <summary of recommended actions from alert data>
<If no alerts:>
✅ No security alerts detected for this device in the investigation period.
- Checked: SecurityAlert filtered by device name and device ID (0 matches)
---
## Security Incidents
<If incidents found:>
| ID | Title | Severity | Status | Classification | Created | Owner | Alerts | Link |
|----|-------|----------|--------|----------------|---------|-------|--------|------|
| <provider_incident_id> | <title> | 🔴/🟠/🟡 <severity> | <New/Active/Closed> | <TP/FP/BP/—> | <date> | <owner_upn> | <count> | [View](<url>) |
**Incident Summary:**
- <X> total incidents (<Y> open, <Z> closed)
- Highest severity: <level>
- <Brief description of most critical incident>
<If no incidents:>
✅ No security incidents involving this device in the investigation period.
- Checked: SecurityAlert → SecurityIncident join on device name and device ID (0 matches)
---
## Logged-On Users
<If users found:>
| Account | Domain | Logon Type | Logon Count | Success | Failed | First Seen | Last Seen |
|---------|--------|------------|:-----------:|:-------:|:------:|------------|-----------|
| <account_name> | <domain> | <Interactive/RemoteInteractive/Network/etc.> | <count> | <count> | <count> | <date> | <date> |
**User Analysis:**
- <X> unique accounts authenticated on this device
- <Summary of logon patterns — expected vs unexpected accounts, after-hours logons, remote IPs>
<If no logon data:>
✅ No logon events detected for this device in the investigation period.
### Defender Logged-On Users (API)
<If MDE logged-on users found:>
| Account | Domain | First Seen | Last Seen | Logon Types |
|---------|--------|------------|-----------|-------------|
| <account_name> | <domain> | <date> | <date> | <types> |
<If no MDE data:>
✅ No logged-on user data returned from Defender for Endpoint API.
---
## Sign-in Activity (From Device)
<If sign-in events found:>
| Device Name | OS | Trust Type | Compliant | Users | Applications | IPs | Sign-ins | Success | Failed | First Seen | Last Seen |
|-------------|-----|------------|-----------|:-----:|:------------:|:---:|:--------:|:-------:|:------:|------------|-----------|
| <name> | <os> | <trust> | 🟢/🔴 | <count> | <count> | <count> | <count> | <count> | <count> | <date> | <date> |
**Top Users:** <list of UPNs>
**Top Applications:** <list of apps>
**Top IPs:** <list of IPs>
<If no sign-in events:>
✅ No sign-in events found for this device in the investigation period.
---
## Process Activity
<If suspicious processes found:>
| Process | Path | Account | Process Count | Suspicious | Sample Command Lines |
|---------|------|---------|:------------:|:----------:|----------------------|
| <filename> | <folder_path> | <account_name> | <count> | 🔴 <count> | <truncated_command> |
**Process Analysis:**
- <X> suspicious process executions detected
- <Summary of suspicious patterns — encoded commands, LOLBins, credential dumping tools, long command lines>
<If no suspicious processes:>
✅ No suspicious process activity detected on this device in the investigation period.
- Checked: DeviceProcessEvents filtered for suspicious indicators (0 flagged)
---
## Network Connections
<If external connections found:>
| Remote IP | Remote Port | URL | Connections | Unique Ports | Protocols | Initiating Processes | First Seen | Last Seen |
|-----------|:-----------:|-----|:-----------:|:------------:|-----------|----------------------|------------|-----------|
| <ip> | <port> | <url> | <count> | <count> | <protocols> | <process_list> | <date> | <date> |
**Network Summary:**
- <X> unique external IPs contacted
- <Y> unique remote ports
- <Top initiating processes>
<If no external connections:>
✅ No external network connections detected for this device in the investigation period.
### Threat Intelligence Matches
<If TI matches found:>
| IP Address | Threat Description | Confidence | Valid Until | Active |
|------------|-------------------|:----------:|------------|:------:|
| <ip> | <description> | <score> | <date> | ✅/❌ |
<If no TI matches:>
✅ No threat intelligence matches found for device network traffic.
- Checked: ThreatIntelIndicators joined with device external IPs (0 matches)
---
## File Activity
<If suspicious file events found:>
| Folder Path | Initiating Process | Total Events | Suspicious | Created | Modified | Deleted | Extensions | First Seen | Last Seen |
|-------------|-------------------|:------------:|:----------:|:-------:|:--------:|:-------:|------------|------------|-----------|
| <path> | <process> | <count> | 🔴 <count> | <count> | <count> | <count> | <ext_list> | <date> | <date> |
**File Activity Analysis:**
- <X> suspicious file operations detected
- <Summary — executable drops in temp folders, script creation, mass file modifications>
<If no suspicious file events:>
✅ No suspicious file activity detected on this device in the investigation period.
- Checked: DeviceFileEvents for suspicious extensions and temp folder activity (0 flagged)
---
## Registry Modifications
<If persistence-related registry events found:>
| Registry Key | Value Name | Initiating Process | Total Events | Persistence | First Seen | Last Seen |
|-------------|------------|-------------------|:------------:|:-----------:|------------|-----------|
| <key> | <value_name> | <process> | <count> | 🔴 <count> | <date> | <date> |
**Registry Analysis:**
- <X> persistence-related registry modifications detected
- <Summary — Run keys, services, Winlogon, IFEO modifications>
<If no persistence registry events:>
✅ No persistence-related registry modifications detected on this device in the investigation period.
- Checked: DeviceRegistryEvents for Run/RunOnce/Services/Winlogon/IFEO keys (0 flagged)
---
## Vulnerabilities
<If vulnerabilities found:>
| CVE ID | Severity | Vendor | Software | Version | Security Update |
|--------|----------|--------|----------|---------|-----------------|
| <cve_id> | 🔴/🟠/🟡 <severity> | <vendor> | <software> | <version> | <update_id> |
**Vulnerability Summary:**
- <X> total vulnerabilities (Critical: <n>, High: <n>, Medium: <n>, Low: <n>)
- <Most critical CVEs and their remediation status>
<If no vulnerabilities:>
✅ No known vulnerabilities detected on this device.
- Checked: DeviceTvmSoftwareVulnerabilities (0 records)
---
## Software Inventory
<If notable software found:>
| Vendor | Software | Version | End of Support | EOS Date |
|--------|----------|---------|:--------------:|----------|
| <vendor> | <software> | <version> | 🔴 Yes / 🟢 No | <date> |
**Software Summary:**
- <X> total software packages installed
- <Y> end-of-support software detected
- <Notable findings — outdated browsers, deprecated runtimes, risky applications>
<If no software data:>
✅ No software inventory data available for this device.
- Checked: DeviceTvmSoftwareInventory (0 records)
---
## Device Configuration
<If configuration data available:>
| Property | Value |
|----------|-------|
| **Public IP** | <ip> |
| **Machine Group** | <group> |
| **Device Category** | <category> |
| **Onboarding Status** | <status> |
| **Sensor Health** | <health> |
| **Exposure Level** | <level> |
| **Azure AD Joined** | <Yes/No> |
| **Internet Facing** | <Yes/No> |
| **Join Type** | <type> |
---
## IP Intelligence
<Table of external IPs from network connections and sign-in data. Run `enrich_ips.py` for top IPs.>
| IP Address | Source | Location | ISP/Org | VPN | Abuse Score | Reports | Risk |
|------------|--------|----------|---------|-----|-------------|---------|------|
| <ip> | 🔵 Network / 🔵 Sign-in / 🔴 TI Match | <city, country> | <org> | 🟢 No / 🔴 Yes | <score>% | <count> | HIGH/MED/LOW |
---
## Risk Assessment
### Risk Score: <XX>/100 — 🔴 CRITICAL / 🔴 HIGH / 🟠 MEDIUM / 🟡 LOW / 🟢 INFORMATIONAL
### Risk Factors
| Factor | Finding |
|--------|---------|
| 🔴/🟠/🟡 **<Factor Name>** | <Evidence-grounded finding with specific numbers> |
### Mitigating Factors
| Factor | Finding |
|--------|---------|
| 🟢 **<Factor Name>** | <Evidence-grounded finding with specific numbers> |
---
## Recommendations
### Critical Actions
<Numbered list of critical actions with evidence. Only include if critical findings exist.>
### High Priority Actions
<Numbered list of high-priority actions with evidence.>
### Monitoring Actions (14-Day Follow-Up)
<Bulleted list of ongoing monitoring recommendations.>
---
## Appendix: Query Details
| # | Query | Table(s) | Tool | Records | Execution |
|---|-------|----------|------|--------:|----------:|
| 1 | Device Sign-In Events | SigninLogs | Data Lake | <count> | <time> |
| 2 | Security Alerts | SecurityAlert | Data Lake | <count> | <time> |
| 3 | Process Events | DeviceProcessEvents | Data Lake | <count> | <time> |
| 4 | Network Connections | DeviceNetworkEvents | Data Lake | <count> | <time> |
| 5 | File Events | DeviceFileEvents | Data Lake | <count> | <time> |
| 6 | Registry Events | DeviceRegistryEvents | Data Lake | <count> | <time> |
| 7 | Security Incidents | SecurityAlert, SecurityIncident | Data Lake | <count> | <time> |
| 8 | Device Inventory | DeviceInfo | Data Lake | <count> | <time> |
| 9 | Software Inventory | DeviceTvmSoftwareInventory | Advanced Hunting | <count> | <time> |
| 10 | Vulnerabilities | DeviceTvmSoftwareVulnerabilities | Advanced Hunting | <count> | <time> |
| 11 | Logon Events | DeviceLogonEvents | Data Lake | <count> | <time> |
| 12 | Threat Intelligence | ThreatIntelIndicators, DeviceNetworkEvents | Data Lake | <count> | <time> |
| — | Device Profile | Microsoft Graph API | Graph | 1 | <time> |
| — | Device Owners/Users | Microsoft Graph API | Graph | <count> | <time> |
| — | Machine Details | Defender for Endpoint API | MDE | 1 | <time> |
| — | Logged-On Users | Defender for Endpoint API | MDE | <count> | <time> |
*Query definitions: see the Sample KQL Queries section in this SKILL.md file.*
**Do NOT include full KQL text in the appendix** — the canonical queries are already documented in this SKILL.md file. The appendix serves as an audit trail only.
---
**Investigation Timeline:**
- [MM:SS] ✓ Phase 1: Device ID retrieval (<X>s)
- [MM:SS] ✓ Phase 2: Parallel data collection (<X>s)
- [MM:SS] ✓ IP Enrichment (<X>s)
- [MM:SS] ✓ Phase 3: Report generation (<X>s)
- **Total Investigation Time:** <duration>✅ No <X> detected... pattern for empty sections.enrich_ips.py for external IPs from network connections and sign-in data. If enrich_ips.py is unavailable, use Sentinel ThreatIntelIndicators data as fallback.reports/ which is gitignored. However, exercise caution with any files that may be shared externally.copilot-instructions.md for all risk/status indicators.Export MCP query results to a single JSON file with these required keys:
{
"device_name": "WORKSTATION-001",
"device_id_entra": "<ENTRA_DEVICE_OBJECT_ID>",
"device_id_defender": "<DEFENDER_DEVICE_ID>",
"device_type": "HybridJoined",
"investigation_date": "2026-01-23",
"start_date": "2026-01-16",
"end_date": "2026-01-25",
"timestamp": "20260123_143200",
"device_profile": {
"displayName": "WORKSTATION-001",
"operatingSystem": "Windows",
"operatingSystemVersion": "10.0.22621.3007",
"trustType": "ServerAd",
"isCompliant": true,
"isManaged": true,
"registrationDateTime": "2025-06-15T10:30:00Z",
"approximateLastSignInDateTime": "2026-01-23T14:00:00Z",
"manufacturer": "Dell Inc.",
"model": "Latitude 5520"
},
"defender_profile": {
"healthStatus": "Active",
"riskScore": "Medium",
"exposureLevel": "Low",
"onboardingStatus": "Onboarded",
"sensorHealthState": "Active",
"lastSeen": "2026-01-23T14:30:00Z",
"lastIpAddress": "10.0.1.50",
"lastExternalIpAddress": "203.0.113.42"
},
"device_owners": [...],
"device_users": [...],
"signin_events": [...],
"security_alerts": [...],
"process_events": [...],
"network_events": [...],
"file_events": [...],
"registry_events": [...],
"incidents": [...],
"logged_on_users": [...],
"software_inventory": [...],
"vulnerabilities": [...],
"automated_investigations": [...],
"remediation_activities": [...],
"threat_intel_matches": [...],
"summary": {
"total_alerts": 5,
"critical_alerts": 1,
"high_alerts": 2,
"medium_alerts": 2,
"low_alerts": 0,
"total_vulnerabilities": 15,
"critical_vulnerabilities": 2,
"unique_logged_on_users": 3,
"suspicious_processes": 4,
"threat_intel_hits": 1
}
}| Issue | Solution |
|---|---|
| Device not found in Graph API | Try searching by deviceId instead of displayName, check case sensitivity |
| Defender Device ID not matching | Use Advanced Hunting to find correct Defender ID by device name |
| DeviceName query returns empty | Use startswith instead of =~ - DeviceName often contains FQDN (e.g., hostname.domain.com) |
| SigninLogs DeviceDetail fails with union | DeviceDetail is dynamic in SigninLogs but string in AADNonInteractiveUserSignInLogs - query tables separately, don't use union isfuzzy=true with DeviceDetail filtering |
| RiskScore column not found | RiskScore is NOT in DeviceInfo table - use GetDefenderMachine API for riskScore |
| Missing compliance data | Device may not be MDM enrolled - check isManaged field |
| No process events | Device may not be onboarded to Defender for Endpoint |
| Trust type is null | Device may be partially registered - check registrationDateTime |
| Query timeout on DeviceEvents | Reduce date range or add more specific filters |
| BitLocker query fails | Verify permissions and that BitLocker is enabled on device |
{
"trustType": "Workplace",
"isCompliant": false,
"isManaged": false,
"approximateLastSignInDateTime": "1970-01-01T00:00:00Z",
"riskScore": "Unknown",
"exposureLevel": "Unknown",
"healthStatus": "Unknown"
}{
"signin_events": [],
"security_alerts": [],
"process_events": [],
"network_events": [],
"file_events": [],
"registry_events": [],
"incidents": [],
"logged_on_users": [],
"software_inventory": [],
"vulnerabilities": [],
"automated_investigations": [],
"remediation_activities": [],
"threat_intel_matches": []
}trustType: AzureAd)trustType: ServerAd)trustType: Workplace)| Factor | Weight | High Risk Indicators |
|---|---|---|
| Defender Risk Score | 25% | "High" or "Critical" |
| Active Alerts | 25% | Any Critical/High severity alerts |
| Vulnerabilities | 20% | Critical CVEs, end-of-support software |
| Compliance Status | 15% | Non-compliant, not managed |
| Sign-in Anomalies | 15% | Multiple users, unusual hours, new IPs |
This skill follows all patterns from the main copilot-instructions.md:
create_file for all outputcopilot-instructions.md for cross-entity correlationExample invocations:
After generating a computer investigation report (markdown file output), an SVG dashboard can be created using the shared SVG rendering skill.
Trigger: User asks "generate an SVG dashboard from the report" or "visualize this report"
Workflow:
svg-widgets.yaml (widget manifest — defines layout, colors, field mapping).github/skills/svg-dashboard/SKILL.md (rendering rules — component library, quality standards)data_sources.field_mapping_notes{report_basename}_dashboard.svg in the same directoryLayout: 5 rows — title banner, risk score card + KPI cards (alerts/incidents/vulnerabilities/users/EOS software), alerts by MITRE tactic bar chart + vulnerabilities by severity bar chart, incidents table + risk/mitigating factors table, assessment banner + recommendations.
Last Updated: March 24, 2026
© SCStelz, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 1 other file in .github/skills/computer-investigation of SCStelz/security-investigator.
Open the folder on GitHubat commit 51e1385
Computer Investigation next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Computer Investigation this skillSCStelz/security-investigator | 249 | — | ~15k | Automated safety check: Warn | MIT | |
| Azure Policyvinayaklatthe/microsoft-security-skills | 175 | — | ~1.9k | Automated safety check: Pass | MIT | |
| Intune Device Mgmtvinayaklatthe/microsoft-security-skills | 175 | — | ~1.8k | Automated safety check: Pass | MIT | |
| Paw Designvinayaklatthe/microsoft-security-skills | 175 | — | ~1.9k | Automated safety check: Pass | MIT | |
| Implementing Azure Defender For Cloudmukul975/Anthropic-Cybersecurity-Skills | 34k | — | ~3.1k | Automated safety check: Pass | Apache-2.0 | |
| HIPAA Pre-Deployment Compliance Checkmaziyarpanahi/openmed | 5.5k | — | ~2k | Automated safety check: Pass | Apache-2.0 |
vinayaklatthe/microsoft-security-skills
Guidance for Azure Policy — enforcing and auditing governance and security guardrails at scale across Azure with definitions, initiatives, assignments, and remediation tasks.
vinayaklatthe/microsoft-security-skills
Guidance for Microsoft Intune device management — enrollment, configuration, compliance, and security baselines across Windows, macOS, iOS/iPadOS, and Android.
vinayaklatthe/microsoft-security-skills
Guidance for designing Privileged Access Workstations (PAW) and the Microsoft privileged access strategy (enterprise access model, clean source principle, tiered admin isolation).
mukul975/Anthropic-Cybersecurity-Skills
Enable Microsoft Defender for Cloud (CSPM + CWPP) across VMs, containers, SQL, storage, and Key Vault, using Azure Policy for evaluation, Log Analytics for telemetry, Azure Arc for hybrid coverage…
maziyarpanahi/openmed
Walks a data pipeline against the HIPAA Privacy and Security Rule checklist and produces a gap report before it processes patient data.
Sushegaad/Claude-Skills-Governance-Risk-and-Compliance
Expert HIPAA compliance assistant for healthcare and software contexts.
SCStelz/security-investigator
A skill your agent uses when asked to investigate Conditional Access policy changes, sign-in failures related to CA policies (error codes 53000, 50074, 530032), or suspected policy…
SCStelz/security-investigator
Weekly review of an investigation tenant-context memory file against the most recent SOC scan reports (e.g.
SCStelz/security-investigator
A skill your agent uses when asked to create heatmaps, visualize patterns over time, show activity grids, or display aggregated data in a matrix format.
SCStelz/security-investigator
Report/investigate RUNTIME ACTIVITY of AI agents (Agent 365 / Copilot Studio / M365 Copilot / Work IQ) — agents used, tools/connectors, channels, tokens, prompt/reply content, and Prompt Shield…
SCStelz/security-investigator
Audit or report on AI agent security posture across Copilot Studio, Microsoft 365 Copilot, Microsoft Foundry, and third-party agents.
SCStelz/security-investigator
Audit Entra ID app registration and service principal security posture.
Works with
Categories
A skill your agent uses when asked to investigate a computer, device, endpoint, or machine for security issues, suspicious activity, malware, or compliance review. Computer Investigation is an agent skill from SCStelz/security-investigator. Use this skill when asked to investigate a computer, device, endpoint, or machine for security issues, suspicious activity, malware, or compliance review.
Computer Investigation fits situations like: asked to investigate a computer; machine for security issues; suspicious activity; compliance review.
Run `npx skills add SCStelz/security-investigator --skill computer-investigation -a claude-code`. Or copy the skill folder (.github/skills/computer-investigation in SCStelz/security-investigator) into .claude/skills/computer-investigation in your project. Claude Code loads it when a task matches its description.
Run `npx skills add SCStelz/security-investigator --skill computer-investigation -a codex`. Or copy the skill folder (.github/skills/computer-investigation in SCStelz/security-investigator) into .agents/skills/computer-investigation in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add SCStelz/security-investigator --skill computer-investigation -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/computer-investigation, .gemini/skills/computer-investigation, .github/skills/computer-investigation and .opencode/skills/computer-investigation in your project.
Going by SKILL.md and its folder, Computer Investigation needs the command-line tools its instructions call (python).
SKILL.md names 1 domain. As links in the text: learn.microsoft.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md flagged 1 warning(s): contains instruction-override wording (e.g. “without asking the user”). Read the flagged lines before installing; the check is not a guarantee either way.
Computer Investigation is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 15k tokens (SKILL.md is roughly 61k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Computer Investigation: Azure Policy (vinayaklatthe/microsoft-security-skills, 175 stars), Intune Device Mgmt (vinayaklatthe/microsoft-security-skills, 175 stars), Paw Design (vinayaklatthe/microsoft-security-skills, 175 stars) and Implementing Azure Defender For Cloud (mukul975/Anthropic-Cybersecurity-Skills, 34k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
SCStelz (a GitHub user) maintains it in SCStelz/security-investigator, which has 249 GitHub stars. The repository holds 22 skills in this directory. The repository was last updated on October 8, 2026.
Source: SCStelz/security-investigator on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.