Pipe all Write-Output to the same Out-File in PowerShell - powershell

As the title suggests, how do you make it so all of the Write-Outputs - no matter where they appear - automatically append to your defined log file? That way the script will be nicer to read and it removes a tiny bit of work!
Little example below, id like to see none of the "| Out-File" if possible, yet have them still output to that file!
$Author = 'Max'
$Time = Get-Date -Format "HH:mm:ss.fff"
$Title = "Illegal Software Removal"
$LogName = "Illegal_Remove_$($env:COMPUTERNAME).log"
$Log = "C:\Windows\Logs\Software" + "\" + $LogName
$RemoteLog = "\\Server\Adobe Illegal Software Removal"
Set-PSBreakpoint -Variable Time -Mode Read -Action { $global:Time = Get-Date -format "HH:mm:ss.fff" } | Out-Null
If((Test-Path $Log) -eq $False){ New-Item $Log -ItemType "File" -Force | Out-Null }
Else { $Null }
"[$Time][Startup] $Title : Created by $Author" | Out-File $Log -Append
"[$Time][Startup] Configuring initial variables required before run..." | Out-File $Log -Append
EDIT: This needs to work on PS v2.0, I don't want the output to appear on screen at all only in the log. So I have the same functionality, but the script would look like so...
"[$Time][Startup] $Title : Created by $Author"
"[$Time][Startup] Configuring initial variables required before run..."

You have two options, one is to do the redirection at the point the script is invoked e.g.:
PowerShell.exe -Command "& {c:\myscript.ps1}" > c:\myscript.log
Or you can use the Start-Transcript command to record everything (except exe output) the shell sees. After the script is done call Stop-Transcript.

Related

xcopy show only when files have been copied

I have made a script using xcopy that generates a csv log file with the date to check that my copies are done.
I would like to be able to display only when it has copied files, and not display anything when there are 0 files copied.
How can I do this?
if I didn't make myself clear let me know
Thanks :)
Here is my code:
$Logfile = "C:\Users\Name\Documents\Power\"
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $logstring and
}
function Get-TimeStamp
{
return "[{0:dd/MM/yy} {0:HH:mm:ss}]" -f (Get-Date)
}
xcopy /s /f /y /b /d C:\Users\Name\Documents\SourceBack C:\Users\Name\Documents\DestBack
>> xcopy.csv
Write-Output "last copied file(s) on $(Get-TimeStamp)" | Out-file
C:\Users\Name\Documents\Power\xcopy.csv -append
I think this is what you're looking for.
The output of xcopy is redirected to a temporary file. If that file contains only a single line (e.g. 0 File(s) copied), no further action is taken.
Otherwise, the output and an additional line with the timestamp is added to your CSV file.
At the end, regardless of the outcome, the temporary file is removed.
$Logfile = "C:\Users\Name\Documents\Power\xcopy.csv"
$TempFile = New-TemporaryFile
try {
Start-Process xcopy `
-ArgumentList '/s','/f','/y','/b','/d','C:\Users\Name\Documents\SourceBack','C:\Users\Name\Documents\DestBack' `
-RedirectStandardOutput $TempFile.FullName `
-Wait
$ProcessOutput = #(Get-Content $TempFile)
if ($ProcessOutput.Length -gt 1) {
($ProcessOutput + "last copied file(s) on $(Get-Date -Format '[dd/MM/yy HH:mm:ss]')") -join "`n" | Add-Content $Logfile
}
} finally {
Remove-Item $TempFile -Force
}
Remarks:
I removed the Get-TimeStamp function, as it was only used once and doesn't add a lot of benefit in that case.
I also removed the LogWrite function as it isn't used in your sample code, and it contains a syntax error (stray and).
You're appending a line to a CSV file (last copied file(s) …), which is bound to cause issues when you try to parse that file later on.
Update
You're never too old to learn, and it seems that Start-Process isn't necessary at all. For more information, see here and here.
$Logfile = "C:\Users\Name\Documents\Power\xcopy.csv"
[string[]] $Output = & xcopy /s /f /y /b /d C:\Users\Name\Documents\SourceBack C:\Users\Name\Documents\DestBack 2>&1
if ($ProcessOutput.Length -gt 1) {
($ProcessOutput + "last copied file(s) on $(Get-Date -Format '[dd/MM/yy HH:mm:ss]')") -join "`n" | Add-Content $Logfile
}

Powershell move files to a new folder that are not still writing to the source folder

I have a powershell script that's moving files from a source directory over to a target directory every 15 minutes. Files of around 1 meg are moving into the source directory by an SFTP server... so the files can be written at anytime by the SFTP clients.
The Move-Item command is moving files, however it seems that it's moving them without making sure the file isn't still being written (in-use?).
I need some help coming up with a way to write the files from the source to the target and make sure the entire file gets to the target. Anyone run across this issue before with Powershell?
I searched and was able to find a few functions that said they solved the problem but when I tried them out I wasn't seeing the same results.
Existing PowerShell script is below:
Move-Item "E:\SFTP_Server\UserFolder\*.*" "H:\TargetFolder\" -Verbose -Force *>&1 | Out-File -FilePath E:\Powershell_Scripts\LOGS\MoveFilesToTarget-$(get-date -f yyyy-MM-dd-HH-mm-ss).txt
I ended up cobbling together a few things and got this working as I wanted it. Basically I'm looping through the files and checking the length of the file once... then waiting a second and checking the length of the file again to see if it's changed. This seems to be working well. Here's a copy of the script incase it helps anyone in the future!
$logfile ="H:\WriteTest\LogFile_$(get-date -format `"yyyyMMdd_hhmmsstt`").txt"
function log($string, $color)
{
if ($Color -eq $null) {$color = "white"}
write-host $string -foregroundcolor $color
$string | out-file -Filepath $logfile -append
}
$SourcePath = "E:\SFTP_Server\UserFolder\"
$TargetPath = "H:\TargetFolder\"
$Stuff = Get-ChildItem "$SourcePath\*.*" | select name, fullname
ForEach($I in $Stuff){
log "Starting to process $I.name" green
$newfile = $TargetPath + $I.name
$LastLength = 1
$NewLength = (Get-Item $I.fullname).length
while ($NewLength -ne $LastLength) {
$LastLength = $NewLength
Start-Sleep -Seconds 1
log "Waiting 1 Second" green
$NewLength = (Get-Item $I.fullname).length
log "Current File Length = $NewLength" green
}
log "File Not In Use - Ready To Move!" green
Move-Item $I.fullname $TargetPath
}

Desired State Configuration can't get hash table from Script resource Get block

I have tested this using the built in Script resource from 1.1 as well as xScript 5.1.0.0 and get the same results. My Set and Test blocks work fine. I'm using several other script resources that are very similar and they work fine as well for the get block.
I've tried a lot of variations in syntax but it always comes back the same. I know the block is running because I commented out the line where the file which gets created gets removed and I see the file. I also ran this as a function in powershell and piped the output to Get-Member and can see it is indeed a hastable that is returned.
On a side note, I really don't like the method I'm using here to manage this setting through DSC. I'm open to other ideas as long as it is still within DSC.
Script StorePasswordsUsingReversibleEncyption
{
SetScript = {
secedit /export /cfg c:\temp\secpol.cfg
(gc C:\temp\secpol.cfg).replace("ClearTextPassword = 1", "ClearTextPassword = 0") | Out-File C:\temp\secpol.cfg
secedit /configure /db c:\windows\security\local.sdb /cfg c:\temp\secpol.cfg /areas SECURITYPOLICY /quiet
rm -force c:\temp\secpol.cfg -confirm:$false
}
TestScript = {
secedit /export /cfg c:\temp\secpol.cfg
$str = (Get-Content 'c:\temp\secpol.cfg' | select-String 'ClearTextPassword' -SimpleMatch).ToString()
rm -force c:\temp\secpol.cfg -confirm:$false
if ($str -eq 'ClearTextPassword = 0') {return $true}
else {return $false}
}
# Not working yet
GetScript = {
secedit /export /cfg c:\temp\secpol.cfg
$str = (Get-Content 'c:\temp\secpol.cfg' | select-String 'ClearTextPassword' -SimpleMatch).ToString()
rm -force c:\temp\secpol.cfg -confirm:$false
return #{Result = $str}
}
}
After I run Get-DSCConfiguration, the output returned in console is this:
Get-DscConfiguration : PowerShell DSC resource MSFT_ScriptResource failed to execute Get-TargetResource functionality
with error message: Failure to get the results from the script in a hash table format.
At line:1 char:1
+ Get-DscConfiguration
+ ~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (MSFT_DSCLocalConfigurationManager:root/Microsoft/...gurationManager)
[Get-DscConfiguration], CimException
+ FullyQualifiedErrorId : ProviderOperationExecutionFailure,Get-DscConfiguration
Try this:
GetScript = {
$null = secedit /export /cfg c:\temp\secpol.cfg
$str = (Get-Content 'c:\temp\secpol.cfg' | select-String 'ClearTextPassword' -SimpleMatch).ToString()
rm -force c:\temp\secpol.cfg -confirm:$false
return #{Result = $str}
}
The problem is that when you call an external command (like secedit), everything it writes to stdout is returned as output if this command (and that's pretty natural). But if don't catch it into a variable, it will be passed further to the output of your script block. The return statement is also a little misleading - it doesn't mean "return this thing only", but "write this thing to output stream, then return".
This means that your original GetScript doesn't return a single hashtable, but rather an arraythat looks like this:
#(
"some-output-from-secedit",
#{ Result = $str }
)
Assigning the output from external commands to a variable (I used $null in this case to denote I want to discard it) will prevent it from cluttering your script block's output.
Another way would be to redirect command's output to Write-Verbose (if you're interested in reading it) or to $null (if you don't care):
secedit /export /cfg c:\temp\secpol.cfg | write-verbose
can you try modifying your getscript block like this:
GetScript = {
start-process secedit -ArgumentList '/export /cfg c:\temp\secpol.cfg' -Wait
$str = (Get-Content 'c:\temp\secpol.cfg' | select-String 'ClearTextPassword' -SimpleMatch).ToString()
rm -force c:\temp\secpol.cfg -confirm:$false
return #{Result = $str}
}

backing up .thumbnails powershell

I have written a backup script, which backs up and logs errors. works fine , except for some .thumbnails, many other .thumbnails do get copied!
of 54000 Files copied, the same 480 .thumbnails do not ever get copied or logged. i will be checking the attributes however i feel the copy-item function shouldve done the job. Any other recommendations are welcome as well, but please stay on topic, thx!!!!
here is my backUP script
Function backUP{ Param ([string]$destination1 ,$list1)
$destination2 = $destination1
#extract new made string for backuplog
$index = $destination2.LastIndexOf("\")
$count = $destination2.length - $index
$source1 = $destination2.Substring($index, $count)
$finalstr2 = $logdrive + $source1
Foreach($item in $list1){
Copy-Item -Container: $true -Recurse -Force -Path $item -Destination $destination1 -erroraction Continue
if(-not $?)
{
write-output "ERROR de copiado : " $error| format-list | out-file -Append "$finalstr2\GCI-ERRORS-backup.txt"
Foreach($erritem in $error){
write-output "Error Data:" $erritem.TargetObject | out-file -Append "$finalstr2\GCI-ERRORS-backup.txt"
}
$error.Clear()
}
}
}
Are you sure your backUP function is receiving .thumbnails files in $list1? If the files are hidden, then Get-ChildItem will only return them if the -Force switch is used.
As for other recommendations, Robocopy.exe is a good dedicated tool for performing file synchronization.
Apparantly permissions to the thumbnails folder, i had not.
that set, script worked fine!

Start-Transcript: This host does not support transcription

I want to start a transcript on a Windows Server 2008 R2
Start-Transcript -path C:\Temp\test.txt
"Hello!"
Stop-Transcript
But PowerShell returns the following error message:
Start-Transcript : This host does not support transcription.
How it is possible to activate transcript?
Windows PowerShell v4 ISE and lower do not support transcription. You must use the command line to run the commandlet.
From PowerShell v5 Start-Transcript is supported natively in ISE.
COMPLETE ANSWER (PowerShell ISE 2.0/4.0)::
Having yet another look at this today on another server, I noticed that the latest PowerShell ISE (which also does not allow Start-Transcript) does not have an Output pane, and instead uses the new ConsolePane. Thus the function now is as follows:
Function Start-iseTranscript
{
Param(
[string]$logname = (Get-logNameFromDate -path "C:\fso" -postfix " $(hostname)" -Create)
)
$transcriptHeader = #"
**************************************
Windows PowerShell ISE Transcript Start
Start Time: $((get-date).ToString('yyyyMMddhhmmss'))
UserName: $env:username
UserDomain: $env:USERDNSDOMAIN
ComputerName: $env:COMPUTERNAME
Windows version: $((Get-WmiObject win32_operatingsystem).version)
**************************************
Transcript started. Output file is $logname
"#
$transcriptHeader >> $logname
$psISE.CurrentPowerShellTab.Output.Text >> $logname
#Keep current Prompt
if ($Global:__promptDef -eq $null)
{
$Global:__promptDef = (gci Function:Prompt).Definition
$promptDef = (gci Function:Prompt).Definition
} else
{
$promptDef = $Global:__promptDef
}
$newPromptDef = #'
if ($Host.Version.Major -eq 2)
{
if ($Global:_LastText -ne $psISE.CurrentPowerShellTab.Output.Text)
{
Compare-Object -ReferenceObject ($Global:_LastText.Split("`n")) -DifferenceObject ($psISE.CurrentPowerShellTab.Output.Text.Split("`n"))|?{$_.SideIndicator -eq "=>"}|%{
$_.InputObject.TrimEnd()}|Out-File -FilePath ($Global:_DSTranscript) -Append
$Global:_LastText = $psISE.CurrentPowerShellTab.Output.Text
}
} elseif ($Host.Version.Major -eq 4)
{
if ($Global:_LastText -ne $psISE.CurrentPowerShellTab.ConsolePane.Text)
{
Compare-Object -ReferenceObject ($Global:_LastText.Split("`n")) -DifferenceObject ($psISE.CurrentPowerShellTab.ConsolePane.Text.Split("`n"))|?{$_.SideIndicator -eq "=>"}|%{
$_.InputObject.TrimEnd()}|Out-File -FilePath ($Global:_DSTranscript) -Append
$Global:_LastText = $psISE.CurrentPowerShellTab.ConsolePane.Text
}
}
'# + $promptDef
$Global:_LastText = $psISE.CurrentPowerShellTab.Output.Text
New-Item -Path Function: -Name "Global:Prompt" -Value ([ScriptBlock]::Create($newPromptDef)) -Force|Out-Null
}
Taking over the prompt is incredibly useful for this, however keeping two copies of the Output buffer is not ideal. I've also added in TrimEnd() as PSISE 2.0 likes to append spaces to fill the entire horizontal line width. Not sure if PSISE 4.0 does this too, but it's no problem now anyway.
NEW ANSWER (PowerShell ISE 2.0)::
I have just recently returned to this problem, and there is a way of forcing every update in PowerShell ISE to log out as a command is executed. This relies on the log path being saved in a Global Variable called _DSTranscript. This variable is passed to the Start-iseTranscript function. I have then Hijacked the Prompt function to execute a compare between _LastText and the hostUI Output Text, and append the differences out to the log. It now works a treat.
Function Start-iseTranscript
{
Param(
[string]$logname = (Get-logNameFromDate -path "C:\fso" -postfix " $(hostname)" -Create)
)
$transcriptHeader = #"
**************************************
Windows PowerShell ISE Transcript Start
Start Time: $(get-date)
UserName: $env:username
UserDomain: $env:USERDNSDOMAIN
ComputerName: $env:COMPUTERNAME
Windows version: $((Get-WmiObject win32_operatingsystem).version)
**************************************
Transcript started. Output file is $logname
"#
$transcriptHeader >> $logname
$psISE.CurrentPowerShellTab.Output.Text >> $logname
#Keep current Prompt
if ($__promptDef -eq $null)
{
$__promptDef = (gci Function:Prompt).Definition
$promptDef = (gci Function:Prompt).Definition
} else
{
$promptDef = $__promptDef
}
$newPromptDef = #'
if ($global:_LastText -ne $psISE.CurrentPowerShellTab.Output.Text)
{
Compare-Object -ReferenceObject $global:_LastText.Split("`n") -DifferenceObject $psISE.CurrentPowerShellTab.Output.Text.Split("`n")|?{$_.SideIndicator -eq "=>"}|%{ $_.InputObject.TrimEnd()}|Out-File -FilePath ($Global:_DSTranscript) -Append
$global:_LastText = $psISE.CurrentPowerShellTab.Output.Text
}
'# + $promptDef
New-Item -Path Function: -Name "Global:Prompt" -Value ([ScriptBlock]::Create($newPromptDef)) -Force|Out-Null
}
ORIGINAL ANSWER::
PowerShell ISE does not natively support Transcription. There is a Scripting Guy blog about how to achieve this. Unfortunately this needs to be the last thing that is run in the script. This means that you need to remember to run it before closing the window. I wish this worked better, or there was a way to force it to run on window closure.
This is the function that produces close to the same result as the Start-Transcript feature:
Function Start-iseTranscript
{
Param(
[string]$logname = (Get-logNameFromDate -path "C:\fso" -name "log" -Create)
)
$transcriptHeader = #"
**************************************
Windows PowerShell ISE Transcript Start
Start Time: $(get-date)
UserName: $env:username
UserDomain: $env:USERDNSDOMAIN
ComputerName: $env:COMPUTERNAME
Windows version: $((Get-WmiObject win32_operatingsystem).version)
**************************************
Transcript started. Output file is $logname
"#
$transcriptHeader >> $logname
$psISE.CurrentPowerShellTab.Output.Text >> $logname
} #end function start-iseTranscript
Either accept you can't, or use a host that does support transcripts (like the console host: PowerShell.exe).
The powershell.exe will also generate this error if there is a problem writing to the log file. For example, if the log file was created by an administrator and the user doesn't have permissions to overwrite the log.
Start-Transcript : The host is not currently transcribing.
At D:\Test1.ps1:9 char:1
+ Start-Transcript -Path "$Source\logs\Test.txt"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Start-Transcript], PSInvalidOperationException
+ FullyQualifiedErrorId : InvalidOperation,Microsoft.PowerShell.Commands.StartTranscriptCommand
A good solution is to try using -Append or to make the log file unique by generating a date/time stamp.
Start-Transcript -Path "$Source\logs\Test.txt" -Append
This way a proper error message is generated.
Access to the path 'D:\Test\logs\Test.txt' is denied.
-Force has the same effect as -Append and will generate a permissions error.
Following the tip from #richard here I created a snippet that allows usage of Transaction logs where I need them (scheduled tasks), so I ended having in Windows 2008R2 the following code that can be run from powershell ISE or as a standalone script.
When run in ISE, the log information will be printed on screen
When run as a script, the log information will be saved to a file
if ($Host.Name -eq "Windows PowerShell ISE Host") {
$ISE=$true
} else {
$ISE=$false
}
if (-Not $ISE) {
$Date = Get-Date -f HHmmss_ddyyyy
Start-Transcript -Path "C:\Temp\$Date.log"
}
//////////
code here ...
//////////
if (-Not $ISE) {
Stop-Transcript
}
Tagging on to the amazing answer and work by #dwarfsoft:
if ($Host.Name -match 'ISE' -and $Host.version.Major -lt 4)
{
#Start-Transcript will not work here. Use Start-iseTranscript by #dwarfsoft above
Start-iseTranscript
}
else
{
#Start Transcript Will work here
Start-Transcript
}
In addition to the ISE (which I note the original poster LaPhi doesn't even mention), the other thing that can cause this error is if you're trying to use Start-Transcript within an Invoke-Command scriptblock. For instance if you're running the script on your client machine, and then connecting to the Windows Server 2008 R2 box via Invoke-Command so Start-Transcript outputs to the server.
When run in the local session Start-Transcript works as expected, however when using Invoke-Command the script is run within a remote session on that computer, and remote sessions have certain limitations, one of which is not supporting transcription.