Ivanti DSM Patch Management optimiert durch PSX

Ivanti DSM Patch Management optimiert durch PSX

Wer mit Ivanti DSM Patch Management betreibt kennt das Problem. Es gibt keine Gesamt-Übersicht über alle Sicherheitslücken. DSM scannt anhand der Jobs alle Computer und erstellt für jeden einzeln eine Übersicht „Sicherheitslücken“.

Anhand der gefundenen Sicherheitslücken weist DSM nun Patch Policy Instanzen der Maschine zu.

Doch woher weiß der Administrator welche Sicherheitslücke im kompletten Netzwerk offen sind? Es kann durchaus ein, das eine Workstation einen Patch installiert bekommen hat, aber auf 500 weiteren Maschinen dieser Patch noch offen ist.

Als Administrator hat man nun die mühselige Aufgabe auf die Patch Policy und diejenigen Computer zu sichten, welche die Policy noch nicht geschlossen haben. Aber was passiert, wenn aus irgendeinem Grund eine Sicherheitslücke gar keine Policy gewiesen bekommt?

Meine Erfahrung: Man bemerkt es erst, wenn das Problem akut wird, und dann muss es schnell gehen.

Ebenso fehlt eine Übersicht welche Sicherheitslücken vor kurzem (Letzte 14 Tage o.ä.) geschlossen wurden.

Meine Lösung - Ivanti DSM Patch Management Reporting PowerShell Script

Genau zu diesen Ivanti DSM Patch Management Problemen habe ich mir ein Powershell Script erstellt, welches ein HTML Report zu folgenden Informationen liefert:

  • Alle Patch Status pro Computer
  • Alle installierten Patche welche letzten Monat installiert wurden
  • Alle Patche welche letztes Jahr installiert wurden
  • Alle installierten Patche
  • Alle offene Patche
  • Letzte Synchronisations-Datum von Computern.

Diese Reports sind durch integriertes JavaScript dynamisch filterbar und wesentlich schneller als in der DSM Console. Ebenso können die Reports an das Management weitergegeben werden, ohne diesen Zugriff auf die DSM Console zu geben.

Wichtig: Damit das Script funktioniert müssen die Powershell Extensions (PSX) von der Firma: NWC-Services installiert und ausreichend Lizensiert sein. Wenn Sie die PSX PowerShell Extensions nicht kennen, werfen Sie doch bitte einen Blick auf die offizielle PSX Website:

PSX PowerShell Extensions

Wie funktioniert das Script?

Das Script ist in 2. Versionen verfügbar:

  • Als Plaintext Powershell Script zum anpassen
  • Als vorkompilierte Exe Datei

Die Verwendung der Scripte ist identisch. Starten Sie das Script oder die Exe von der Kommandozeile aus und übergeben Sie folgende Parameter:

HTMLReport.ps1 –argServer „Servername:Port“ –argUser „Domain\Username“ –argPassword „Passwort“ –context "emdb:\rootDSE\Managed Users & Computers*“

Der angegebene Benutzer benötigt Rechte in der DSM Konsole. Der Context stellt der Organisationspfad dar. Mit diesem Kontext können die auch einzelne OUs aus Ihrer Organisation ansprechen. Tippen Sie dazu einfach den kompletten OU Pfad ein. Z.B.

"emdb:\rootDSE\Managed Users & Computers\2\. Computers\Meine Firma\Production Network\Workstations*"

Das Script

Laden Sie sich datables von JQuery herunter und legen Sie es in den gleichen Pfad wie das Script, ansonsten funktioniert das Sortieren und Filtern von Datensätzen nicht.

#Connection Options
param
(
	[string]$argServer = '',					#localhost:8090
	[string]$argUser = '',						#domain\username
	[string]$argPassword = '',					#Password123
	[string]$loggedInUser = '',					#Optional for Logging
	[string]$context = "emdb:\rootDSE\Managed Users & Computers\2\. Computers\*"
)

$subRoutineFlag = 0; #Default = 0 (Change only for debugging)
$patchPackageContext = "emdb:\rootDSE\Global Software Library\Patch Library\*"

$allFixedPatchPackages = @(); # Array mit allen Sicherheitslücken welche gefixt wurden

$text = "";

function WriteNavigation
{
	$ret = $ret + '<div class="navbar navbar-inverse navbar-fixed-top"><div class="container"><div class="navbar-header"></div><div class="navbar-collapse collapse"><ul class="nav navbar-nav">'
	$ret = $ret + '<li></li>'
	$ret = $ret + '<li><a href="./Report.html">Computer Overview</a></li>'
	$ret = $ret + '<li><a href="./SyncStatus.html">Last Syncs</a></li>'
	$ret = $ret + '<li><a href="./PatchesMonth.html">Patches last Month</a></li>'
	$ret = $ret + '<li><a href="./PatchesYear.html">Patches this Year</a></li>'
	$ret = $ret + '<li><a href="./Patches.html">All installed Patches</a></li>'
	$ret = $ret + '<li><a href="./PatchesPending.html">Open Patches</a></li>'
	$ret = $ret + '<li><a href="#">|</a></li>'
	$ret = $ret + '<li><a href="https://www.marcogriep.de">www.marcogriep.de</a></li>'
	$ret = $ret + '</ul></div></div></div><br/><br/><br/><br/><br/><br/>'

	return $ret;
}

#Prepare PS to Use HEAT DSM
import-module psx7 -DisableNameChecking

#Create global Authentification
$Server = "\\$argServer";
$Username = $argUser;
$global:path = $context
$password = $argPassword | ConvertTo-SecureString -asPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($Username, $password)

Write-Host "Using context: " + $context

#Connect to HEAT DSM
new-psdrive -name emdb -root $Server -scope script -psprovider blsemdb -Credential $credential
emdb:

# ------------------------------------------------------------------------------------------------------------------------------------------
# _____                      _ _           _____                        
#/  ___|                    (_) |         |_   _|                       
#\ `--.  ___  ___ _   _ _ __ _| |_ _   _    | | ___ ___ _   _  ___  ___ 
# `--. \/ _ \/ __| | | | '__| | __| | | |   | |/ __/ __| | | |/ _ \/ __|
#/\__/ /  __/ (__| |_| | |  | | |_| |_| |  _| |\__ \__ \ |_| |  __/\__ \
#\____/ \___|\___|\__,_|_|  |_|\__|\__, |  \___/___/___/\__,_|\___||___/
#                                   __/ |                               
#                                  |___/  
# ------------------------------------------------------------------------------------------------------------------------------------------

if ($subRoutineFlag -eq 0)
{
	#Identify all Computers
	$computers = Get-EmdbComputer $context -Recurse

	#Write HTML Header
	$text = "";
	$text = $text + '<!DOCTYPE html><html><head><title>Report</title><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0">'

	#Link all JavaScript Libraries & CSS Files
	$text = $text + '<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>'
	$text = $text + '<script>'
	$sr = new-object System.io.streamreader(get-item 'datatables.js')
	$text = $text + $sr.ReadToEnd()
	$text = $text + '</script>'
	$text = $text + '<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">'
	$text = $text + '<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>'

	#Create Navigation
	$text = $text + '</head><body>'

	$return = WriteNavigation;
	$text = $text + $return;

	#Create a Container for HTML Body
	$text = $text + '<div class="container body-content">'

	#Write Security Issues for each Machine
	foreach ($machine in $computers)
	{
		Write-Host "Computer: " $machine.Name
		$text = $text + '<div class="entry">'
		$text = $text + '<h2>' + $machine.Name + '</h2>'
		$text = $text + '<h4>Last Sync:' + $machine.LastSyncDate + '</h4>'
		#Get-EmdbComputer $context -Name $machine.Name -Recurse | ForEach-Object { $_.GetAssociations("ComputerMissingPatch") } | Add-EmdbRelatedItem -PassThru | Format-Table @{Label="Patch"; Expression={$_.GetTargetObject().Name}}, @{Label="Status"; Expression={$_.GetPropertyValue("Status", $true, "de")}}, @{Label="Gefunden am"; Expression={$_.DetectDate}}, @{Label="Gefixt am"; Expression={$_.FixDate}} -AutoSize
		$issues = Get-EmdbComputer $context -Name $machine.Name -Recurse | ForEach-Object { $_.GetAssociations("ComputerMissingPatch") } | Add-EmdbRelatedItem -PassThru

		$text = $text + '<table class="table table-striped" border="1"><thead><tr><th>Patch</th><th>Compliance</th><th>Found</th><th>Fixed</th></tr></thead><tbody>'

		foreach ($secIssue in $issues)
		{
			Write-Host $secIssue.GetTargetObject().Name " - " $secIssue.Status
			if ($secIssue.Status -eq "NotFixed")
			{
				$text = $text + '<tr class="danger"><td>' + $secIssue.GetTargetObject().Name + '</td><td>' + $secIssue.Status + '</td><td>' + $secIssue.DetectDate + '</td><td>' + $secIssue.FixDate + '</td></tr>'
			}
			else
			{
				$text = $text + '<tr class="success"><td>' + $secIssue.GetTargetObject().Name + '</td><td>' + $secIssue.Status + '</td><td>' + $secIssue.DetectDate + '</td><td>' + $secIssue.FixDate + '</td></tr>'
			}

			if ($allFixedPatchPackages.Length -gt 0)
			{
				$found = $false
				foreach ($fixedPatch in $allFixedPatchPackages)
				{
					if ($fixedPatch.ID -eq $secIssue.ID)
					{
						$found = $true;
						break;
					}
					if ($fixedPatch.GetTargetObject().Name -eq $secIssue.GetTargetObject().Name)
					{
						$found = $true;
						break;
					}
				}
				if ($found -ne $true)
				{
					$allFixedPatchPackages += $secIssue;
				}
			}
			else
			{
				$allFixedPatchPackages += $secIssue;
			}
		}
		$text = $text + '</tbody></table></div>'
	}

	$text = $text + '<blockquote class="blockquote-reverse"><p>IT Security Audit - Patch Management</p><small>Erstellt von <cite title="Griep Marco">Griep Marco</cite></small></blockquote>'
	$text = $text + '</br></br></body></html>'

	# Render Tables as Sortable Datatable through JQuery Plugin DataTables
	$text = $text + '<script>$(function(){ $("table.table").dataTable(); })</script>';

	# Write Report
	$stream = new-object IO.FileStream "C:\temp\Report.html", 'Create', 'Write', 'Read'
	$sWriter = new-object System.IO.StreamWriter $stream;
	$sWriter.Write($text);
	$sWriter.close()

	$subRoutineFlag = 1;
}

# --------------------------------------------------------------------------------------------------------------------------------------------------
#______     _       _                          _               _    ___  ___            _   _     
#| ___ \   | |     | |                        | |             | |   |  \/  |           | | | |    
#| |_/ /_ _| |_ ___| |__   ___  ___   ______  | |     __ _ ___| |_  | .  . | ___  _ __ | |_| |__  
#|  __/ _` | __/ __| '_ \ / _ \/ __| |______| | |    / _` / __| __| | |\/| |/ _ \| '_ \| __| '_ \ 
#| | | (_| | || (__| | | |  __/\__ \          | |___| (_| \__ \ |_  | |  | | (_) | | | | |_| | | |
#\_|  \__,_|\__\___|_| |_|\___||___/          \_____/\__,_|___/\__| \_|  |_/\___/|_| |_|\__|_| |_|
#                                                                                                                                                                                                    
# --------------------------------------------------------------------------------------------------------------------------------------------------

if ($subRoutineFlag -eq 1)
{
	#Write HTML Header
	$text = "";
	$text = $text + '<!DOCTYPE html><html><head><title>Report</title><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0">'

	#Link all JavaScript Libraries & CSS Files
	$text = $text + '<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>'
	$text = $text + '<script>'
	$sr = new-object System.io.streamreader(get-item 'datatables.js')
	$text = $text + $sr.ReadToEnd()
	$text = $text + '</script>'
	$text = $text + '<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">'
	$text = $text + '<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>'

	#Create Navigation
	$text = $text + '</head><body>'

	$return = WriteNavigation;
	$text = $text + $return;

	#Create a Container for HTML Body
	$text = $text + '<div class="container body-content">'

	$text = $text + '<table class="table table-striped" border="1"><thead><tr><th>ID</th><th>Patch</th><th>Compliance</th><th>Found</th><th>Fixed</th></tr></thead><tbody>'

	$LastMonth = (Get-Date).Month-1
	$ThisYear = (Get-Date).Year

	if ($LastMonth -eq 0) # If Last month was last year
	{
		$LastMonth = 12;
		$ThisYear = $ThisYear - 1;
	}

	#$text = $text + '<tr class="success"><td>' + $secIssue.ID + '</td><td>' + $secIssue.GetTargetObject().Name + '</td><td>' + $secIssue.Status + '</td><td>' + $secIssue.DetectDate + '</td><td>' + $secIssue.FixDate + '</td></tr>'

	foreach ($secIssue in $allFixedPatchPackages)
	{
		if ($secIssue.FixDate.Year -eq $ThisYear)
		{
			if ($secIssue.FixDate.Month -eq $LastMonth)
			{
				$text = $text + '<tr class="success"><td>' + $secIssue.ID + '</td><td>' + $secIssue.GetTargetObject().Name + '</td><td>' + $secIssue.Status + '</td><td>' + $secIssue.DetectDate + '</td><td>' + $secIssue.FixDate + '</td></tr>'
			}
		}
	}

	$text = $text + '</tbody></table></div>'

	$text = $text + '<blockquote class="blockquote-reverse"><p>IT Security Audit - Patch Management</p><small>Erstellt von <cite title="Griep Marco">Griep Marco</cite></small></blockquote>'
	$text = $text + '</br></br></body></html>'

	# Render Tables as Sortable Datatable through JQuery Plugin DataTables
	$text = $text + '<script>$(function(){ $("table.table").dataTable(); })</script>';

	#Write Report
	$stream = new-object IO.FileStream "C:\temp\PatchesMonth.html", 'Create', 'Write', 'Read'
	$sWriter = new-object System.IO.StreamWriter $stream;
	$sWriter.Write($text);
	$sWriter.close()

	$subRoutineFlag = 2;
}

# --------------------------------------------------------------------------------------------------------------------------------------------------
#______     _       _                          _____                           _    __   __              
#| ___ \   | |     | |                        /  __ \                         | |   \ \ / /              
#| |_/ /_ _| |_ ___| |__   ___  ___   ______  | /  \/_   _ _ __ _ __ ___ _ __ | |_   \ V /___  __ _ _ __ 
#|  __/ _` | __/ __| '_ \ / _ \/ __| |______| | |   | | | | '__| '__/ _ \ '_ \| __|   \ // _ \/ _` | '__|
#| | | (_| | || (__| | | |  __/\__ \          | \__/\ |_| | |  | | |  __/ | | | |_    | |  __/ (_| | |   
#\_|  \__,_|\__\___|_| |_|\___||___/           \____/\__,_|_|  |_|  \___|_| |_|\__|   \_/\___|\__,_|_|   
#                                                                                                                                                                                                                                                                                                                                                                                                               
# --------------------------------------------------------------------------------------------------------------------------------------------------

if ($subRoutineFlag -eq 2)
{
	#Write HTML Header
	$text = "";
	$text = $text + '<!DOCTYPE html><html><head><title>Report</title><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0">'

	#Link all JavaScript Libraries & CSS Files
	$text = $text + '<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>'
	$text = $text + '<script>'
	$sr = new-object System.io.streamreader(get-item 'datatables.js')
	$text = $text + $sr.ReadToEnd()
	$text = $text + '</script>'
	$text = $text + '<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">'
	$text = $text + '<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>'

	#Create Navigation
	$text = $text + '</head><body>'
	$return = WriteNavigation;
	$text = $text + $return;

	#Create a Container for HTML Body
	$text = $text + '<div class="container body-content">'

	$text = $text + '<table class="table table-striped" border="1"><thead><tr><th>ID</th><th>Patch</th><th>Compliance</th><th>Found</th><th>Fixed</th></tr></thead><tbody>'

	$LastMonth = (Get-Date).Month - 1
	$ThisYear = (Get-Date).Year

	if ($LastMonth -eq 0) # If Last month was last year
	{
		$LastMonth = 12;
		$ThisYear = $ThisYear - 1;
	}

	#$text = $text + '<tr class="success"><td>' + $secIssue.ID + '</td><td>' + $secIssue.GetTargetObject().Name + '</td><td>' + $secIssue.Status + '</td><td>' + $secIssue.DetectDate + '</td><td>' + $secIssue.FixDate + '</td></tr>'

	foreach ($secIssue in $allFixedPatchPackages)
	{
		if ($secIssue.FixDate.Year -eq $ThisYear)
		{
				$text = $text + '<tr class="success"><td>' + $secIssue.ID + '</td><td>' + $secIssue.GetTargetObject().Name + '</td><td>' + $secIssue.Status + '</td><td>' + $secIssue.DetectDate + '</td><td>' + $secIssue.FixDate + '</td></tr>'
		}
	}

	$text = $text + '</tbody></table></div>'

	$text = $text + '<blockquote class="blockquote-reverse"><p>IT Security Audit - Patch Management</p><small>Erstellt von <cite title="Griep Marco">Griep Marco</cite></small></blockquote>'
	$text = $text + '</br></br></body></html>'

	# Render Tables as Sortable Datatable through JQuery Plugin DataTables
	$text = $text + '<script>$(function(){ $("table.table").dataTable(); })</script>';

	#Write Report
	$stream = new-object IO.FileStream "C:\temp\PatchesYear.html", 'Create', 'Write', 'Read'
	$sWriter = new-object System.IO.StreamWriter $stream;
	$sWriter.Write($text);
	$sWriter.close()

	$subRoutineFlag = 3;
}

# --------------------------------------------------------------------------------------------------------------------------------------------------
#______     _       _                           ___  _ _   _____          _        _ _          _ 
#| ___ \   | |     | |                         / _ \| | | |_   _|        | |      | | |        | |
#| |_/ /_ _| |_ ___| |__   ___  ___   ______  / /_\ \ | |   | | _ __  ___| |_ __ _| | | ___  __| |
#|  __/ _` | __/ __| '_ \ / _ \/ __| |______| |  _  | | |   | || '_ \/ __| __/ _` | | |/ _ \/ _` |
#| | | (_| | || (__| | | |  __/\__ \          | | | | | |  _| || | | \__ \ || (_| | | |  __/ (_| |
#\_|  \__,_|\__\___|_| |_|\___||___/          \_| |_/_|_|  \___/_| |_|___/\__\__,_|_|_|\___|\__,_|
#                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
# --------------------------------------------------------------------------------------------------------------------------------------------------

if ($subRoutineFlag -eq 3)
{
	#Write HTML Header
	$text = "";
	$text = $text + '<!DOCTYPE html><html><head><title>Report</title><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0">'

	#Link all JavaScript Libraries & CSS Files
	$text = $text + '<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>'
	$text = $text + '<script>'
	$sr = new-object System.io.streamreader(get-item 'datatables.js')
	$text = $text + $sr.ReadToEnd()
	$text = $text + '</script>'
	$text = $text + '<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">'
	$text = $text + '<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>'

	#Create Navigation
	$text = $text + '</head><body>'
	$return = WriteNavigation;
	$text = $text + $return;

	#Create a Container for HTML Body
	$text = $text + '<div class="container body-content">'

	$text = $text + '<table class="table table-striped" border="1"><thead><tr><th>ID</th><th>Patch</th><th>Compliance</th><th>Found</th><th>Fixed</th></tr></thead><tbody>'

	#$text = $text + '<tr class="success"><td>' + $secIssue.ID + '</td><td>' + $secIssue.GetTargetObject().Name + '</td><td>' + $secIssue.Status + '</td><td>' + $secIssue.DetectDate + '</td><td>' + $secIssue.FixDate + '</td></tr>'

	foreach ($secIssue in $allFixedPatchPackages)
	{
		if ($secIssue.Status -ne "NotFixed")
		{
			$text = $text + '<tr class="success"><td>' + $secIssue.ID + '</td><td>' + $secIssue.GetTargetObject().Name + '</td><td>' + $secIssue.Status + '</td><td>' + $secIssue.DetectDate + '</td><td>' + $secIssue.FixDate + '</td></tr>'
		}
	}

	$text = $text + '</tbody></table></div>'

	$text = $text + '<blockquote class="blockquote-reverse"><p>IT Security Audit - Patch Management</p><small>Erstellt von <cite title="Griep Marco">Griep Marco</cite></small></blockquote>'
	$text = $text + '</br></br></body></html>'

	# Render Tables as Sortable Datatable through JQuery Plugin DataTables
	$text = $text + '<script>$(function(){ $("table.table").dataTable(); })</script>';

	#Write Report
	$stream = new-object IO.FileStream "C:\temp\Patches.html", 'Create', 'Write', 'Read'
	$sWriter = new-object System.IO.StreamWriter $stream;
	$sWriter.Write($text);
	$sWriter.close()

	$subRoutineFlag = 4;
}

# --------------------------------------------------------------------------------------------------------------------------------------------------
#______              _ _              ______     _       _               
#| ___ \            | (_)             | ___ \   | |     | |              
#| |_/ /__ _ __   __| |_ _ __   __ _  | |_/ /_ _| |_ ___| |__   ___  ___ 
#|  __/ _ \ '_ \ / _` | | '_ \ / _` | |  __/ _` | __/ __| '_ \ / _ \/ __|
#| | |  __/ | | | (_| | | | | | (_| | | | | (_| | || (__| | | |  __/\__ \
#\_|  \___|_| |_|\__,_|_|_| |_|\__, | \_|  \__,_|\__\___|_| |_|\___||___/
#                               __/ |                                    
#                              |___/                                     
# --------------------------------------------------------------------------------------------------------------------------------------------------

if ($subRoutineFlag -eq 4)
{
	#Write HTML Header
	$text = "";
	$text = $text + '<!DOCTYPE html><html><head><title>Report</title><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0">'

	#Link all JavaScript Libraries & CSS Files
	$text = $text + '<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>'
	$text = $text + '<script>'
	$sr = new-object System.io.streamreader(get-item 'datatables.js')
	$text = $text + $sr.ReadToEnd()
	$text = $text + '</script>'
	$text = $text + '<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">'
	$text = $text + '<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>'

	#Create Navigation
	$text = $text + '</head><body>'
	$return = WriteNavigation;
	$text = $text + $return;

	#Create a Container for HTML Body
	$text = $text + '<div class="container body-content">'

	$text = $text + '<table class="table table-striped" border="1"><thead><tr><th>ID</th><th>Patch</th><th>Compliance</th><th>Found</th><th>Fixed</th></tr></thead><tbody>'

	#$text = $text + '<tr class="success"><td>' + $secIssue.ID + '</td><td>' + $secIssue.GetTargetObject().Name + '</td><td>' + $secIssue.Status + '</td><td>' + $secIssue.DetectDate + '</td><td>' + $secIssue.FixDate + '</td></tr>'

	foreach ($secIssue in $allFixedPatchPackages)
	{
		if ($secIssue.Status -eq "NotFixed")
		{
			$text = $text + '<tr class="danger"><td>' + $secIssue.ID + '</td><td>' + $secIssue.GetTargetObject().Name + '</td><td>' + $secIssue.Status + '</td><td>' + $secIssue.DetectDate + '</td><td>' + $secIssue.FixDate + '</td></tr>'
		}
	}

	$text = $text + '</tbody></table></div>'

	$text = $text + '<blockquote class="blockquote-reverse"><p>IT Security Audit - Patch Management</p><small>Erstellt von <cite title="Griep Marco">Griep Marco</cite></small></blockquote>'
	$text = $text + '</br></br></body></html>'

	# Render Tables as Sortable Datatable through JQuery Plugin DataTables
	$text = $text + '<script>$(function(){ $("table.table").dataTable(); })</script>';

	#Write Report
	$stream = new-object IO.FileStream "C:\temp\PatchesPending.html", 'Create', 'Write', 'Read'
	$sWriter = new-object System.IO.StreamWriter $stream;
	$sWriter.Write($text);
	$sWriter.close()

	$subRoutineFlag = 5;
}

# --------------------------------------------------------------------------------------------------------------------------------------------------
# _____                   ______                      _   
#/  ___|                  | ___ \                    | |  
#\ `--. _   _ _ __   ___  | |_/ /___ _ __   ___  _ __| |_ 
# `--. \ | | | '_ \ / __| |    // _ \ '_ \ / _ \| '__| __|
#/\__/ / |_| | | | | (__  | |\ \  __/ |_) | (_) | |  | |_ 
#\____/ \__, |_| |_|\___| \_| \_\___| .__/ \___/|_|   \__|
#        __/ |                      | |                   
#       |___/                       |_|                   
# --------------------------------------------------------------------------------------------------------------------------------------------------

if ($subRoutineFlag -eq 5)
{
	#Identify all Computers
	$computers = Get-EmdbComputer $context -Recurse

	#Write HTML Header
	$text = "";
	$text = $text + '<!DOCTYPE html><html><head><title>Report</title><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0">'

	#Link all JavaScript Libraries & CSS Files
	$text = $text + '<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>'
	$text = $text + '<script>'
	$sr = new-object System.io.streamreader(get-item 'datatables.js')
	$text = $text + $sr.ReadToEnd()
	$text = $text + '</script>'
	$text = $text + '<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">'
	$text = $text + '<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>'

	#Create Navigation
	$text = $text + '</head><body>'

	$return = WriteNavigation;
	$text = $text + $return;

	#Create a Container for HTML Body
	$text = $text + '<div class="container body-content">'

	#Write Security Issues for each Machine
	$text = $text + '<div class="entry">'
	$text = $text + '<h3>Sync Overview</h3>'
	$text = $text + '<table class="table table-striped" border="1"><thead><tr><th>Computer-Name</th><th>Last Sync</th></tr></thead><tbody>'

	foreach ($machine in $computers)
	{
		Write-Host "Computer: " $machine.Name
		$text = $text + '<tr class="info"><td>' + $machine.Name + '</td><td>' + $machine.LastSyncDate + '</td></tr>'
	}

	$text = $text + '</tbody></table></div>'

	$text = $text + '<div class="entry">'
	$text = $text + '<h3>Out of Sync Timeframe</h3>'
	$text = $text + '<table class="table table-striped" border="1"><thead><tr><th>Computer-Name</th><th>Last Sync</th></tr></thead><tbody>'

	$MinMonth = (Get-Date).Month - 3
	$ThisYear = (Get-Date).Year

	if ($MinMonth -lt 1) # Month can not be smaller than januar
	{
		$MinMonth = 1;
	}

	foreach ($machine in $computers)
	{

		if ($machine.LastSyncDate.Year -lt $ThisYear) # If workstation did not sync this year
		{
			$text = $text + '<tr class="danger"><td>' + $machine.Name + '</td><td>' + $machine.LastSyncDate + '</td></tr>'
		}
		else # Workstation syncted this year
		{
			if ($machine.LastSyncDate.Month -lt $MinMonth) # But not in this "acceptable" months
			{
				$text = $text + '<tr class="danger"><td>' + $machine.Name + '</td><td>' + $machine.LastSyncDate + '</td></tr>'
			}
		}
	}

	$text = $text + '</tbody></table></div>'

	$text = $text + '<blockquote class="blockquote-reverse"><p>IT Security Audit - Patch Management</p><small>Erstellt von <cite title="Griep Marco">Griep Marco</cite></small></blockquote>'
	$text = $text + '</br></br></body></html>'

	# Render Tables as Sortable Datatable through JQuery Plugin DataTables
	$text = $text + '<script>$(function(){ $("table.table").dataTable(); })</script>';

	# Write Report
	$stream = new-object IO.FileStream "C:\temp\SyncStatus.html", 'Create', 'Write', 'Read'
	$sWriter = new-object System.IO.StreamWriter $stream;
	$sWriter.Write($text);
	$sWriter.close()

	$subRoutineFlag = 6;
}

Sie benötigen Unterstützung?

Sie benötigen Unterstützung bei einem Ihrer DSM Projekte oder brauchen ein individuelles Powershell Script? Dann schreiben Sie mir doch eine Nachricht über mein Kontaktformular oder besuchen Sie mich auf meiner Website - dort finden Sie diverse weitere Dienstleistungen und Informationen zu Preisen. Wenn Sie diese Themen wie Softwareverteilung und Ivanti DSM interessiert, lassen Sie mir doch ein Newsletter Abo da. Eintragen können Sie sich in der rechten Seitenleiste.

Kennen Sie schon meine DSM Management Suite?

Im Software Bereich meiner Website gibt es meine DSM Management Suite zum download. Diese unterstützt Sie im Patch Management, verbessert das Reporting und zeigt defizite in Ihrer Ivanti DSM Infrastruktur an.