Skip to content

Instantly share code, notes, and snippets.

@leinad13
Last active November 21, 2017 06:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save leinad13/7408a805e545a96fe6dd7d92f484dbfd to your computer and use it in GitHub Desktop.
Save leinad13/7408a805e545a96fe6dd7d92f484dbfd to your computer and use it in GitHub Desktop.
##############################################
############## 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