I use following PowerShell script to monitor a mapped drive for newly created files and write these changes to a csv file.
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = 'Z:\'
$action =
{
$path = $event.SourceEventArgs.FullPath
$changetype = $event.SourceEventArgs.ChangeType
Write-output "$path was $changetype at $(get-date)" >> C:\temp\HostFileList.csv
}
Register-ObjectEvent $watcher 'Created' -Action $action
Get-EventSubscriber
while($true){ sleep -seconds 1 }
The computer I'm running this script on is heavily used by multiple users. Each user will log onto the computer with their own profile, click a desktop batch file to map a network drive folder and save their work there. My script is set up in Local Group Policy to be ran at start up. From what I can tell the script doesn't have any issues running in the background and listening to the mapped drive folder. My problem is that the script refuses to write an output file to the designated location. If I manually run the script, it creates the output file just fine. Also, when I manually run the script with the local GP startup script enabled, I get two entries in my file, which makes me believe the script is running fine from the local GP. Could anyone advise why my script creates an output file when I run it manually, but not when it is ran at startup? Any feedback is much appreciated.
As for this...
Write-output
Write-Output (Microsoft.PowerShell.Utility)
Sends the specified objects to the next command in the pipeline. If
the command is the last command in the pipeline, the objects are
displayed in the console.
Use the file cmdlets
Export-Csv
Out-File
... and the -append parameter. So, collect your properties and pipe to one of them.
| Export-Csv -Path 'C:\temp\HostFileList.csv' -Append -NoTypeInformation
Export-Csv (Microsoft.PowerShell.Utility)
Converts objects into a series of comma-separated value (CSV) strings
and saves the strings to a file.
$AppService = (Get-Service -DisplayName *Application* | Select-Object -Property DisplayName, Status)
$AppService | Export-Csv -Path .\Services.Csv -NoTypeInformation
Get-Content -Path .\Services.Csv
$WinService = (Get-Service -DisplayName *Windows* | Select-Object -Property DisplayName, Status)
$WinService | Export-Csv -Path ./Services.csv -NoTypeInformation -Append
Get-Content -Path .\Services.Csv
"DisplayName","Status"
"Application Layer Gateway Service","Stopped"
"Application Identity","Running"
"Windows Audio Endpoint Builder","Running"
"Windows Audio","Running"
"Windows Event Log","Running"
Out-File (Microsoft.PowerShell.Utility) - PowerShell
Sends output to a file.
-Append Adds the output to the end of an existing file.
Related
I am trying to use power shell to determine whether a server has a particular patch installed based on the KB and if not append the name to a csv. my input file has system names so I want to export that system name if it does not find the patch installed.
here is what i have so far. The export to csv part does not seem to work.
forEach-Object{
try{
$status = wmic /node:#sys.csv qfe list full /format:table | findstr /i $kb_number
if(!$status){
$output_file = New-Item C:\temp\$kb_number.csv -ItemType File
export-csv $output_file -append -Force
}
else{
write-output $status
}
}
catch{
$error_message = $_.Exception.Message
#write-output "the error message is" $error_message
write-output "Could not find any system with this patch installed."
}
}
Why your code might be failing
We don't see where you're setting the values of #sys.csv or $kb_number in the code you shared. Either of those could be throwing you off.
But the real issue is Export-Csv. For one, you're making a new CSV with every iteration of the loop. And for two, you have to pass in some item for the cmdlet to export as a CSV. Right now, you're only providing these values.
$output_file = New-Item C:\temp\$kb_number.csv -ItemType File
Export-csv -Path $output_file -append -Force
Export-Csv requires an input object. You're not giving it one now.
What do you want to export? If you just want a list of computers without a patch, do this instead.
if(-not(Test-path C:\temp\$kb_number.csv)){
#if file doesn't exist, make it
$output_file = New-Item C:\temp\$kb_number.txt -ItemType File
}
#adds computer name if it doesn't have the patch
Add-Content -Path $output_file -Value $computer
General Suggestions
Instead of using ForEach-Object, you might find it's easier to debug if you use a ForEach loop like this.
$computers = Get-Content C:\pathTo\Your\ComputerList.txt
ForEach($computer in $computers){
}
One additional source of trouble is that your code is using older dos commands in WMIC and then tries to use PowerShell to store the records. You don't need to do this and can make it easier on yourself if you swap out the calls to wmic for Get-WmiObject or Get-CimInstance, the PowerShell native versions of the commands.
To do that, change this line:
wmic /node:#sys.csv qfe list full /format:table | findstr /i $kb_number
translates into
$kb_number = "KB4576484"
Get-CimInstance Win32_QuickFixEngineering -Filter "HotfixID = '$kb_number'" -ComputerName $computer
Source Description HotFixID InstalledBy InstalledOn
------ ----------- -------- ----------- -----------
Update KB4576484 NT AUTHORITY\SYSTEM 9/14/2020 12:00:00 AM
You can store the output of that in a variable and then call Export-Csv on it and that should work.
When in doubt, remove the filter part and just get it working to export all patches to a csv. Then add complexity by adding back the filtering statements.
I would like to record all my inputs and outputs of my Powershell script into a logfile. I've tried a function, but I have to write the function in each line. I have test it with a Start-transcript, I dont know, but I think its only for Errors, it doesn't work really good.
It is possible, if I start my script to get a Log all lines?
for Example:
$u=rene
New-AdUser -Name $u ...
Write-Hoste "New User ist Create $u"
and in my log file should stand:
17.01.2018 13:00 $u=rene
17.01.2018 13:00 New-AdUser -Name $u ...
17.01.2018 13:00 Write-Hoste "New User ist Create $u"
... and everything else (for Example Errors)
If you want to log the entire script, then you could do something like this
#(
$u=rene
New-AdUser -Name $u ...
Write-Hoste "New User ist Create $u"
... Rest of your script
) | Out-file -FilePath \\PathToYourLogFile\Logfile.txt"
That is one way of doing it. One more way of doing it would be using the Start-Transcript at the beginning of your script and ending your script with Stop-Transcript cmdlet and redirecting the output to a text fie. See start-transcript and stop-transcript for details.
I found one example that will create a daily log for your script (if you run it multiple times a day it'll just append to what you have). The log will be in the same folder as your script and it'll clean out any logs older than 15 days.
$VerbosePreference = "Continue"
$LogPath = Split-Path $MyInvocation.MyCommand.Path
Get-ChildItem "$LogPath\*.log" | Where LastWriteTime -LT (Get-Date).AddDays(-15) | Remove-Item -Confirm:$false
$LogPathName = Join-Path -Path $LogPath -ChildPath "$($MyInvocation.MyCommand.Name)-$(Get-Date -Format 'MM-dd-yyyy').log"
Start-Transcript $LogPathName -Append
Then at the end of your script, just add
Stop-Transcript
Here is the link for the same.
If you want to log specific things then you could use Out-File cmdlet at specific and desired places. Hope that helps!
I've created a script that I'm using to clean up some drives at my work. I've been asked to create a log and leave it in the source folder after I move files.
Currently, the process is slow because my script creates a text file of files that meet the parameters I input. Once I have verified it, or edited the text file, I allow the script to read the file and keep doing what it needs to do. I'm creating this original text file with Out-File. Obviously, the path of the files change because I'm moving them from one drive to another. I'd like to log their new path but can't seem to figure out how to do this.
The file the script creates and reads from looks like the following:
C:\This\Is\The\Source\Something.rpt
C:\This\Is\The\Source\Somethingelse.bak
C:\This\Is\The\Source\AnotherFile.jpg
I'm looking to create something that will reflect the new path once the files are moved. In the different ways I've tried I either end up with nothing or just the last file copied, which would tell me Out-File is not appending but overwriting each time it gets a new file path.
And the list will just go on. The following is bit from my script I'm having issue with:
$path = Read-Host "What path should I look at?"
$SourceFolder = $path
$files = Get-ChildItem $path -Recurse
| Where-Object {$_.lastwritetime.Date -eq $targetdate}
|Where-Object {$_.PSIsContainer -eq $false}
| ForEach-Object {$_.fullname}
| Out-File $OutFileCopy
$Target = Read-Host "What is the destination?"
Write-Host "Please view the text file created." -foregroundcolor "yellow" -backgroundcolor "red"
Invoke-Item $OutFileCopy
$CopyContinueAnswer = Read-Host "Would you like to continue? Y or N?"
If ($CopyContinueAnswer -eq 'Y') {
$Readfile = Get-Content $outfilecopy
$ReadFile | Where-Object {$_.PSIsContainer -eq $false}
foreach ($file in $ReadFile) {
$logfile = "$Sourcefolder\log.txt"
out-file $logfile
Write-Host "The old path is $File"
$TargetPath = $File.Replace($SourceFolder, $Target)
if (!(Test-Path $TargetPath)){
Write-Host "This is the new path $TargetPath" -foregroundcolor "Magenta"
Write-Host
Copy-Item -path $file -Destination $TargetPath
Write-output $TargetPath | out-file $logfile
}
Out-File by default will overwrite an existing file. If you do not want this to happen, use Out-File -append. I recommend looking at the Microsoft documentation for Out-File; you can find it by typing Get-Help Out-File at any PowerShell prompt, or clicking on the link.
The file that I want to record the new files paths is being created inside the foreach loop. Everytime it stepped to the next file it would recreate the log.txt file and erase what was there previously. Now that I've taken it out of the loop, I don't have the issue of nothing being recorded or only the last file that went through the loop being recorded.
I'll create a portion of the script that looks to see if the log.txt already exists before it tries to create one.
I'm trying to run a command which accepts a list of names. It should go through each member of the list and then output it to a text file in a specific location, and the name of that text file will be the member name from the list. Then the script continues to the next member run the command on it, and then write the output to a text file whose name will be the 2nd member in the list.
I'm sure a loop is involved, and perhaps a temporary variable which I have no idea how to declare :(
Invoke-Command -ComputerName (Get-Content "C:\ServerList.txt") -ScriptBlock {
Servermanagercmd.exe -query roles.xml
} -credential baloon\yellow | Out-File C:\OutputF.txt
Pull out the Get-Content of the serverlist file so the server name values are available down the pipeline:
Get-Content c:\serverlist.txt | Where {$_} |
Foreach { Invoke-Command -ComputerName $_ -Scriptblock {...} -Credential baloon\yellow |
Out-File "$_.txt" }
Note that the Where {$_} is to weed out any empty lines in the file.
Shall I assume this issue is that the data that is being output has no context since you wont know what system it is from?
$outputfile = C:\OutputF.txt
Set-Content -Path $outputfile -Value ""
ForEach($server in (Get-Content "C:\ServerList.txt")){
Add-Content -Path $outputfile -Value "Server: $server"
Invoke-Command -ComputerName $server -ScriptBlock {Servermanagercmd.exe -query roles.xml} -credential baloon\yellow |
Add-Content -Path $outputfile
}
Wipe the file to start new (remove this if you dont want to erase the file.). For each server in your list add the server name on its own line in the file and then run the Invoke-Command also sending its output to file.
This will might be inefficient depending on the number of server you are checking. At that point I would consider using jobs.
Update
After reading Keith Hills answer I realize that I misread your question. My logic would output all to one file with the server name separating the contents. You actually wanted separate files.
I want my PowerShell script to go out to remote servers, gather hotfix data, and export that data to Excel for each server or one Excel sheet per server.
Currently when I run the script it does go out to each server listed in a .txt file and gathers the hotfix data. But it exports that data to one Excel file with a name of all the servers. It's a very long name!
Here's the script:
$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
$Server_Name = Get-Content $dir\computers.txt
Get-HotFix -computername $Server_Name | Export-Csv -Path "$dir\$Server_Name HotFixData.csv" -notype
Get-Content $dir\computers.txt | Foreach-Object {
Get-HotFix -ComputerName $_ | Export-Csv -Path "$dir\$($_)_HotFixData.csv" -notype
}