Last active
November 21, 2017 06:35
-
-
Save leinad13/7408a805e545a96fe6dd7d92f484dbfd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
############################################## | |
############## Script Info ################### | |
############################################## | |
## Created By : Dan Cook 2017 ######## | |
############################################## | |
<# | |
Version Info : | |
0.1 - 25/05/2017 - Creates a graphvis diagram and accompanying DOT code file | |
for an SCCM application dependency tree, by querying the | |
primary server WMI | |
0.1.1 - TODO - Make the console output tidier. Use the Write-Color function to add to the console output | |
0.2 - TODO - Script needs to handle more dependencies better, not all the dependencies are "Automatically Installed". Some are either / OR etc... | |
#> | |
#region Variables | |
$query = "QueryString" # Query used as initial search string | |
$server = "ServerName" # SCCM Primary Site Server name (may need to be FQDN) | |
$sitecode = "PS1" # Site code | |
$basepath = "C:\Path\" # Path for output | |
## Output graphvis type | |
## (png, svg, pdf) tested so far | |
$graph_filetype = "png" | |
#endregion | |
function Write-Color([String[]]$Text, [ConsoleColor[]]$Color = "White", [int]$StartTab = 0, [int] $LinesBefore = 0,[int] $LinesAfter = 0) { | |
$DefaultColor = $Color[0] | |
if ($LinesBefore -ne 0) { for ($i = 0; $i -lt $LinesBefore; $i++) { Write-Host "`n" -NoNewline } } # Add empty line before | |
if ($StartTab -ne 0) { for ($i = 0; $i -lt $StartTab; $i++) { Write-Host "`t" -NoNewLine } } # Add TABS before text | |
if ($Color.Count -ge $Text.Count) { | |
for ($i = 0; $i -lt $Text.Length; $i++) { Write-Host $Text[$i] -ForegroundColor $Color[$i] -NoNewLine } | |
} else { | |
for ($i = 0; $i -lt $Color.Length ; $i++) { Write-Host $Text[$i] -ForegroundColor $Color[$i] -NoNewLine } | |
for ($i = $Color.Length; $i -lt $Text.Length; $i++) { Write-Host $Text[$i] -ForegroundColor $DefaultColor -NoNewLine } | |
} | |
Write-Host | |
if ($LinesAfter -ne 0) { for ($i = 0; $i -lt $LinesAfter; $i++) { Write-Host "`n" } } # Add empty line after | |
} | |
function IsNull($objectToCheck) { | |
if ($objectToCheck -eq $null) { | |
return $true | |
} | |
if ($objectToCheck -is [String] -and $objectToCheck -eq [String]::Empty) { | |
return $true | |
} | |
if ($objectToCheck -is [DBNull] -or $objectToCheck -is [System.Management.Automation.Language.NullString]) { | |
return $true | |
} | |
return $false | |
} | |
# Empty Hash table to cache CIID and AppName | |
$CI_NameTable = @{} | |
# Empty array to cache SMS_AppDependenceRelation WMI Class objects | |
$AppDependenceTable = New-Object System.Collections.Generic.List[pscustomobject] | |
# Colours to use for the different levels | |
$levelColors = @{ | |
1 = "deepskyblue2" | |
2 = "brown3" | |
3 = "goldenrod" | |
4 = "deeppink3" | |
5 = "coral3" | |
} | |
# First couple of lines of DOT output syntax | |
$output = @" | |
digraph Dependencies { | |
rankdir = LR | |
node [shape=box] | |
"@ | |
function Resolve-ApplicationName($appCIID) | |
{ | |
# Check if we have already resolved this appCIID | |
$appname = $null | |
$appname = $CI_NameTable.Item($appCIID) | |
if ($appname -eq $null) | |
{ | |
# Name not resolved get name from WMI | |
gwmi -ComputerName $server -Namespace "root\sms\site_$sitecode" -Class SMS_ApplicationLatest -Filter "CI_ID = '$appCIID'" | select LocalizedDisplayName | %{ | |
$CI_NameTable.Add($appCIID, $_.LocalizedDisplayName) | |
return $_.LocalizedDisplayName | |
} | |
} else { | |
# Name already resolved | |
return $appname | |
} | |
} | |
function Get-Dependency($appCIID, $parentName, $depth) | |
{ | |
$apprelation = $null | |
## Check if relation is already resolved and in cache | |
$apprelation = $AppDependenceTable | ?{$_.FromApplicationCIID -eq $appCIID} | |
## Generate Indent based on depth | |
$indent = "" | |
$i=0 | |
for ($i=1;$i -le $depth;$i++) | |
{ | |
$indent = "$indent " | |
} | |
if ($apprelation -eq $null) | |
{ | |
## Relations not found in cache - need to query server WMI | |
$apprelation = gwmi -ComputerName $server -Namespace "root\sms\site_$sitecode" -class SMS_AppDependenceRelation -Filter "FromApplicationCIID='$appCIID'" | |
$apprelation | Add-Member -MemberType NoteProperty -Name WhereFrom -Value WMI | |
} else { | |
#$apprelation | Add-Member -MemberType NoteProperty -Name WhereFrom -Value Cache | |
return | |
} | |
$apprelation | %{ | |
$ToApplicationCIID = $_.ToApplicationCIID | |
$ToApplicationName = Resolve-ApplicationName $ToApplicationCIID | |
$colourName = $null | |
$colourName = $levelColors.Item($depth) | |
if ($colourName -eq $null) | |
{ | |
# deep node - no colour match, use gray | |
$colourName = "gray34" | |
} | |
$nextdepth = $depth+1 | |
## Write Output | |
write-host "$indent -> $($ToApplicationName) (Depth : $depth; Query : $($_.WhereFrom); Parent : $parentName)" | |
## Adding to global output | |
$global:output += "`n`"$parentName`" -> `"$ToApplicationName`"[color=`"$colourName`", label=`"$depth`"]" | |
## Cache relation data | |
$AppDependenceTable.Add([pscustomobject]@{FromApplicationCIID=$_.FromApplicationCIID;ToApplicationCIID=$_.ToApplicationCIID}) | |
## Recurse! | |
Get-Dependency $ToApplicationCIID $ToApplicationName $nextdepth | |
} | |
} | |
# Query WMI for the names of the main app to choose from | |
$appnames = gwmi -ComputerName $server -Namespace "root\sms\site_$sitecode" -query "select LocalizedDisplayName, LocalizedDescription, CI_ID, CI_UniqueID, CIVersion, CIType_ID from SMS_ApplicationLatest where LocalizedDisplayName like '%$query%'" | select-object -Property LocalizedDisplayName, LocalizedDescription, CI_ID, CI_UniqueID, CIVersion, CIType_ID | |
## Show Window of found applications from the query | |
$chosenapp = $appnames | Out-GridView -PassThru -Title "Choose Application" | |
## Quit Script if no app chosen | |
if (IsNull($chosenapp)) { | |
"No App Chosen" | |
exit | |
} | |
"Loading Dependency Tree for $($chosenapp.LocalizedDisplayName)..." | |
## Main Recursing Function | |
get-dependency $chosenapp.CI_ID $chosenapp.LocalizedDisplayName 1 | |
$output += @" | |
} | |
"@ | |
## Generate code filename from the app name | |
$code_filename = $chosenapp.LocalizedDisplayName.replace(' ','_') | |
$code_filepath = "$basepath\$code_filename.gv" | |
## Generate graph filename from the app name | |
$graph_filename = $chosenapp.LocalizedDisplayName.replace(' ','_') | |
$graph_filepath = "$basepath\$graph_filename.$graph_filetype" | |
## Save output as file | |
$output | out-file $code_filepath | |
## Make Output PNG | |
$output | & 'dot.exe' -T $graph_filetype -o $graph_filepath | |
ii $graph_filepath |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment