بنقرة واحدة
installed-apps
Inventory installed applications and software on Windows Servers
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Inventory installed applications and software on Windows Servers
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Connect to Azure VMs via PowerShell remoting over public IP (HTTPS/5986)
Test connectivity and establish PowerShell remoting sessions to Windows Servers
Check installed Windows Server roles and features
Analyze disk space, volume health, and storage capacity on Windows Servers
Analyze Windows Event Logs for errors, warnings, and critical events
Analyze network configuration, adapters, DNS, and connectivity on Windows Servers
| name | installed-apps |
| description | Inventory installed applications and software on Windows Servers |
Get a list of installed applications, recent installations/updates, software versions, and identify potentially problematic software.
Performance: SLOW (5-10s with registry method, 30-120s with Win32_Product)
⚠️ IMPORTANT: This skill uses the fast registry-based method (5-10s). NEVER use Get-CimInstance Win32_Product as it triggers MSI consistency checks and takes 30-120 seconds. Only run when user specifically asks about installed software.
$ServerName = "TARGET_SERVER"
# For current user (default): no credential needed
# For explicit credentials: check if $credential variable exists (user must create BEFORE running gh copilot)
$scriptBlock = {
try {
# Method 1: Registry-based (most reliable for traditional apps)
$registryPaths = @(
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
$installedApps = foreach ($path in $registryPaths) {
Get-ItemProperty -Path $path -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName } |
Select-Object DisplayName, DisplayVersion, Publisher, InstallDate,
@{L='InstallLocation';E={$_.InstallLocation}},
@{L='UninstallString';E={$_.UninstallString}},
@{L='EstimatedSizeMB';E={[math]::Round($_.EstimatedSize/1KB,2)}}
}
# Remove duplicates and sort
$uniqueApps = $installedApps |
Sort-Object DisplayName -Unique |
Sort-Object DisplayName
# Parse install dates (format: YYYYMMDD)
$appsWithDates = $uniqueApps | ForEach-Object {
$installDateParsed = $null
if ($_.InstallDate) {
try {
$year = $_.InstallDate.Substring(0,4)
$month = $_.InstallDate.Substring(4,2)
$day = $_.InstallDate.Substring(6,2)
$installDateParsed = Get-Date "$year-$month-$day" -ErrorAction SilentlyContinue
} catch {
# Invalid date format
}
}
$_ | Add-Member -NotePropertyName 'InstallDateParsed' -NotePropertyValue $installDateParsed -PassThru
}
# Recent installations (last 30 days)
$recentApps = $appsWithDates | Where-Object {
$_.InstallDateParsed -and $_.InstallDateParsed -gt (Get-Date).AddDays(-30)
}
[PSCustomObject]@{
TotalApplications = $uniqueApps.Count
AllApplications = $appsWithDates
RecentInstalls = $recentApps
Timestamp = Get-Date
}
} catch {
throw "Application inventory failed: $($_.Exception.Message)"
}
}
try {
$invokeParams = @{
ComputerName = $ServerName
ScriptBlock = $scriptBlock
ErrorAction = 'Stop'
UseSSL = $true
Port = 5986
SessionOption = (New-PSSessionOption -SkipCACheck -SkipCNCheck)
}
if ($credential) {
$invokeParams['Credential'] = $credential
}
Write-Host "`nInventorying installed applications on $ServerName..." -ForegroundColor Cyan
$result = Invoke-Command @invokeParams
Write-Host "`n=== Installed Applications Summary ===" -ForegroundColor Yellow
Write-Host " Total Applications: $($result.TotalApplications)"
Write-Host ""
if ($result.RecentInstalls) {
Write-Host "=== Recently Installed (Last 30 Days) ===" -ForegroundColor Yellow
$result.RecentInstalls |
Select-Object DisplayName, DisplayVersion, Publisher, InstallDateParsed |
Format-Table -AutoSize
} else {
Write-Host "No recent installations detected (last 30 days)" -ForegroundColor Gray
}
Write-Host "`n=== All Installed Applications (Top 50) ===" -ForegroundColor Yellow
$result.AllApplications |
Select-Object DisplayName, DisplayVersion, Publisher -First 50 |
Format-Table -AutoSize
# Return full result
$result
} catch {
Write-Error "Failed to inventory applications on $ServerName : $($_.Exception.Message)"
}
$ServerName = "TARGET_SERVER"
$Credential = $null
$SearchPattern = "*SQL*" # e.g., "*Office*", "*Java*", "*Adobe*"
try {
$result = Invoke-Command -ComputerName $ServerName -Credential $Credential -UseSSL -Port 5986 -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck) -ScriptBlock {
param($pattern)
$registryPaths = @(
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
foreach ($path in $registryPaths) {
Get-ItemProperty -Path $path -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName -like $pattern } |
Select-Object DisplayName, DisplayVersion, Publisher, InstallDate
}
} -ArgumentList $SearchPattern -ErrorAction Stop
if ($result) {
Write-Host "`nApplications matching '$SearchPattern':" -ForegroundColor Cyan
$result | Sort-Object DisplayName -Unique | Format-Table -AutoSize
} else {
Write-Host "`nNo applications found matching '$SearchPattern'" -ForegroundColor Yellow
}
$result
} catch {
Write-Error "Application search failed: $($_.Exception.Message)"
}
$ServerName = "TARGET_SERVER"
# For current user (default): no credential needed
# For explicit credentials: check if $credential variable exists (user must create BEFORE running gh copilot)
$DaysBack = 30
$scriptBlock = {
param($days)
try {
# Get installed hotfixes
$hotfixes = Get-HotFix -ErrorAction Stop
# Filter recent updates
$recentHotfixes = $hotfixes | Where-Object {
$_.InstalledOn -and $_.InstalledOn -gt (Get-Date).AddDays(-$days)
}
[PSCustomObject]@{
TotalHotfixes = $hotfixes.Count
AllHotfixes = $hotfixes | Sort-Object InstalledOn -Descending
RecentHotfixes = $recentHotfixes
}
} catch {
throw "Hotfix enumeration failed: $($_.Exception.Message)"
}
}
try {
$invokeParams = @{
ComputerName = $ServerName
ScriptBlock = $scriptBlock
ArgumentList = @($DaysBack)
ErrorAction = 'Stop'
UseSSL = $true
Port = 5986
SessionOption = (New-PSSessionOption -SkipCACheck -SkipCNCheck)
}
if ($credential) {
$invokeParams['Credential'] = $credential
}
Write-Host "`nRetrieving Windows Updates/Hotfixes from $ServerName..." -ForegroundColor Cyan
$result = Invoke-Command @invokeParams
Write-Host "`n=== Windows Update Summary ===" -ForegroundColor Yellow
Write-Host " Total Hotfixes Installed: $($result.TotalHotfixes)"
Write-Host ""
if ($result.RecentHotfixes) {
Write-Host "=== Recent Updates (Last $DaysBack Days) ===" -ForegroundColor Yellow
$result.RecentHotfixes | Format-Table HotFixID, Description, InstalledOn, InstalledBy -AutoSize
} else {
Write-Host "⚠ No updates installed in last $DaysBack days" -ForegroundColor Yellow
Write-Host " → Server may be missing critical security patches" -ForegroundColor Red
}
Write-Host "`n=== Last 20 Installed Updates ===" -ForegroundColor Yellow
$result.AllHotfixes | Select-Object -First 20 |
Format-Table HotFixID, Description, InstalledOn, InstalledBy -AutoSize
# Return full result
$result
} catch {
Write-Error "Failed to retrieve hotfixes from $ServerName : $($_.Exception.Message)"
}
$ServerName = "TARGET_SERVER"
# For current user (default): no credential needed
# For explicit credentials: check if $credential variable exists (user must create BEFORE running gh copilot)
$scriptBlock = {
try {
# Get installed Windows Features
$features = Get-WindowsFeature -ErrorAction SilentlyContinue | Where-Object { $_.Installed }
# Categorize features
$roles = $features | Where-Object { $_.FeatureType -eq "Role" }
$roleServices = $features | Where-Object { $_.FeatureType -eq "Role Service" }
$featuresList = $features | Where-Object { $_.FeatureType -eq "Feature" }
[PSCustomObject]@{
TotalInstalled = $features.Count
Roles = $roles
RoleServices = $roleServices
Features = $featuresList
AllFeatures = $features
}
} catch {
# Get-WindowsFeature may not be available (non-Server SKU or missing module)
[PSCustomObject]@{
TotalInstalled = 0
Error = "Windows Features enumeration not available on this system"
}
}
}
try {
$invokeParams = @{
ComputerName = $ServerName
ScriptBlock = $scriptBlock
ErrorAction = 'Stop'
UseSSL = $true
Port = 5986
SessionOption = (New-PSSessionOption -SkipCACheck -SkipCNCheck)
}
if ($credential) {
$invokeParams['Credential'] = $credential
}
Write-Host "`nRetrieving Windows Features from $ServerName..." -ForegroundColor Cyan
$result = Invoke-Command @invokeParams
if ($result.Error) {
Write-Host "`n$($result.Error)" -ForegroundColor Yellow
} else {
Write-Host "`n=== Windows Features Summary ===" -ForegroundColor Yellow
Write-Host " Total Installed: $($result.TotalInstalled)"
Write-Host " Roles: $($result.Roles.Count)"
Write-Host " Role Services: $($result.RoleServices.Count)"
Write-Host " Features: $($result.Features.Count)"
Write-Host ""
if ($result.Roles) {
Write-Host "=== Installed Roles ===" -ForegroundColor Yellow
$result.Roles | Format-Table Name, DisplayName -AutoSize
}
if ($result.Features | Select-Object -First 1) {
Write-Host "=== Installed Features (Top 20) ===" -ForegroundColor Yellow
$result.Features | Select-Object -First 20 | Format-Table Name, DisplayName -AutoSize
}
}
$result
} catch {
Write-Error "Failed to retrieve Windows Features: $($_.Exception.Message)"
}
$ServerName = "TARGET_SERVER"
# For current user (default): no credential needed
# For explicit credentials: check if $credential variable exists (user must create BEFORE running gh copilot)
Write-Host "Querying Win32_Product (this may take several minutes)..." -ForegroundColor Yellow
$scriptBlock = {
try {
# Note: Win32_Product is slow and triggers consistency checks
# Use registry method when possible
$products = Get-CimInstance -ClassName Win32_Product -ErrorAction Stop
$products | Select-Object Name, Version, Vendor, InstallDate |
Sort-Object Name
} catch {
throw "Win32_Product query failed: $($_.Exception.Message)"
}
}
try {
$invokeParams = @{
ComputerName = $ServerName
ScriptBlock = $scriptBlock
ErrorAction = 'Stop'
UseSSL = $true
Port = 5986
SessionOption = (New-PSSessionOption -SkipCACheck -SkipCNCheck)
}
if ($credential) {
$invokeParams['Credential'] = $credential
}
$products = Invoke-Command @invokeParams
Write-Host "`nInstalled Products (via Win32_Product):" -ForegroundColor Cyan
Write-Host "Total: $($products.Count)" -ForegroundColor Yellow
$products | Format-Table Name, Version, Vendor -AutoSize
$products
} catch {
Write-Error "Win32_Product query failed: $($_.Exception.Message)"
}
| Finding | Meaning | Action |
|---|---|---|
| Many recent installs | Software updates, new deployments | Normal if expected |
| No recent installs (>90 days) | No patching or updates | Check update process |
| Install after issue started | New software may be cause | Investigate |
Microsoft
Java
Adobe
Antivirus/Security
Monitoring Agents
| Issue | Risk | Action |
|---|---|---|
| Multiple Java versions | Security vulnerability | Remove old versions |
| Flash Player installed | End-of-life software | Uninstall immediately |
| Outdated software | Security/compatibility | Update or remove |
| Unknown publishers | Potential malware | Investigate |
| Duplicate software | Wasted resources | Clean up |
Healthy: Regular monthly updates (Patch Tuesday = 2nd Tuesday of month) Warning: No updates >60 days Critical: No updates >90 days (missing security patches)
Track critical software versions:
# Handle missing registry keys or permissions
$ServerName = "TARGET_SERVER"
try {
$result = Invoke-Command -ComputerName $ServerName -UseSSL -Port 5986 -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck) -ScriptBlock {
$apps = @()
$paths = @(
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
foreach ($path in $paths) {
try {
$apps += Get-ItemProperty -Path $path -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName } |
Select-Object DisplayName, DisplayVersion
} catch {
# Path doesn't exist or access denied; continue
}
}
$apps | Sort-Object DisplayName -Unique
} -ErrorAction Stop
$result | Format-Table -AutoSize
} catch {
Write-Error "Application inventory failed: $($_.Exception.Message)"
}
Win32_Product Class: Querying Win32_Product is SLOW (can take 5-10 minutes) and triggers Windows Installer consistency checks. Use the registry-based method instead unless you specifically need Win32_Product data.
Based on installed applications: