#
Stand-alone implementation of One Data Collector
#>
# Copyright © 2016, Microsoft Corporation. All rights reserved.
# :: ======================================================= ::
<#
-DESCRIPTION
Utils_OneDataCollector.ps1 Contains common functionalities used across the One Data Collector diagnostics.
-FUNCTIONS
Pop-Message
Create-ZipFromDirectory
Update-Progress
Export-Report
Get-OSVersion
Get-ValidPath
Get-ValidFileName
Get-NewFileNameIfExists
Initialize
Collect-Files
Collect-RegistryKeys
Collect-EventLogs
Collect-Commands
Get-Packages
Get-UserSelectedPackages
Process-Package
Compress-CollectedDataAndReport
Get-PackageFromUserAndProcess
Write-Log
#>
#region Fields
$Global:ResultRootDirectory = [System.IO.Path]::Combine(($env:TEMP), 'CollectedData')
$fileTime = Get-Date ([datetime]::UtcNow) -UFormat "%m_%d_%Y_%H_%m_UTC%Z"
$CompressedResultFileName = "$($env:COMPUTERNAME)_CollectedData_$fileTime.ZIP"
[System.Nullable[bool]] $newZipperAvailable = $null # Stores flag whether [System.IO.Compression.ZipFile] can be used.
$global:LogName = "$env:systemroot\temp\stdout.log"
$ODCversion = "2024.10.2"
#endregion
#==================================================================================
# Functions
#==================================================================================
function Write-Log {
<#
.SYNOPSIS
Script-wide logging function
.DESCRIPTION
Writes debug logging statements to script log file
.EXAMPLE
Write-Log "Entering function"
Write log entry with information level
.EXAMPLE
Write-Log -Level Error -WriteStdOut "Error"
Write error to log and also show in PowerShell output
.NOTES
NAME: Write-Log
Set $global:LogName at the beginning of the script
#>
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[string]$Message = "",
[Parameter()]
[ValidateNotNullOrEmpty()]
[ValidateSet('Information', 'Warning', 'Error', 'Verbose')]
[string]$Level = 'Information',
[Parameter()]
[switch]$WriteStdOut,
[Parameter()]
# create log in format
[string]$LogName = $global:LogName
)
BEGIN {
if ( ($null -eq $LogName) -or ($LogName -eq "")) { Write-Error "Please set variable `$global`:LogName." }
}
PROCESS {
# only log verbose if flag is set
if ( ($Level -eq "Verbose") -and ( -not ($debugMode) ) ) {
# don't log events unless flag is set
} else {
[pscustomobject]@{
Time = (Get-Date -f u)
Line = "`[$($MyInvocation.ScriptLineNumber)`]"
Level = $Level
Message = $Message
} | Export-Csv -Path $LogName -Append -Force -NoTypeInformation -Encoding Unicode
if ( $WriteStdOut -or ( ($Level -eq "Verbose") -and $debugMode)) { Write-Output $Message }
}
}
END {}
}
#region Functions
function RunCommand {
Param ([string]$cmdToRun)
$line = "`r`n" + "=" * 80
# [\$\(]* ignore leading $() syntax
# (.+?) first word in the command. Check to see it exists
# ([\s+$].*|$) cmd line arguments or (for|$) single word command
$regsearches = $cmdToRun -match "[\$\(]*(.+?)([\s+$].*|$)" # to populate $matches
if (Get-Command $matches[1] -ErrorAction SilentlyContinue ) {
Write-Output "`r`n$line`r`n`t`t $cmdToRun $line`r`n"
try { Invoke-Expression($cmdToRun) -ErrorAction Stop }
catch { $error[0].ToString()}
}
else {
$matches
Write-Output "Command $cmdToRun not found"
}
}
# get human-readable reg binary ascii output given hex input
function Convert-RegBinaryToString {
param([char[]]$binaryArray = @())
$characters = $binaryArray | ForEach-Object { [char][byte]($_)}
$characters -join ""
}
# Print registry keys in human-readable format, including binary info
function Get-PrintableRegKeyValues {
param($regKey)
$line = "=" * 80
# support regkey formats other thank HKLM: HKCU: syntax
if ($regKey -match "^(HK.+?[^:])\\.+" ) {
# replace HKEY_LOCAL_MACHINE with HKLM:
if ($Matches[1] -in $(Get-PSDrive).Root) {
$regKey = $regKey -replace $Matches[1], ( $(Get-PSDrive | Where-Object {$_.Root -eq $Matches[1]} ).Name + ":")
}
# replace HKLM with HKLM:
else {
$regKey = $regKey -replace $Matches[1], "$($Matches[1]):"
}
}
$regKeys = @()
$regKeys += $(Get-Item $regKey)
$regKeys += Get-ChildItem $regKey
# add current key to the list
foreach ($regKey in $regKeys) {
Write-Output "$($regKey.Name)`r`n$line`r`n"
foreach ($value in $regKey.GetValueNames() ) {
$RegValueAndData = "$value = "
if ($regKey.GetValueKind($value) -eq "Binary"){
$RegValueAndData += Convert-RegBinaryToString $regKey.GetValue($value)
}
else {
$RegValueAndData += $regKey.GetValue($value)
}
$RegValueAndData
}
Write-Output "`r`n"
}
}
function Pop-Message
{
<#
.DESCRIPTION
Displays a message box with given details
.PARAMETER Message
The message you want to display.
.PARAMETER Title
The title of the message box.
.PARAMETER Type
The type of the message box as defined below. Default is 64.
- 16: (0x10) Show "Stop Mark" icon.
- 32: (0x20) Show "Question Mark" icon.
- 48: (0x30) Show "Exclamation Mark" icon.
- 64: (0x40) Show "Information Mark" icon.
.EXAMPLE
Pop-Message -Message 'Your Message' -Caption 'Your Caption' -Type 48
.EXAMPLE
Pop-Message -M 'Your Message' -C 'Your Caption' -T 32
#>
[CmdletBinding()]
PARAM
(
[Alias('M')]
[Parameter(Position = 1, Mandatory = $true)]
[string] $Message ="Message",
[Alias('C')]
[Parameter(Position = 2, Mandatory = $false)]
[string] $Caption = "Title",
[Alias('T')]
[Parameter(Position = 3, Mandatory = $false)]
[int] $Type = 64
)
PROCESS
{
$popWindow = New-Object -ComObject wscript.shell
$popWindow.Popup($Message, 0, $Caption, $Type) | Out-Null
Remove-Variable popWindow
}
}
function Create-ZipFromDirectory
{
<#
.DESCRIPTION
Creates a ZIP file from a given the directory.
.PARAMETER SourceDirectory
The folder with the files you intend to zip.
.PARAMETER ZipFileName
The zip file that you intend to create
.PARAMETER IncludeParentDirectory
Setting this option will include the parent directory.
.PARAMETER Overwrite
Setting this option will overwrite the zip file if already exits.
.EXAMPLE
Create-ZipFromDirectory -Source $ResultRootDirectory -ZipFileName $CompressedResultFileName -IncludeParentDirectory -Overwrite
.EXAMPLE
Create-ZipFromDirectory -S $ResultRootDirectory -O $CompressedResultFileName -Rooted -Force
#>
PARAM
(
[Alias('S')]
[Parameter(Position = 1, Mandatory = $true)]
[ValidateScript({Test-Path -Path $_})]
[string]$SourceDirectory,
[Alias('O')]
[parameter(Position = 2, Mandatory = $false)]
[string]$ZipFileName,
[Alias('Rooted')]
[Parameter(Mandatory = $false)]
[switch]$IncludeParentDirectory,
[Alias('Force')]
[Parameter(Mandatory = $false)]
[switch]$Overwrite
)
PROCESS
{
$ZipFileName = (("{0}.zip" -f $ZipFileName), $ZipFileName)[$ZipFileName.EndsWith('.zip', [System.StringComparison]::OrdinalIgnoreCase)]
if(![System.IO.Path]::IsPathRooted($ZipFileName))
{
$ZipFileName = ("{0}\{1}" -f (Get-Location), $ZipFileName)
}
if($Overwrite)
{
if(Test-Path($ZipFileName)){ Remove-Item $ZipFileName -Force -ErrorAction SilentlyContinue }
}
$source = Get-Item $SourceDirectory
if ($source.PSIsContainer)
{
if($newZipperAvailable -eq $null)
{
try
{
$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.IO.Compression.FileSystem
$newZipperAvailable = $true
}
catch
{
$newZipperAvailable = $false
}
}
if($newZipperAvailable -eq $true) # More efficent and works silently.
{
try {
[System.IO.Compression.ZipFile]::CreateFromDirectory($source.FullName, $ZipFileName, [System.IO.Compression.CompressionLevel]::Optimal, $IncludeParentDirectory)
}
catch {
$timeElapsed = 0
while (Get-Process -Name msinfo32 -ErrorAction SilentlyContinue) {
sleep 20
$timeElapsed += 20
"Waiting for msinfo32 to exit. Please wait. This may take 2-3 minutes. Elapsed time: $timeElapsed seconds."
}
[System.IO.Compression.ZipFile]::CreateFromDirectory($source.FullName, $ZipFileName, [System.IO.Compression.CompressionLevel]::Optimal, $IncludeParentDirectory)
}
}
else # Will show progress dialog.
{
# Preparing zip if not available.
if(-not(Test-Path($ZipFileName)))
{
Set-Content $ZipFileName (“PK” + [char]5 + [char]6 + (“$([char]0)” * 18))
(dir $ZipFileName).IsReadOnly = $false
}
if(-not $IncludeParentDirectory)
{
$source = Get-ChildItem $SourceDirectory
}
$zipPackage = (New-Object -ComObject Shell.Application).NameSpace($ZipFileName)
[System.Int32]$NoProgressDialog = 16 #Tried but not effective.
foreach($file in $source)
{
$zipPackage.CopyHere($file.FullName, $NoProgressDialog)
do
{
Start-Sleep -Milliseconds 256
}
while ($zipPackage.Items().count -eq 0) # Waiting for an operation to complete.
}
}
}
else
{
Write-Error 'The directory name is invalid.'
}
}
}
function Update-Progress
{
<#
.DESCRIPTION
Updates the diagnostics progress for the items being collected.
.PARAMETER Activity
Activity which is being in progress. Possible Values are Files, RegistryKeys, EventLogs, and Commands.
.PARAMETER PackageID
The ID of a package which is being processed.
.PARAMETER Filename
The name of a file which is being collected.
.EXAMPLE
Update-Progress -Activity 'FILES' -PackageID 'Apps' -Filename 'SomeFile.log'
#>
[CmdletBinding()]
PARAM
(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateScript({-not([string]::IsNullOrEmpty($_)) -or ($_.Trim().Length -le 0)})]
[string] $Activity,
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateScript({-not([string]::IsNullOrEmpty($_)) -or ($_.Trim().Length -le 0)})]
[string] $PackageID,
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateScript({-not([string]::IsNullOrEmpty($_)) -or ($_.Trim().Length -le 0)})]
[string] $Filename
)
PROCESS
{
Write-DiagProgress -Activity ("{0}: {1}" -f $PackageID, ($Utils_OneDataCollector_Strings.ProgressActivity_DataCollectionGenericMessage)) -Status ("{0}: {1}" -f $Activity, ([System.IO.Path]::GetFileName($Filename)))
}
}
function Export-Report
{
<#
.DESCRIPTION
Exports the given object as XML and (if specified) CSV.
.PARAMETER InputObject
The object which needs to be exported.
.PARAMETER Name
The name of the report
.PARAMETER ExportDirectory
The directory where the report has to be exported.
.PARAMETER ExportAsCSV
Setting this option will export the input object as CSV along with XML.
.PARAMETER Overwrite
Setting this option will overwrite the report files if already exits.
.EXAMPLE
Export-Report -InputObject $files -Name 'Files Collected' -ExportDirectory $packageDirectory -PackageID 'Apps' -ExportAsCSV -Overwrite
.EXAMPLE
Export-Report -I $files -N 'Files Collected' -D $packageDirectory -P 'Apps' -CSV -Force
#>
[CmdletBinding()]
PARAM
(
[Alias('I')]
[ValidateScript({$_ -ne $null})]
[Parameter(Position = 1, Mandatory = $true)]
[object] $InputObject,
[Alias('N')]
[parameter(Position = 2, Mandatory = $true)]
[ValidateScript({-not([string]::IsNullOrEmpty($_)) -or ($_.Trim().Length -le 0)})]
[string] $Name,
[Alias('D')]
[parameter(Position = 3, Mandatory = $true)]
[ValidateScript({-not([string]::IsNullOrEmpty($_)) -or ($_.Trim().Length -le 0)})]
[string] $ExportDirectory,
[Alias('P')]
[Parameter(Position = 4, Mandatory = $true)]
[ValidateScript({-not([string]::IsNullOrEmpty($_)) -or ($_.Trim().Length -le 0)})]
[string] $PackageID,
[Alias('CSV')]
[Parameter(Mandatory = $false)]
[switch]$ExportAsCSV,
[Alias('Force')]
[Parameter(Mandatory = $false)]
[switch]$Overwrite
)
PROCESS
{
if($InputObject)
{
$Name = [System.IO.Path]::GetFileNameWithoutExtension($Name)
$ReportFilename = ([System.IO.Path]::Combine($ExportDirectory,"Files\General", ("{0}_{1}.XML" -f $PackageID, $Name)))
if ( -not (Test-Path "$ExportDirectory\Files\General") ) { mkdir "$ExportDirectory\Files\General" -Force}
if($Overwrite)
{
if(Test-Path($ReportFilename)){ Remove-Item $ReportFilename -Force -ErrorAction SilentlyContinue }
}
else
{
$ReportFilename = $ReportFilename | Get-NewFileNameIfExists
}
($InputObject | ConvertTo-XML).Save($ReportFilename)
if($ExportAsCSV)
{
$ReportFilename = ([System.IO.Path]::Combine($ExportDirectory,"Files\General", ("{0}_{1}.CSV" -f $PackageID, $Name)))
if($Overwrite)
{
if(Test-Path($ReportFilename)){ Remove-Item $ReportFilename -Force -ErrorAction SilentlyContinue }
}
else
{
$ReportFilename = $ReportFilename | Get-NewFileNameIfExists
}
$InputObject | Export-Csv -Path $ReportFilename -Force -NoTypeInformation
}
}
}
}
function Get-ValidPath
{
[CmdletBinding()]
PARAM
(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateScript({-not([string]::IsNullOrEmpty($_)) -or ($_.Trim().Length -le 0)})]
[string] $Path
)
PROCESS
{
foreach( $invalidChar in ([System.IO.Path]::GetInvalidPathChars()))
{
$Path = $Path.Replace($invalidChar, '#');
}
return $Path
}
}
function Get-OSVersion
{
param (
[alias('s')]
[Parameter(Mandatory = $false)]
[switch]$short,
[alias('l')]
[Parameter(Mandatory = $false)]
[switch]$long
)
$osVersion = [System.Environment]::OSVersion.Version
if ($osVersion.Major -ne 10) {
Write-Output "Unknown Windows version"
return $null
}
elseif ($short) {
if ($osVersion.Build -gt 19045) { return "11" }
else { return "10" }
}
elseif ($long){
return "$($osVersion.Major).$($osVersion.Minor).$($osVersion.Build)"
}
else {
return ("{0}.{1}" -f $osVersion.Major, $osVersion.Minor)
}
}
function Get-ValidFileName
{
[CmdletBinding()]
PARAM
(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateScript({-not([string]::IsNullOrEmpty($_)) -or ($_.Trim().Length -le 0)})]
[string] $FileName
)
PROCESS
{
foreach( $invalidChar in ([System.IO.Path]::GetInvalidFileNameChars()))
{
$FileName = $FileName.Replace($invalidChar, '_');
}
return $FileName
}
}
function Get-NewFileNameIfExists
{
[CmdletBinding()]
PARAM
(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateScript({-not([string]::IsNullOrEmpty($_)) -or ($_.Trim().Length -le 0)})]
[string] $FileName
)
PROCESS
{
if(Test-Path($FileName))
{
$FileNameBackup = $FileName
[int] $duplicateFilenameCounter = 0;
while(Test-Path($FileName))
{
$duplicateFilenameCounter += 1
$FileName = $FileNameBackup
if($FileName.LastIndexOf('.') -ne -1) #Files
{
$FileName = $FileName.Insert($FileName.LastIndexOf('.'), (" ({0})" -f $duplicateFilenameCounter))
}
else #Directory/File without extension
{
$FileName = "$FileName ({0})" -f $duplicateFilenameCounter
}
}
}
else
{
New-Item -ItemType Directory -Force -Path (Split-Path $FileName) | Out-Null
}
return $FileName
}
}
#region Initializing Types
function Initialize
{
[string]$sourceCode = @"
namespace Microsoft.One.DataCollector
{
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
///
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:Microsoft.One.DataCollector")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "urn:Microsoft.One.DataCollector", IsNullable = false)]
public partial class DataPoints
{
private DataPointsPackage[] packageField;
///
[System.Xml.Serialization.XmlElementAttribute("Package")]
public DataPointsPackage[] Package
{
get
{
return this.packageField;
}
set
{
this.packageField = value;
}
}
}
///
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:Microsoft.One.DataCollector")]
public partial class DataPointsPackage
{
private DataPointsPackageCommands commandsField;
private DataPointsPackageEventLogs eventLogsField;
private DataPointsPackageFiles filesField;
private DataPointsPackageRegistries registriesField;
private string idField;
///
public DataPointsPackageCommands Commands
{
get
{
return this.commandsField;
}
set
{
this.commandsField = value;
}
}
///
public DataPointsPackageEventLogs EventLogs
{
get
{
return this.eventLogsField;
}
set
{
this.eventLogsField = value;
}
}
///
public DataPointsPackageFiles Files
{
get
{
return this.filesField;
}
set
{
this.filesField = value;
}
}
///
public DataPointsPackageRegistries Registries
{
get
{
return this.registriesField;
}
set
{
this.registriesField = value;
}
}
///
[System.Xml.Serialization.XmlAttributeAttribute()]
public string ID
{
get
{
return this.idField;
}
set
{
this.idField = value;
}
}
}
///
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:Microsoft.One.DataCollector")]
public partial class DataPointsPackageCommands
{
private DataPointsPackageCommandsCommand[] commandField;
private string[] textField;
///
[System.Xml.Serialization.XmlElementAttribute("Command")]
public DataPointsPackageCommandsCommand[] Command
{
get
{
return this.commandField;
}
set
{
this.commandField = value;
}
}
///
[System.Xml.Serialization.XmlTextAttribute()]
public string[] Text
{
get
{
return this.textField;
}
set
{
this.textField = value;
}
}
}
///
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:Microsoft.One.DataCollector")]
public partial class DataPointsPackageCommandsCommand
{
private string typeField;
private string teamField;
private string outputFileNameField;
private string valueField;
///
[System.Xml.Serialization.XmlAttributeAttribute()]
public string Type
{
get
{
return this.typeField;
}
set
{
this.typeField = value;
}
}
///
[System.Xml.Serialization.XmlAttributeAttribute()]
public string Team
{
get
{
return this.teamField;
}
set
{
this.teamField = value;
}
}
///
[System.Xml.Serialization.XmlAttributeAttribute()]
public string OutputFileName
{
get
{
return this.outputFileNameField;
}
set
{
this.outputFileNameField = value;
}
}
///
[System.Xml.Serialization.XmlTextAttribute()]
public string Value
{
get
{
return this.valueField;
}
set
{
this.valueField = value;
}
}
}
///
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:Microsoft.One.DataCollector")]
public partial class DataPointsPackageEventLogs
{
private DataPointsPackageEventLogsEventLog[] eventLogField;
private string[] textField;
///
[System.Xml.Serialization.XmlElementAttribute("EventLog")]
public DataPointsPackageEventLogsEventLog[] EventLog
{
get
{
return this.eventLogField;
}
set
{
this.eventLogField = value;
}
}
///
[System.Xml.Serialization.XmlTextAttribute()]
public string[] Text
{
get
{
return this.textField;
}
set
{
this.textField = value;
}
}
}
///
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:Microsoft.One.DataCollector")]
public partial class DataPointsPackageEventLogsEventLog
{
private string teamField;
private string valueField;
///
[System.Xml.Serialization.XmlAttributeAttribute()]
public string Team
{
get
{
return this.teamField;
}
set
{
this.teamField = value;
}
}
///
[System.Xml.Serialization.XmlTextAttribute()]
public string Value
{
get
{
return this.valueField;
}
set
{
this.valueField = value;
}
}
}
///
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:Microsoft.One.DataCollector")]
public partial class DataPointsPackageFiles
{
private DataPointsPackageFilesFile[] fileField;
private string[] textField;
///
[System.Xml.Serialization.XmlElementAttribute("File")]
public DataPointsPackageFilesFile[] File
{
get
{
return this.fileField;
}
set
{
this.fileField = value;
}
}
///
[System.Xml.Serialization.XmlTextAttribute()]
public string[] Text
{
get
{
return this.textField;
}
set
{
this.textField = value;
}
}
}
///
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:Microsoft.One.DataCollector")]
public partial class DataPointsPackageFilesFile
{
private string teamField;
private string valueField;
///
[System.Xml.Serialization.XmlAttributeAttribute()]
public string Team
{
get
{
return this.teamField;
}
set
{
this.teamField = value;
}
}
///
[System.Xml.Serialization.XmlTextAttribute()]
public string Value
{
get
{
return this.valueField;
}
set
{
this.valueField = value;
}
}
}
///
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:Microsoft.One.DataCollector")]
public partial class DataPointsPackageRegistries
{
private DataPointsPackageRegistriesRegistry[] registryField;
private string[] textField;
///
[System.Xml.Serialization.XmlElementAttribute("Registry")]
public DataPointsPackageRegistriesRegistry[] Registry
{
get
{
return this.registryField;
}
set
{
this.registryField = value;
}
}
///
[System.Xml.Serialization.XmlTextAttribute()]
public string[] Text
{
get
{
return this.textField;
}
set
{
this.textField = value;
}
}
}
///
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:Microsoft.One.DataCollector")]
public partial class DataPointsPackageRegistriesRegistry
{
private string teamField;
private string outputFileNameField;
private string valueField;
///
[System.Xml.Serialization.XmlAttributeAttribute()]
public string Team
{
get
{
return this.teamField;
}
set
{
this.teamField = value;
}
}
///
[System.Xml.Serialization.XmlAttributeAttribute()]
public string OutputFileName
{
get
{
return this.outputFileNameField;
}
set
{
this.outputFileNameField = value;
}
}
///
[System.Xml.Serialization.XmlTextAttribute()]
public string Value
{
get
{
return this.valueField;
}
set
{
this.valueField = value;
}
}
}
///
/// Contains common functionalities used across the troubleshooter.
///
public static class Utilities
{
///
/// Gets the data points from file.
///
/// The filename.
/// if set to true [show error].
///
public static DataPoints GetDataPointsFromFile(string filename, out bool isValid, out string errorMessage)
{
isValid = false;
errorMessage = String.Empty;
try
{
if (!File.Exists(filename))
{
return null;
}
using (TextReader reader = new StreamReader(filename))
{
XmlSerializer serializer = new XmlSerializer(typeof(DataPoints));
var dataPoints = (DataPoints)serializer.Deserialize(reader);
isValid = true;
return dataPoints;
}
}
catch (InvalidOperationException ex)
{
isValid = false;
errorMessage = String.Format("{0}{1}", ex.Message, (ex.InnerException != null ? String.Format("{0}{1}", Environment.NewLine, ex.InnerException.Message) : String.Empty));
return null;
}
catch (Exception ex)
{
isValid = false;
errorMessage = String.Format("{0}{1}", ex.Message, (ex.InnerException != null ? String.Format("{0}{1}", Environment.NewLine, ex.InnerException.Message) : String.Empty));
return null;
}
}
///
/// Gets the data points from XML String.
///
/// XML string source.
/// if set to true [show error].
///
public static DataPoints GetDataPointsFromString(string xmlSource, out bool isValid, out string errorMessage)
{
isValid = false;
errorMessage = String.Empty;
try
{
StringReader strReader = new StringReader(xmlSource);
XmlTextReader xmlread;
xmlread = new XmlTextReader(strReader);
XmlSerializer serializer = new XmlSerializer(typeof(DataPoints));
var dataPoints = (DataPoints)serializer.Deserialize(xmlread);
isValid = true;
return dataPoints;
}
catch (InvalidOperationException ex)
{
isValid = false;
errorMessage = String.Format("{0}{1}", ex.Message, (ex.InnerException != null ? String.Format("{0}{1}", Environment.NewLine, ex.InnerException.Message) : String.Empty));
return null;
}
catch (Exception ex)
{
isValid = false;
errorMessage = String.Format("{0}{1}", ex.Message, (ex.InnerException != null ? String.Format("{0}{1}", Environment.NewLine, ex.InnerException.Message) : String.Empty));
return null;
}
}
}
}
"@
if([float](Get-OSVersion) -le [float](6.1))
{
try
{
$type = Add-Type -TypeDefinition $sourceCode -PassThru -Language CSharpVersion3 -ReferencedAssemblies System.Xml -ErrorAction Continue
}
catch
{
$_.Exception.Message | ConvertTo-Xml | Update-DiagReport -ID 'TS_Main' -Name 'Type Initialization Error' -Verbosity Informational
}
}
else
{
try
{
$type = Add-Type -TypeDefinition $sourceCode -PassThru -ReferencedAssemblies System.Xml
}
catch
{
$_.Exception.Message | Out-File odc_debug.log -Force -Append
$_ | Out-File odc_debug.log -Force -Append
}
}
}
Initialize # Initializing the types so below functionalities can be used.
#endregion
function Collect-Files
{
[CmdletBinding()]
PARAM
(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Microsoft.One.DataCollector.DataPointsPackageFilesFile[]] $Files,
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[string] $PackageID
)
PROCESS
{
$filesCollected = @();
$filesNotCollected = @();
$currentActivity = 'Files'
$packageDirectory = [System.IO.Path]::Combine($ResultRootDirectory, $PackageID)
New-Item -ItemType Directory -Path $packageDirectory -Force | Out-Null
foreach($file in $Files)
{
$filePath = $file.Value
$sourceFilename = [System.Environment]::ExpandEnvironmentVariables($filePath)
$sourceFilename = $sourceFilename -replace '"', ""
if(Test-Path($sourceFilename))
{
try
{
$ErrorActionPreference = 'Stop'
$resolvedFiles = Get-ChildItem -Path $sourceFilename
foreach($resolvedFile in $resolvedFiles)
{
$teamName = ($file.Team, 'General')[([string]::IsNullOrEmpty($file.Team)) -or ($file.Team.Trim().Length -le 0)];
$dstFileName = $env:COMPUTERNAME + "_" + ($resolvedFile.Name)
$destinationFilename = (@($packageDirectory, $currentActivity, $teamName, $dstFileName) -join '\')| Get-ValidPath | Get-NewFileNameIfExists
if ($resolvedFile.Length -ne 0) {
Copy-Item -Path ($resolvedFile.FullName) -Destination $destinationFilename -Force
}
else {
Write-Log -Message "Skipping zero-byte file $($resolvedFile.FullName)" -Level Warning -WriteStdOut
}
}
}
catch
{
Add-Member -InputObject $file -MemberType NoteProperty -Name Status -Value ($error[0].ToString()) -Force
$filesNotCollected += $file;
}
Add-Member -InputObject $file -MemberType NoteProperty -Name Status -Value 'Collected' -Force
$filesCollected += $file;
}
else
{
Add-Member -InputObject $file -MemberType NoteProperty -Name Status -Value 'The system cannot find the file specified.' -Force
$filesNotCollected += $file;
}
}
# Exporting list of files collected
if($filesCollected)
{
Export-Report -InputObject $filesCollected -Name 'FilesCollected' -ExportDirectory $packageDirectory -PackageID $PackageID -CSV -Force
}
# Exporting list of files NOT collected
if($filesNotCollected)
{
Export-Report -InputObject $filesNotCollected -Name 'FilesNotCollected' -ExportDirectory $packageDirectory -PackageID $PackageID -CSV -Force
}
}
}
function Collect-RegistryKeys
{
[CmdletBinding()]
PARAM
(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Microsoft.One.DataCollector.DataPointsPackageRegistriesRegistry[]] $RegistryKeys,
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[string] $PackageID
)
PROCESS
{
$registryKeysCollected = @();
$registryKeysNotCollected = @();
$currentActivity = 'RegistryKeys'
$packageDirectory = [System.IO.Path]::Combine($ResultRootDirectory, $PackageID)
New-Item -ItemType Directory -Path $packageDirectory -Force | Out-Null
foreach($registryKey in $RegistryKeys)
{
$registryKeyToExport = $registryKey.Value.Replace('\*', '')
$teamName = ($registryKey.Team, 'General')[([string]::IsNullOrEmpty($registryKey.Team)) -or ($registryKey.Team.Trim().Length -le 0)];
$outputFilename = ($registryKey.OutputFileName, ($registryKeyToExport | Get-ValidFileName))[([string]::IsNullOrEmpty($registryKey.OutputFileName)) -or ($registryKey.OutputFileName.Trim().Length -le 0)];
$outputFilename = $env:COMPUTERNAME + "_" + $outputFilename
$outputFilename = (@($ResultRootDirectory, $PackageID, $currentActivity, $teamName, ("{0}.txt" -f [System.IO.Path]::GetFileNameWithoutExtension($outputFilename))) -join '\') | Get-ValidPath | Get-NewFileNameIfExists
$registryKey.OutputFileName = [System.IO.Path]::GetFileName($outputFilename)
try
{
Update-Progress -Activity $currentActivity -PackageID $PackageID -Filename $outputFilename
$ErrorActionPreference = 'Stop'
[string] $result = REG.exe EXPORT ($registryKeyToExport) ($outputFilename) /y /reg:64 2>&1
Add-Member -InputObject $registryKey -MemberType NoteProperty -Name Status -Value $result -Force
$registryKeysCollected += $registryKey
}
catch [Exception]
{
Add-Member -InputObject $registryKey -MemberType NoteProperty -Name Status -Value ($error[0].ToString()) -Force
$registryKeysNotCollected += $registryKey
}
}
# Exporting list of registry keys collected
if($registryKeysCollected)
{
Export-Report -InputObject $registryKeysCollected -Name 'RegistryKeysCollected' -ExportDirectory $packageDirectory -PackageID $PackageID -CSV -Force
}
# Exporting list of registry keys NOT collected
if($registryKeysNotCollected)
{
Export-Report -InputObject $registryKeysNotCollected -Name 'RegistryKeysNotCollected' -ExportDirectory $packageDirectory -PackageID $PackageID -CSV -Force
}
}
}
function Collect-EventLogs
{
[CmdletBinding()]
PARAM
(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Microsoft.One.DataCollector.DataPointsPackageEventLogsEventLog[]] $EventLogs,
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[string] $PackageID
)
PROCESS
{
$eventLogsCollected = @();
$eventLogsNotCollected = @();
$currentActivity = 'EventLogs'
$packageDirectory = [System.IO.Path]::Combine($ResultRootDirectory, $PackageID)
New-Item -ItemType Directory -Path $packageDirectory -Force | Out-Null
foreach($eventLog in $EventLogs)
{
$sourceEventLog = [System.Environment]::ExpandEnvironmentVariables($eventLog.Value);
if(Test-Path($sourceEventLog))
{
try
{
$ErrorActionPreference = 'Stop'
$resolvedEventLogs = Get-ChildItem -Path $sourceEventLog
foreach($resolvedEventLog in $resolvedEventLogs)
{
$teamName = ($eventLog.Team, 'General')[([string]::IsNullOrEmpty($eventLog.Team)) -or ($eventLog.Team.Trim().Length -le 0)];
$dstEventLog = $env:COMPUTERNAME + "_" + ($resolvedEventLog.Name)
$destinationEventLog = (@($packageDirectory, $currentActivity, $teamName, $dstEventLog ) -join '\') | Get-ValidPath | Get-NewFileNameIfExists
Update-Progress -Activity $currentActivity -PackageID $PackageID -Filename ($destinationEventLog)
Copy-Item -Path ($resolvedEventLog.FullName) -Destination $destinationEventLog -Force -ErrorAction SilentlyContinue | Out-Null
}
}
catch
{
Add-Member -InputObject $eventLog -MemberType NoteProperty -Name Status -Value ($error[0].ToString()) -Force
$eventLogsNotCollected += $eventLog;
}
Add-Member -InputObject $eventLog -MemberType NoteProperty -Name Status -Value 'Collected' -Force
$eventLogsCollected += $eventLog;
}
else
{
Add-Member -InputObject $eventLog -MemberType NoteProperty -Name Status -Value 'The system cannot find the specified event log.' -Force
$eventLogsNotCollected += $eventLog;
}
}
# Exporting list of event logs collected
if($eventLogsCollected)
{
Export-Report -InputObject $eventLogsCollected -Name 'EventLogsCollected' -ExportDirectory $packageDirectory -PackageID $PackageID -CSV -Force
}
# Exporting list of event logs NOT collected
if($eventLogsNotCollected)
{
Export-Report -InputObject $eventLogsNotCollected -Name 'EventLogsNotCollected' -ExportDirectory $packageDirectory -PackageID $PackageID -CSV -Force
}
}
}
function Collect-Commands
{
[CmdletBinding()]
PARAM
(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Microsoft.One.DataCollector.DataPointsPackageCommandsCommand[]] $Commands,
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[string] $PackageID
)
PROCESS
{
$commandsCollected = @();
$commandsNotCollected = @();
$currentActivity = 'Commands'
$packageDirectory = [System.IO.Path]::Combine($ResultRootDirectory, $PackageID)
New-Item -ItemType Directory -Path $packageDirectory -Force | Out-Null
foreach($command in $Commands)
{
$teamName = ($command.Team, 'General')[([string]::IsNullOrEmpty($command.Team)) -or ($command.Team.Trim().Length -le 0)];
$fileName = $command.OutputFileName
if(!($fileName -eq "NA"))
{
$fileName = "$env:COMPUTERNAME" + "_" + $filename
$outputFilename = ($fileName, ([System.IO.Path]::GetRandomFileName()))[([string]::IsNullOrEmpty($fileName)) -or ($fileName.Trim().Length -le 0)];
$outputFilename = (@($ResultRootDirectory, $PackageID, $currentActivity, $teamName, ("{0}.txt" -f [System.IO.Path]::GetFileNameWithoutExtension($outputFilename))) -join '\') | Get-ValidPath | Get-NewFileNameIfExists
$command.OutputFileName = [System.IO.Path]::GetFileName($outputFilename)
Update-Progress -Activity $currentActivity -PackageID $PackageID -Filename $outputFilename
}
else
{
$outputFilename = $null
}
if($command.Type)
{
switch($command.Type.ToUpperInvariant())
{
"PS"
{
try
{
$ErrorActionPreference = 'Stop'
if($outputFilename)
{
(Invoke-Expression ($command.Value)) > $outputFilename
}
else
{
(Invoke-Expression ($command.Value))
}
Add-Member -InputObject $command -MemberType NoteProperty -Name Status -Value 'Collected' -Force
$commandsCollected += $command
}
catch [Exception]
{
Add-Member -InputObject $command -MemberType NoteProperty -Name Status -Value ($error[0].ToString()) -Force
$commandsNotCollected += $command
}
break
}
"CMD"
{
try
{
$ErrorActionPreference = 'Stop'
(CMD.exe /c ($command.Value) 2>&1) > $outputFilename
Add-Member -InputObject $command -MemberType NoteProperty -Name Status -Value 'Collected' -Force
$commandsCollected += $command
}
catch [Exception]
{
Add-Member -InputObject $command -MemberType NoteProperty -Name Status -Value ($error[0].ToString()) -Force
$commandsNotCollected += $command
}
break
}
}
}
else
{
Add-Member -InputObject $command -MemberType NoteProperty -Name Status -Value 'Invalid Command Type'
$commandsNotCollected += $command
}
}
# Exporting list of commands collected
if($commandsCollected)
{
Export-Report -InputObject $commandsCollected -Name 'CommandsCollected' -ExportDirectory $packageDirectory -PackageID $PackageID -CSV -Force
}
# Exporting list of commands NOT collected
if($commandsNotCollected)
{
Export-Report -InputObject $commandsNotCollected -Name 'CommandsNotCollected' -ExportDirectory $packageDirectory -PackageID $PackageID -CSV -Force
}
}
}
function Get-Packages
{
[CmdletBinding()]
PARAM
(
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[string] $Filename
)
PROCESS
{
$packages = "" | Select ValidPackages, InvalidPackages
$packages.ValidPackages = @();
$packages.InvalidPackages = @();
if (([string]::IsNullOrEmpty($Filename)) -or ($Filename.Trim().Length -le 0))
{
Pop-Message -Message "Blank file name" -Caption "Blank file name" -Type 64
return $packages
}
if (-not (Test-Path($Filename)))
{
Pop-Message -Message "Missing file. Unable to find `'$Filename`'" -Caption "Missing file" -Type 48
return $packages
}
# Getting XML files from Package
$packageFilenames = @()
if ($Filename.EndsWith('.CAB', [System.StringComparison]::OrdinalIgnoreCase))
{
# Extracting all files in the CAB
$unpackedPackagePath = 'UnpackedPackage'
if(Test-Path($unpackedPackagePath))
{
Remove-Item $unpackedPackagePath -Force -Recurse -ErrorAction SilentlyContinue
}
New-Item $unpackedPackagePath -Type Directory -Force | Out-Null
$expandFiles = Expand.exe -r $filename -F:* $unpackedPackagePath
# Moving attachments to the root so commands can use it.
$attachments = Get-ChildItem $unpackedPackagePath | ? {$_.Extension -inotlike '.xml'}
if($attachments)
{
$attachments | % { Move-Item -Path ($_.FullName) -Destination . -Force}
}
# Processing packages
$packageFiles = Get-ChildItem $unpackedPackagePath | ? {$_.Extension -ilike '.xml'}
foreach($packageFile in $packageFiles)
{
$packageFilenames += $packageFile.FullName
}
}
elseif ($Filename.EndsWith('.XML', [System.StringComparison]::OrdinalIgnoreCase))
{
$packageFilenames += $Filename
}
else
{
Pop-Message -Message "Invalid package type" -Caption "Invalid package type" -Type 48
return $packages
}
foreach($file in $packageFilenames)
{
# Getting data points from XML (package) files
[bool] $isValid = $false;
[string] $errorMessage = '';
$dataPoints = [Microsoft.One.DataCollector.Utilities]::GetDataPointsFromFile($file, [ref] $isValid, [ref] $errorMessage)
if($isValid)
{
foreach($package in ($dataPoints.Package))
{
if($package -ne $null)
{
$packageID = $package.ID
$strLength = $packageID.length
if($strLength -gt 0)
{
$packages.ValidPackages += $package
}
else
{
Pop-Message -Message "Problem with package contents." -Caption "Import error" -Type 64
}
}
}
}
else
{
$invalidPackage = "" | Select FileName, ErrorMessage
$invalidPackage.FileName = [System.IO.Path]::GetFileName($file)
$invalidPackage.ErrorMessage = $errorMessage
$packages.InvalidPackages += $invalidPackage
}
}
return $packages
}
}
function Process-Package
{
[CmdletBinding()]
PARAM
(
[Alias('P')]
[ValidateScript({$_ -ne $null})]
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Microsoft.One.DataCollector.DataPointsPackage] $Package
)
PROCESS
{
if(($Package.Files) -ne $null -and ($Package.Files.File) -ne $null)
{
Collect-Files -Files ($Package.Files.File) -PackageID ($Package.ID)
}
if(($Package.Commands) -ne $null -and ($Package.Commands.Command) -ne $null)
{
Collect-Commands -Commands ($Package.Commands.Command) -PackageID ($Package.ID)
}
if(($Package.Registries) -ne $null -and ($Package.Registries.Registry) -ne $null)
{
Collect-RegistryKeys -RegistryKeys ($Package.Registries.Registry) -PackageID ($Package.ID)
}
if(($Package.EventLogs) -ne $null -and ($Package.EventLogs.EventLog) -ne $null)
{
Collect-EventLogs -EventLogs ($Package.EventLogs.EventLog) -PackageID ($Package.ID)
}
}
}
function Compress-CollectedDataAndReport
{
if(Test-Path($ResultRootDirectory))
{
Write-DiagProgress -Activity "Compressing zip file" -status $CompressedResultFileName
$ErrorActionPreference = "Stop"
# Give long-running processes a chance to exit
try {
Create-ZipFromDirectory -Source $ResultRootDirectory -ZipFileName $CompressedResultFileName -Force
Copy-Item -Path $ResultRootDirectory -Destination (get-location) -Force -Recurse -ErrorAction SilentlyContinue
}
catch {
sleep 15
Create-ZipFromDirectory -Source $ResultRootDirectory -ZipFileName $CompressedResultFileName -Force
Copy-Item -Path $ResultRootDirectory -Destination (get-location) -Force -Recurse -ErrorAction SilentlyContinue
}
finally {
Remove-Item -Path $ResultRootDirectory -Force -Recurse -ErrorAction SilentlyContinue
}
}
Remove-Item -Path $pwd\CollectedData -Force -Recurse -ErrorAction SilentlyContinue
}
#endregion
function Write-DiagProgress {
Param (
$activity,
$status
)
Write-Log -Message "$activity ::: $status" -WriteStdOut
}
Initialize # Initializing the types so below functionalities can be used.
Function Test-IsAdmin
{
([Security.Principal.WindowsPrincipal] `
[Security.Principal.WindowsIdentity]::GetCurrent() `
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
#Region stand-alone
Initialize # Initializing the types so below functionalities can be used.
if (-not (Test-IsAdmin) ) {
Return "Please run PowerShell elevated (run as administrator) and run the script again."
Break
}
$ResultRootDirectory = [System.IO.Path]::Combine(($env:TEMP), 'CollectedData')
# clean up any folders from old runs
if (Test-Path $ResultRootDirectory) {
Write-Log -Message "Removing $ResultRootDirectory" -Level Information
$x = Remove-Item -Recurse -Path $ResultRootDirectory -Force
}
Write-DiagProgress -status "Starting Intune ODC ver. $ODCversion"
$xmlPath = join-path (get-location) 'Intune.XML'
Write-DiagProgress -status "XML path is $xmlPath"
# we assume that the XML and .ps1 are in the same folder
Write-DiagProgress -status "Working folder is $(get-location). PWD is $pwd"
if (-not (Test-Path $xmlPath) ) {
try {
$downloadLocation = "https://raw.githubusercontent.com/markstan/IntuneOneDataCollector/master/Intune.xml"
if ( $null = [System.Net.WebProxy]::GetDefaultProxy().Address ) {
Invoke-WebRequest -UseBasicParsing -Uri $downLoadLocation -OutFile .\Intune.XML
}
else {
$myproxy = ([System.Net.WebProxy]::GetDefaultProxy().Address.AbsoluteURI)
Invoke-WebRequest -UseBasicParsing -Uri $downLoadLocation -OutFile .\Intune.XML -Proxy $myproxy
}
}
catch {
$message = "Unable to download Intune.XML. Exiting"
Write-Output $message
Write-Log -Message $message
Pop-Message -Caption "Unable to download Intune.xml" -message "Unable to download Intune.XML. Please download from http://aka.ms/IntuneXML and run the script again" -Type 48
}
}
# verify that XML was downloaded correctly.
if ( ( get-content .\Intune.xml -TotalCount 1) -ne '' ){
Write-DiagProgress -status "`r`n`r`n`t******`r`n`t`t Warning: Intune.xml does not contain valid header. Please verified that you have the correct file.`r`n`t****** "
}
Write-DiagProgress -status "Loading $xmlPath"
$package = get-packages -Filename $xmlPath
Write-DiagProgress -status "Loading $($package.ValidPackages[0].ID)"
Process-Package -Package $package.ValidPackages[0]
Compress-CollectedDataAndReport
if (Test-Path -Path . -Filter ".*CollectedData.*.zip") {
Remove-Item -Path .\CollectedData -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -Path .\*.evtx -Force -ErrorAction SilentlyContinue
}
Start-Process .