DSCResources/MSFT_xExchJetstressCleanup/MSFT_xExchJetstressCleanup.psm1
function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [parameter(Mandatory = $true)] [System.String] $JetstressPath, [System.String] $ConfigFilePath, [System.String[]] $DatabasePaths, [System.Boolean] $DeleteAssociatedMountPoints, [System.String[]] $LogPaths, [System.String] $OutputSaveLocation, [System.Boolean] $RemoveBinaries ) #Load helper module Import-Module "$((Get-Item -LiteralPath "$($PSScriptRoot)").Parent.Parent.FullName)\Misc\xExchangeCommon.psm1" -Verbose:0 LogFunctionEntry -Parameters @{"JetstressPath" = $JetstressPath} -VerbosePreference $VerbosePreference $returnValue = @{ JetstressPath = $JetstressPath } $returnValue } function Set-TargetResource { [CmdletBinding()] param ( [parameter(Mandatory = $true)] [System.String] $JetstressPath, [System.String] $ConfigFilePath, [System.String[]] $DatabasePaths, [System.Boolean] $DeleteAssociatedMountPoints, [System.String[]] $LogPaths, [System.String] $OutputSaveLocation, [System.Boolean] $RemoveBinaries ) #Load helper module Import-Module "$((Get-Item -LiteralPath "$($PSScriptRoot)").Parent.Parent.FullName)\Misc\xExchangeCommon.psm1" -Verbose:0 Import-Module "$((Get-Item -LiteralPath "$($PSScriptRoot)").Parent.Parent.FullName)\Misc\xExchangeDiskPart.psm1" -Verbose:0 LogFunctionEntry -Parameters @{"JetstressPath" = $JetstressPath} -VerbosePreference $VerbosePreference VerifyParameters @PSBoundParameters $jetstressInstalled = IsJetstressInstalled if ($jetstressInstalled) { throw "Jetstress must be uninstalled before using the xExchJetstressCleanup resource" } #If a config file was specified, pull the database and log paths out and put them into $DatabasePaths and $LogPaths if ($PSBoundParameters.ContainsKey("ConfigFilePath")) { [xml]$configFile = LoadConfigXml -ConfigFilePath "$($ConfigFilePath)" [string[]]$DatabasePaths = $configFile.configuration.ExchangeProfile.EseInstances.EseInstance.DatabasePaths.Path [string[]]$LogPaths = $configFile.configuration.ExchangeProfile.EseInstances.EseInstance.LogPath } [string[]]$FoldersToRemove = $DatabasePaths + $LogPaths #Now delete the specified directories [Hashtable]$ParentFoldersToRemove = @{} #Only used if $DeleteAssociatedMountPoints is $true foreach ($path in $FoldersToRemove) { #Get the parent folder for the specified path $parent = GetParentFolderFromString -Folder "$($path)" if (([string]::IsNullOrEmpty($parent) -eq $false -and $ParentFoldersToRemove.ContainsKey($parent) -eq $false)) { $ParentFoldersToRemove.Add($parent, $null) } RemoveFolder -Path "$($path)" } #Delete associated mount points if requested if ($DeleteAssociatedMountPoints -eq $true -and $ParentFoldersToRemove.Count -gt 0) { GetDiskInfo foreach ($parent in $ParentFoldersToRemove.Keys) { if ((Get-ChildItem -LiteralPath "$($parent)" -ErrorAction SilentlyContinue) -eq $null) { $volNum = MountPointExists -Path "$($parent)" if ($volNum -ge 0) { StartDiskpart -Commands "select volume $($volNum)","remove mount=`"$($parent)`"" -VerbosePreference $VerbosePreference | Out-Null RemoveFolder -Path "$($parent)" } else { Write-Warning "Folder '$($parent)' does not have an associated mount point." } } else { Write-Warning "Folder '$($parent)' still has child items. Skipping removing mount point." } } } #Clean up binaries if requested if ($RemoveBinaries -eq $true -and (Test-Path -LiteralPath "$($JetstressPath)") -eq $true) { #Move output files if requested if ([string]::IsNullOrEmpty($OutputSaveLocation) -eq $false) { if ((Test-Path -LiteralPath "$($OutputSaveLocation)") -eq $false) { mkdir -Path "$($OutputSaveLocation)" } $outputFiles = Get-ChildItem -LiteralPath "$($JetstressPath)" | ` where {$_.Name -like "Performance*" -or $_.Name -like "Stress*" -or $_.Name -like "DBChecksum*" -or $_.Name -like "XmlConfig*" -or $_.Name -like "*.evt" -or $_.Name -like "*.log"} $outputFiles | Move-Item -Destination "$($OutputSaveLocation)" -Confirm:$false -Force } #Now remove the Jetstress folder #If the config file is in the Jetstress directory, remove everything but the config file, or else running Test-TargetResource after removing the directory will fail if ((GetFolderNoTrailingSlash -Folder "$($JetstressPath)") -like (GetParentFolderFromString -Folder "$($ConfigFilePath)")) { Get-ChildItem -LiteralPath "$($JetstressPath)" | where {$_.FullName -notlike "$($ConfigFilePath)"} | Remove-Item -Recurse -Confirm:$false -Force } else #No config file in this directory. Remove the whole thing { RemoveFolder -Path "$($JetstressPath)" } } #Test if we successfully cleaned up Jetstress. If so, flag or initiate a reboot $cleanedUp = Test-TargetResource @PSBoundParameters if ($cleanedUp -eq $true) { Write-Verbose "Jetstress was successfully cleaned up. A reboot must occur to finish the cleanup." $global:DSCMachineStatus = 1 } } function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [parameter(Mandatory = $true)] [System.String] $JetstressPath, [System.String] $ConfigFilePath, [System.String[]] $DatabasePaths, [System.Boolean] $DeleteAssociatedMountPoints, [System.String[]] $LogPaths, [System.String] $OutputSaveLocation, [System.Boolean] $RemoveBinaries ) #Load helper module Import-Module "$((Get-Item -LiteralPath "$($PSScriptRoot)").Parent.Parent.FullName)\Misc\xExchangeCommon.psm1" -Verbose:0 Import-Module "$((Get-Item -LiteralPath "$($PSScriptRoot)").Parent.Parent.FullName)\Misc\xExchangeDiskPart.psm1" -Verbose:0 LogFunctionEntry -Parameters @{"JetstressPath" = $JetstressPath} -VerbosePreference $VerbosePreference VerifyParameters @PSBoundParameters $jetstressInstalled = IsJetstressInstalled if ($jetstressInstalled) { Write-Verbose "Jetstress is still installed" return $false } else { #If a config file was specified, pull the database and log paths out and put them into $DatabasePaths and $LogPaths if ($PSBoundParameters.ContainsKey("ConfigFilePath")) { [xml]$configFile = LoadConfigXml -ConfigFilePath "$($ConfigFilePath)" [string[]]$FoldersToRemove = $configFile.configuration.ExchangeProfile.EseInstances.EseInstance.DatabasePaths.Path + $configFile.configuration.ExchangeProfile.EseInstances.EseInstance.LogPath } else { [string[]]$FoldersToRemove = $DatabasePaths + $LogPaths } #First make sure DB and log folders were cleaned up GetDiskInfo foreach ($folder in $FoldersToRemove) { #If DeleteAssociatedMountPoints was requested, make sure the parent folder doesn't have a mount point if ($DeleteAssociatedMountPoints -eq $true) { $parent = GetParentFolderFromString -Folder "$($folder)" if ((MountPointExists -Path "$($parent)") -ge 0) { Write-Verbose "Folder '$($parent)' still has a mount point associated with it." return $false } } #Now check the folder itself if ((Test-Path -LiteralPath "$($folder)") -eq $true) { Write-Verbose "Folder '$($folder)' still exists." return $false } } #Now check for binaries if ($RemoveBinaries -eq $true -and (Test-Path -LiteralPath "$($JetstressPath)") -eq $true) { if ((GetFolderNoTrailingSlash -Folder "$($JetstressPath)") -like (GetParentFolderFromString -Folder "$($ConfigFilePath)")) { $items = Get-ChildItem -LiteralPath "$($JetstressPath)" | where {$_.FullName -notlike "$($ConfigFilePath)"} if ($items -ne $null -or $items.Count -gt 0) { Write-Verbose "Folder '$($JetstressPath)' still exists and contains items that are not the config file." return $false } } else { Write-Verbose "Folder '$($JetstressPath)' still exists." return $false } } } Write-Verbose "Jetstress has been successfully cleaned up." return $true } #Verifies that parameters for Jetstress were passed in correctly. Throws an exception if not. function VerifyParameters { [CmdletBinding()] param ( [parameter(Mandatory = $true)] [System.String] $JetstressPath, [System.String] $ConfigFilePath, [System.String[]] $DatabasePaths, [System.Boolean] $DeleteAssociatedMountPoints, [System.String[]] $LogPaths, [System.String] $OutputSaveLocation, [System.Boolean] $RemoveBinaries ) if ($PSBoundParameters.ContainsKey("ConfigFilePath") -eq $false -and ($PSBoundParameters.ContainsKey("DatabasePaths") -eq $false -or $PSBoundParameters.ContainsKey("LogPaths") -eq $false)) { throw "Either the ConfigFilePath parameter must be specified, or DatabasePaths and LogPaths must be specified." } if ($PSBoundParameters.ContainsKey("ConfigFilePath") -eq $true) { if ([string]::IsNullOrEmpty($ConfigFilePath) -or ((Test-Path -LiteralPath "$($ConfigFilePath)") -eq $false)) { throw "The path specified for ConfigFilePath, '$($ConfigFilePath)', is either invalid or inaccessible" } } else { if ($DatabasePaths -eq $null -or $DatabasePaths.Count -eq 0) { throw "No paths were specified in the DatabasePaths parameter" } if ($LogPaths -eq $null -or $LogPaths.Count -eq 0) { throw "No paths were specified in the LogPaths parameter" } } } #Get a string for a folder without the trailing slash function GetFolderNoTrailingSlash { param([string]$Folder) if ($Folder.EndsWith('\')) { $Folder = $Folder.Substring(0, $Folder.Length - 1) } return $Folder } #Simple string parsing method to determine what the parent folder of a folder is given the child folder's path function GetParentFolderFromString { param([string]$Folder) $Folder = GetFolderNoTrailingSlash -Folder "$($Folder)" $parent = $Folder.Substring(0, $Folder.LastIndexOf('\')) return $parent } #Removes the specified folder, if it exists, and all subdirectories function RemoveFolder { [CmdletBinding()] param([string]$Path) if ((Test-Path -LiteralPath "$($Path)") -eq $true) { Write-Verbose "Attempting to remove folder '$($Path)' and all subfolders" Remove-Item -LiteralPath "$($Path)" -Recurse -Confirm:$false -Force } else { Write-Verbose "Folder '$($Path)' does not exist. Skipping." } } #Loads the specified JetstressConfig.xml file and puts it into an [xml] variable function LoadConfigXml { [CmdletBinding()] [OutputType([Xml])] param ( [parameter(Mandatory = $true)] [System.String] $ConfigFilePath ) [xml]$configFile = Get-Content -LiteralPath "$($ConfigFilePath)" if ($configFile -ne $null) { [string[]]$DatabasePaths = $configFile.configuration.ExchangeProfile.EseInstances.EseInstance.DatabasePaths.Path [string[]]$LogPaths = $configFile.configuration.ExchangeProfile.EseInstances.EseInstance.LogPath if ($DatabasePaths -eq $null -or $DatabasePaths.Count -eq 0) { throw "Failed to read any database paths out of config file '$($ConfigFilePath)'" } elseif ($LogPaths -eq $null -or $LogPaths.Count -eq 0) { throw "Failed to read any log paths out of config file '$($ConfigFilePath)'" } } else { throw "Failed to read config file at '$($ConfigFilePath)'" } return $configFile } #Checks whether Jetstress is installed by looking for Jetstress 2013's Product GUID function IsJetstressInstalled { return ((Get-WmiObject -Class Win32_Product -Filter "IdentifyingNumber = '{75189587-0D84-4404-8F02-79C39728FA64}'") -ne $null) } Export-ModuleMember -Function *-TargetResource |