I am having a bit of trouble with a PowerShell script. The intent of this is to spider the network and look for files/folders that exist on any PC.
Here is the original source:
#FiToFin Script#
$Fltr = "how_recover*.*"
$Online = "C:\Users\<username>\Scripts\Logs\Online.log"
$CSV = "C:\Users\<username>\Scripts\Devices.csv"
#$Tstpath = test-path "\\$computer\c$"
$Offline = "C:\Users\<username>\Scripts\Logs\Offline.log"
##################################################
$devices = Get-Content "$CSV"
foreach ($computer in $devices) {
Test-Path "\\$computer\c$" > $Tstpath
if ($Tstpath -eq $True) {
ls -Path "\\$computer\c$\users" -Filter $Fltr -Recurse |
Out-File -Append $Online
} else {
Write-Host "$computer is NOT Online" | Out-File -Append $Offline
}
}
##################################################
Write-Host "_____________________"
Write-Host "Online file = $Online"
Write-Host "Offile file = $Offline"
Write-Host "_____________________"
I have changed the if statement to if($Tstpath -eq "True"), if ($lastexitcode -eq $true) and if($Tstpath -eq $false) and they all just parse the first {Do command} no matter what. They never drop into else. Even tried the Tstpath = Test-Path \\$computer\c$ as a variable and just running that.
When it parses the first {Do Command} the return is
ls : Cannot find path '\\<computerName>\c$\u' because it does not exist.
At C:\Users\<username>\Scripts\FiToFin.ps1:19 char:3
+ ls -Path "\\$computer\c$\users" -Filter $Fltr -Recurse | Out-File -Append $On ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (\\<computername>\c$\u:String) [Get-ChildItem], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
What does work:
If my test machines are on I can ls -Path "\\$computer\c$\users" -Filter $Fltr -Recurse | Out-File -Append $Online just fine.
I get True or False from Test-Path \\$computer\c$ and can even > $var and Write-Host the results just fine.
I have no idea why this is and would love to know.
This also works:
###################################################################
$computer = "TestPC"
$Tstpath = Test-Path \\$computer\c$
####################################################################
$Tstpath > $null
if($Tstpath -eq $True) {
Write-Host "$computer is Online"
} else {
Write-Host "$computer is NOT Online"
}
But when you add the command ls or Get-ChildItem it freaks out.
So, question is: Why is it never executing the else portion?
I see two issues that would be causing your issues. How you initialize and update the variable $Tstpath
# Presumably Initialize
$Tstpath = test-path "\\$computer\c$"
# Updating in loop
test-path "\\$computer\c$" > $Tstpath
I will assume that you are testing in PowerShell ISE and that $Tstpath had a $true value at some point.
The issue is that you were never updating the variable. Looking at TechNet for about_redirection you will see that:
Operator Description Example
-------- ---------------------- ------------------------------
'>' Sends output to the Get-Process > Process.txt
specified file.
Your command was trying to output that to "file". You should have had an error about not being able to find the file or a file somewhere on your system with a single boolean in it (since it was not an append redirector).
What you should have done to stay with your logic is save the result via assignment.
$Tstpath = Test-path "\\$computer\c$"
Then you can test that.
However it is redundant since you do not ever need that value again. Would just be easier to put it straight in the if statement.
if(test-path "\\$computer\c$"){"Do something"}else{"Fail Trumpet"}
I would also suggest using Export-CSV -Append since you are dealing with objects. Would make for good structured output.
Get-ChildItem -path "\\$computer\c$\users\" -Filter $Fltr -Recurse | Export-CSV -Append $Online -NoTypeInformation
Related
New here and getting to learn powershell, so forgive me for mistakes.
A senior staff had left abruptly and i was tasked to finding out all folders in DFS that the employee had access to (security reasons).
Couldn't find a script that does that for me (to scan 14TB of DFS shares to find what folders user or his group memberships may have access to), so just wrote my own.
Its working fine but too slow for my liking, wondering if it can be tuned to run faster.
running it in 2 parts to save folders first, then user each folder path to get ACL permissions and filter against the username to a csv (with ~ as delimiter to avoid messing with commas).
using powershell 5.1
$ErrorActionPreference = "Continue"
#$rootDirectory = 'C:\temp'
$rootDirectory = '\\?\UNC\myServer\myShare'
$scriptName = 'myACL'
$version = 1.0
$dateStamp = (Get-Date).ToString('yyyyMMddHHmm')
$scriptDirectory = $PSScriptRoot
$log = $scriptDirectory + "\" + $scriptName + "_dirList_v" + $version + "_"+$dateStamp+".log"
"Path" | Out-File $log
function getSubfolders ([String]$arg_directory, [string]$arg_log)
{
$subFolders = Get-ChildItem -LiteralPath $arg_directory -Directory -Force -ErrorAction SilentlyContinue | Select-Object -expandProperty FullName
$subFolders | Out-File $arg_log -append
#"just before loop" | Out-File $arg_log -append
foreach ($folder in $subFolders)
{
#"working on $folder" | Out-File $arg_log -append
getSubfolders $folder $arg_log
}
#"returning from function" | Out-File $arg_log -append
}
#part1
getSubfolders $rootDirectory $log
#part2
$dirListSourceFile = $log
$log2 = $scriptDirectory + "\" + $scriptName + "_permissionList_v" + $version + "_"+$dateStamp+".csv"
$i=0
"Sr~Path~User/Group~Rights~isInherited?" | Out-File $log2
Start-Sleep -s 2
Import-CSV $dirListSourceFile | ForEach-Object{
$i++
$path = $_.path.Trim()
$Acl = get-acl $path | Select *
ForEach ($Access in $Acl.Access)
{
if($Access.IdentityReference.value -eq "mydomain\user1" -or $Access.IdentityReference.value -eq "mydomain\sg1" -or $Access.IdentityReference.value -eq "mydomain\sg2" -or $Access.IdentityReference.value -eq "mydomain\sg3" -or $Access.IdentityReference.value -eq "mydomain\sg4")
{
"$i~$path~$($Access.IdentityReference.value)~$($Access.FileSystemRights)~$($Access.IsInherited)" | Out-File $log2 -append
}
}
}
As you can read in the comments, if you have the possibility to run the code locally do it. You can use the same technique, as you did in case of the UNC path, for local paths - e.g. \\?\C:\directoy
see:
https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry
Furthermore you did write a recursive function but thats not necessary in this case as get-childitem has this feature built in. Currently you call for each subfolder get-childitem again and also you write each time a log entry to the disk. Its faster to collect the data and write it to the disk one time, e.g.:
#Get paths locally and add \\?\ in combination with -pspath to overcome 256 string length limit, add -recurse for recursive enumeration
$folders = get-childitem -PSPath "\\?\[localpath]" -Directory -Recurse -ErrorAction:SilentlyContinue
#Write to lofile
$folders.fullname | set-content -Path $arg_log
Also if you want to optimize performance avoid unecessary operations like this:
$Acl = get-acl $path | Select *
get-acl gives you a complete object and you take it send it over the pipeline and select all (*) properties from it. Why? This $Acl = get-acl $path is enough.
Finally you may use io classes directly, instead of get-childitem - see:
How to speed up Powershell Get-Childitem over UNC
Trying to delete files from multiple path,
So have created a csv file like path,days,filter
importing this file in shell and looping over each object to delete contents, but getchilditem is failing again and again,
Unable to understand reason behind that,
Below is the code what m trying to achieve
Start-Transcript -Path "D:\delete.log"
$pathlist= Import-csv -LiteralPath "D:\diskpath.csv"
$count = 0
foreach($p in $pathlist){
Write-host $p.path " | " $p.days -ForegroundColor DarkCyan
$path = $p.path
$days = $p.days
$filter = $p.filter
Get-ChildItem -Path $path -Filter $filter | where-object{$_.LastWriteTime -lt [datetime]::Now.AddDays(-$days)}|Remove-Item -Force -Verbose -Recurse -ErrorAction SilentlyContinue -Confirm $false
}
Stop-Transcript
without for loop, script executes properly, but with for loop it fails
Please let know if any further information needed on this query,
will like provide the same,
have already google and read multiple questions here at SO, but unable to find reason behind failure,
#T-Me and #Theo
Thanks for highlighting error, haven't looked type error in script my bad, whereas while manual typing in PowerShell was writing correctly, but in script made mistake, now its working-
Start-Transcript -Path "D:\delete.log"
$pathlist= Import-csv -LiteralPath "D:\diskpath.csv"
$count = 0
foreach($p in $pathlist){
Write-host $p.path " | " $p.days -ForegroundColor DarkCyan
$path = $p.path
$days = $p.days
$filter = $p.filter
Get-ChildItem -Path $path -Filter $filter | where-object{$_.LastWriteTime -lt [datetime]::Now.AddDays(-$days)}|Remove-Item -Force -Verbose -Recurse
}
I am trying to figure out how to write a powershell script that will automatically install office2010 on multiple pcs. I am struggling on the portion where you create the text file that we loop through listing the ComputerName and the Users Login. I have researched this all over the web but for some reason am unable to get this to work.
Function Get-FileName{
[CmdletBinding()]
Param(
[String]$Filter = "|*.*",
[String]$InitialDirectory = "C:\")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $InitialDirectory
$OpenFileDialog.filter = $Filter
[void]$OpenFileDialog.ShowDialog()
$OpenFileDialog.filename
}
ForEach ($computer in (GC (Get-FileName -InitialDirectory $env:USERPROFILE\Desktop -Filter "Text files (*.txt)|*.txt|All files (*.*)|*.*"))) {
$filepath = Test-Path -Path "\\$computer\C:\Program Files (x86)\Microsoft Office"
If ($filepath -eq $false)
{
Get-Service remoteregistry -ComputerName $computer | Start-Service
Copy-Item -Path "\\server\Orig\Install\Office2010" -Destination "\\$computer\c$\windows\temp\" -Container -Recurse -Force
# $InstallString = '"C:\windows\temp\Office 2010\setup.exe"'
# ([WMICLASS]"\\$computer\ROOT\CIMV2:Win32_Process").Create($InstallString)
# "$computer" + "-" + "(Get-Date)" | Out-File -FilePath "\\server\Orig\Install\RemoteInstallfile.txt" -Append
# }
# Else
# {
# "$computer" + "_Already_Had_Software_" + "(Get-Date)" | Out-File -FilePath "\\server\Orig\Install\RemoteInstallfile.txt" -Append
}
}
ComputerList.txt
IT-Tech | David
IT-Tech would be the computer name and David would be the user. Then I would have a list like this line by line in the txt file.
So i was thinking I could do something like this Listing the computer name and then the user name of how to install. This part confuses me though just trying to learn and see what this powershell stuff is all about!
Any help with this would be greatly appreciated!
A line of your file, as you've said, will contain something like "IT-Tech | David", so when you iterate through that file that's the value of $computer. You then attempt to use this as the computer name call which will of course fail because first you need to split it out.
I will also point out it is extremely bad form to abbreviate and use aliases in scripts, you should only use them in the console. Also for readability it helps to split complex bits out.
$file = Get-FileName -InitialDirectory $env:USERPROFILE\Desktop -Filter "Text files (*.txt)|*.txt|All files (*.*)|*.*"
ForEach ($item in (Get-Content $file)) {
$sitem = $item.Split("|")
$computer = $sitem[0].Trim()
$user = $sitem[1].Trim()
$filepath = Test-Path -Path "\\$computer\C:\Program Files (x86)\Microsoft Office"
If ($filepath -eq $false)
{
Get-Service remoteregistry -ComputerName $computer | Start-Service
Copy-Item -Path "\\server\Orig\Install\Office2010" -Destination "\\$computer\c$\windows\temp\" -Container -Recurse -Force
<#
$InstallString = '"C:\windows\temp\Office 2010\setup.exe"'
([WMICLASS]"\\$computer\ROOT\CIMV2:Win32_Process").Create($InstallString)
"$computer" + "-" + "(Get-Date)" | Out-File -FilePath "\\server\Orig\Install\RemoteInstallfile.txt" -Append
}
Else
{
"$computer" + "_Already_Had_Software_" + "(Get-Date)" | Out-File -FilePath "\\server\Orig\Install\RemoteInstallfile.txt" -Append
#>
}
}
Note that this will NOT install the product if the installer is already in the destination, not sure if that is intended behaviour or not.
I am trying to create a powershell script that will check for 3 specific files in a folder. If the 3 files exist continue.
I keep trying to use test-path command. The closest i got was this:
$checkwim = test-path $imagepath\* -include OS.wim, data.wim, backup.wim.
This however does't work for me as it returns "True" if any of the 3 are found. I need to make sure all 3 exist.
I got it to work using the below but i was hoping for an easier\shorter method.
$checkwimos = test-path $imagepath\* -include OS.wim
$checkwimdata = test-path $imagepath\* -include Data.wim
$checkwimonline = test-path $imagepath\* -include Online.wim
if (($checkwimos -ne $True) -or ($checkwimdata -ne $True) -or ($checkwimonline -ne $True))
{
Echo "WIM file(s) not located. Script Aborting"
exit
}
Is there a simpler way to do this?
if you wanted to avoid hard coding a check for each file type to include you could do something like this:
$files = #('os.wim','data.wim','backup.wim')
$checkWim = $files | foreach-object {test-path $imagepath\* -Include $_} | Where-Object {$_ -eq $false}
If($checkWim -eq $false){"WIM file(s) not located. Script Aborting"}
else{
#do stuff
}
You could also import a list of file instead of creating an array.
Another tack:
$files = #('os.wim','data.wim','backup.wim')
if (($files | foreach {test-path $imagepath\$_}) -contains $false)
{
Echo "WIM file(s) not located. Script Aborting"
exit
}
Your way is already pretty simple, although you could make it a bit more readable with:
$checkwimos = test-path (Join-Path $imagepath OS.wim)
$checkwimdata = test-path (Join-Path $imagepath Data.wim)
$checkwimonline = test-path (Join-Path $imagepath Online.wim)
if (-not ($checkwimos -and $checkwimdata -and $checkwimonline))
{
Echo "WIM file(s) not located. Script Aborting"
exit
}
if you insist on a one-liner, the below should work.
if (#(Get-ChildItem -LiteralPath "C:\temp\" | Where-Object -FilterScript {#("os.wim", "data.wim", "backup.wim") -ccontains $_.Name}).Count -eq 3)
{
write "Files were present"
}
powershell newb here. I am having some difficulty trying log my output to a file. I have tried two tactics, both of which do not work for me. The first is using the Start/Stop-Transcript cmdlet. This works great in testing on my local machine, but doesn't seem to work at all in a script that I deploy to workstations.
$path1 = Test-Path ($env:ProgramFiles + "\Sophos\Sophos Anti-Virus\SavService.exe")
$path2 = Test-Path (${env:ProgramFiles(x86)} + "\Sophos\Sophos Anti-Virus\SavService.exe")
$shareloc = '\\SERVER1\NETLOGON\SophosPackages\SophosInstall_wFW_Silent.exe'
$logpath = '\\SERVER1\NETLOGON\si_sophos_log.txt'
if (($path1 -eq $true) -or ($path2 -eq $true)) {} ELSE {
& $shareloc
Start-Transcript -Append -Path $logpath | Out-Null
Write-Output ""
Get-Date
Write-Output "Sophos has been installed on `"$env:COMPUTERNAME`""
Write-Output ""
Stop-Transcript
}
The way I would prefer to do it, is using: | Out-File -Append -FilePath $logpath
I think this would be the preferred method because it would catch any error that might occur in the log, as apposed to Start-Transcript. When I try to use this method however, I get an error at the pipeline "An empty pipeline element is not allowed."
$path1 = Test-Path ($env:ProgramFiles + "\Sophos\Sophos Anti-Virus\SavService.exe")
$path2 = Test-Path (${env:ProgramFiles(x86)} + "\Sophos\Sophos Anti-Virus\SavService.exe")
$shareloc = '\\SERVER1\NETLOGON\SophosPackages\SophosInstall_wFW_Silent.exe'
$logpath = '\\SERVER1\NETLOGON\si_sophos_log.txt'
if (($path1 -eq $true) -or ($path2 -eq $true)) {} ELSE {
& $shareloc
Write-Output ""
Get-Date
Write-Output "Sophos has been installed on `"$env:COMPUTERNAME`""
Write-Output ""
} | Out-File -Append -FilePath $logpath
Thank you in advance for any assistance!
If you write the following :
if ($true) {Write-Output "titi"} else {Write-Output "toto"} | Out-File -Append c:\temp\titi
You will get the same error, because the if condition is not evaluated when you pipe.
You can try to force ti evaluate it
$(if ($true) {Write-Output "titi"} else {Write-Output "toto"}) | Out-File -Append c:\temp\titi
When the if condition evaluates as true, the empty scriptblock gets piped to Out-File which causes your error. i.e. the following throws the error you specified:
if($true) { } else { Write-Output "Something" } | Out-File -Append -FilePath C:\temp\myfile.txt