I’ve recently come to really appreciate PowerShell as the most versatile and powerful language available natively in any/all Windows OS.
i.e. 2 lines of code to ensure laptop battery is plugged in and prompt the user.
If ((Get-WmiObject -Query “SELECT * From Win32_Battery”).BatteryStatus -ne 2)
{ Write-Host “Please plug in your laptop” }
An equivalent VBScript would easily run around 30 lines of code.
Running PowerShell via the Task Sequence has been around for a bit, which is simple enough.
This however, does NOT include any logging support and requires that your scripts exist in the “Scripts” Directory. Which in my opinion almost renders the feature useless.
Here is my Template, which can be used within an application:
GitHub Link: https://github.com/brianfgonzalez/Scripts/blob/master/Z-MyMDTTemplate.ps1
<# Purpose: Replaces Windows 8/8.1 Start Screen Background image. Version: 1.0 - March 14, 2013 Author - Brian Gonzalez Blog : https://supportishere.com ChangeLog: 2014-03-17 BFG: - Updated LogHelper output strings to match MDT logging strings. - set LogHelper to also write to BDD.log. #> # Create Global Variables $oInvocation = (Get-Variable MyInvocation).Value $sScriptDirectory = Split-Path $oInvocation.MyCommand.Path $sScriptNameExt = $oInvocation.MyCommand.Name $sScriptName = $sScriptNameExt.Substring(0, $sScriptNameExt.Length - 4) # Ensure script can attach to TS Environment try { $ErrorActionPreference = "Stop" $oTSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment $sLogPath = $oTSEnv.Value("LogPath") } catch [System.Runtime.InteropServices.COMException] { $sLogPath = "C:\Windows\Temp\DeploymentLogs" #Hardcode Logs Directory } finally {$ErrorActionPreference = "Continue"} # Set Logging File Variables $sLogFilePath = "$sLogPath\$sScriptName.log" $sCmdLogFilePath = "$sLogPath\$sScriptName-Cmd.log" $sBDDLogFilePath = "$sLogPath\BDD.log" $sTZbias = (Get-WmiObject -Query “Select Bias from Win32_TimeZone”).Bias $sTime = ((Get-Date -Format hh:mm:ss.fff) + $sTZbias) $sDate = Get-Date -Format MM-dd-yyyy # Function to run processes and capture output function RunWithLogging { param([string]$cmd, [string]$params) $ps = new-object System.Diagnostics.Process $ps.StartInfo.Filename = $cmd $ps.StartInfo.Arguments = $params $ps.StartInfo.RedirectStandardOutput = $True $ps.StartInfo.UseShellExecute = $false $ps.start() | Out-Null $ps.WaitForExit() [string] $out = $ps.StandardOutput.ReadToEnd(); $out | Out-File $sCmdLogFilePath -Append return $ps.ExitCode } function CopyFileWithLogging { param([string]$sSrcFile, [string]$sDesFile) If (!(Test-Path $sSrcFile)) {LogHelper "$sSrcFile not found." "Error"} try { $ErrorActionPreference = "stop" Copy-Item $sSrcFile $sDesFile -Force -Recurse } catch [System.Management.Automation.ItemNotFoundException] { LogHelper ("Could not find target location: " + $sDesFile) "Error" return 1 } catch [System.UnauthorizedAccessException] { LogHelper ("Access Error: Could not overwrite target file: " + $sDesFile) "Error" return 2 } finally {$ErrorActionPreference = "continue"} If (Test-Path $sDesFile) { LogHelper ($sDesFile + " was copied successfully.") return 0 } } function LogHelper { param([string] $sLogContent, [string] $sLogType = "Update") try { $ErrorActionPreference = "stop" If (!(Test-Path $sLogFilePath)) { # Create Log File $sUpdateStringTest = ("<![LOG[Initial LOG Entry]LOG]!><time=""" + $sTime + """ date=""" + ` $sDate + """ component=""" + $sScriptName + """ context="""" type=""1"" thread="""" file=""" + ` $sScriptName + """>") } } catch [System.IO.DirectoryNotFoundException] { Write-Host (Split-Path $sLogFilePath),"folder does not exist" New-Item -ItemType Directory (Split-Path $sLogFilePath) | Out-Null $sUpdateStringTest = ("<![LOG[Created Logging Directory.]LOG]!><time=""" + $sTime + """ date=""" + ` $sDate + """ component=""" + $sScriptName + """ context="""" type=""1"" thread="""" file=""" + ` $sScriptName + """>") $sUpdateStringTest | Out-File $sLogFilePath -Append -NoClobber -Encoding Default } finally {$ErrorActionPreference = "continue"} If ($sLogType -eq "Update") { # Write to log named after script file. $sUpdateStringTest = ("<![LOG[" + $sLogContent + "]LOG]!><time=""" + $sTime + """ date=""" + ` $sDate + """ component=""" + $sScriptName + """ context="""" type=""1"" thread="""" file=""" + ` $sScriptName + """>") $sUpdateStringTest | Out-File $sLogFilePath -Append -NoClobber -Encoding Default # Write also to BDD.log If (Test-Path $sBDDLogFilePath) {$sUpdateStringTest | Out-File $sBDDLogFilePath -Append -NoClobber -Encoding Default} } ElseIf (($sLogType -eq "Error") -or ($sLogType -eq "Failure")) { # Write to log named after script file. <![LOG[FAILURE (Err): $sUpdateStringTest = ("<![LOG[FAILURE (Err):" + $sLogContent + "]LOG]!><time=""" + $sTime + """ date=""" + ` $sDate + """ component=""" + $sScriptName + """ context="""" type=""1"" thread="""" file=""" + ` $sScriptName + """>") $sUpdateStringTest | Out-File $sLogFilePath -Append -NoClobber -Encoding Default # Write also to BDD.log If (Test-Path $sBDDLogFilePath) {$sUpdateStringTest | Out-File $sBDDLogFilePath -Append -NoClobber -Encoding Default} } } # Start Main Code Here # Examples # Run a Command Line #LogHelper "About to run a command." #$sCmd = "$env:SystemDrive\Windows\System32\cmd.exe" #$sParams = "/C IPCONFIG /ALL" #$sReturn = RunWithLogging $sCmd $sParams #LogHelper "Command Completed and Returned: $sReturn" # Copy a File/Folder #$sSourceFilePath = ($sScriptDirectory + "\SourceFile.txt") #$sDestinationFilePath = "C:\Temp" #LogHelper ("About to copy """ + $sSourceFilePath + """ to " + $sDestinationFilePath) #$sReturn = CopyFileWithLogging $sSourceFilePath $sDestinationFilePath #LogHelper ("Copy is complete and returned: $sReturn")
/BG
Hi Brian,
I like this script, but I am having some issues trying to test it out. Admitiddly, I am testing this outside of an MDT task sequence, so the script cannot attach to the TS Environment.
I am using this script to install an application, and it does that just fine. However, I get errors when trying to write to the log files, because they don’t exist. I can’t see anywhere in the script where it creates these log files if they don’t exist. All I see are commands to update log files, not create them.
Am I missing something? Thank you!
David,
Im happy you found the post. I actually ended up creating a separate script to act like the ZTIUtility.wsf script, but via Powershell.
Here is the script, must be placed in the Scripts\ Directory.
https://github.com/brianfgonzalez/Scripts/blob/master/ZTIUtility.ps1
And here is an example Powershell script using the functions in the other script.
https://github.com/brianfgonzalez/Scripts/blob/master/ZTIUtility.ps1
it seems to work great in my testing.
/BrianG
Hi Brian,
I keep finding myself back at this page as I need to write my own PowerShell scripts for MDT. I noticed in your reply above that the example you linked to is the same URL as the ZTIUtility.ps1 link.
Could you please post to a working example that uses the ZTIUtility script? That would be most helpful, at least to me. 🙂
Thanks.
David,
Sure, here is one I used to apply a custom local group policy to a specific user account.
https://github.com/brianfgonzalez/Scripts/blob/master/Z-CopyLGPO-Content.ps1