Powershell - calling icacls with parantheses included in parameters - powershell

I'm pretty new to Powershell, but I have lots of experience in VBScript and Python. I'm trying to be a good Windows admin and get into Powershell more. So, here is what I'm trying to do:
A parent folder contains dozens of sub-folders that are named as AD usernames (ex. Users\username1, Users\username2, where Users is the parent folder). I want to loop through each folder name, parse out the sub-folder name, and pass that to icacls to apply permissions based on the username. I did a multi-liner because I was running into issues piping. This is what I have after trying several different approaches:
$root_folder = "c:\temp\test"
$cmd1 = "icacls "
$cmd2 = " /grant cm\"
$cmd3 = ":`(OI`)`(CI`)F"
$paths_collection = get-childitem $root_folder | foreach-object -process {$_.FullName}
foreach ($path in $paths_collection)
{$string = $path.split("\");$last = $string[-1];$command = $cmd1 + $path +$cmd2 +$last +$cmd3;invoke-expression $command}
It wasn't originally this rough, but I started breaking it apart when I was running into issues.
THE PROBLEM - in $cmd3, the (OI)(CI) is not coming in cleanly to the invoke-expression. If I change $cmd3 to just ":F" it works, but I have to set inheritance using the offending parameters. PLEASE HELP. I've been racking my brain all day on this one. Couldn't really find anything that dealt with this issue specifically (tried backticks, referencing the $command as '$command', etc.)
ERROR:
The term 'OI' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling
of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:56
+ icacls C:\temp\test\garthwaitm /grant domain\user1:(OI <<<< )(IO)F
+ CategoryInfo : ObjectNotFound: (OI:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException

Just to add to this old question, in PowerShell 3.0 you can now use --% to tell PowerShell to stop processing anything else on the line, so you can use something like this:
icacls.exe $path --% /grant "Everyone:(OI)(CI)(F)"

I think you are unnecessarily complicating it.
Get the echoargs.exe from Powershell Community Extensions.
See if something like below is what you wanted:
PS >.\EchoArgs.exe /grant $path "cm\$last" ":(OI)(CI)F"
Arg 0 is </grant>
Arg 1 is <c:\test>
Arg 2 is <cm\user>
Arg 3 is <:(OI)(CI)F>
Then call it with the command you want:
&icacls /grant $path "cm\$last" ":(OI)(CI)F"
BTW, you can use Split-Path to get the $last. And use select -expand fullname instead of the foreach-object -process {$_.FullName}

Have you tried using quotes around $command, e.g. Invoke-Expression "$command"? Other techniques for preventing parentheses from being processed are discussed here: http://msdn.microsoft.com/en-us/library/cc281841.aspx

Related

Literal quotes in "Powershell Start-Process -ArgumentList"

I am currently attempting to create a command that opens an admin Powershell from the right click context menu. For context, context menu commands run in CMD.
My issue is that I am trying to cd into the directory where the right click occurs. The below command works just fine for most directories, but if the directory path contains a space, then it will only try to move into the portion of the path before the space, throwing an error. My understanding is that the current directory is passed in through %V but when I run the command echo %V using the same process, it splits paths with a space onto 2 lines, so I assume the parts of the path are stored in separate strings?
Powershell -noexit "Start-Process 'C:\Users\<me>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Windows PowerShell\Windows PowerShell.lnk' -ArgumentList '-noexit','Set-Location -literalPath `"%V`"' -Verb RunAs"
I have updated the above command to match a suggestion below, and when right clicking on the desktop (which previously worked due to a lack of spaces) I now get the following error:
Set-Location : Cannot find path 'C:\Users\<me>\Desktop`' because it does not exist.
At line:1 char:1
+ Set-Location -literalPath `C:\Users\<me>\Desktop`
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Users\<me>\Desktop`:String) [Set-Location], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.SetLocationCommand
Note that in both of the above code blocks, <me> is my actual username.
I've been tearing my hair out trying to put quotes around the path but I can't seem to get Powershell to put the quotes in due to the fact that I already use both single and double quotes.
Any help would be greatly appreciated, thanks in advance.
Edit:
For those still looking for an answer, I ran the following Powershell script to add functional commands to my context menu:
$menu = 'Open Windows PowerShell Here as Administrator'
$command = "$PSHOME\powershell.exe -NoExit -NoProfile -Command ""Set-Location '%V'"""
'directory', 'directory\background', 'drive' | ForEach-Object {
New-Item -Path "Registry::HKEY_CLASSES_ROOT\$_\shell" -Name runas\command -Force |
Set-ItemProperty -Name '(default)' -Value $command -PassThru |
Set-ItemProperty -Path {$_.PSParentPath} -Name '(default)' -Value $menu -PassThru |
Set-ItemProperty -Name HasLUAShield -Value ''
}
The answer was found from How do I start PowerShell from Windows Explorer?
If you want to avoid space issues, you can reuse " by escaping it with ` in a string.
For example :
$command = "Set-Location `"C:\temp\test space`""
String will become this and spaces will be handled correctly :
Set-Location "C:\temp\test space"

Unable to get output from get-filehash

I am looking for a reliable command-line method of getting SHA256 hashes for files in Windows. My understanding is that the way to do this is via Microsoft's Get-FileHash cmdlet under PowerShell. I have seen several web sites with examples and reviewed Microsoft's own documentation. It appears that the following syntax should work on Windows Server 2012:
Get-FileHash myfile.txt -Algorithm SHA256
The command runs without error, but there is no output. If I send the output to a file, the file is created with no content. I have also seen examples which pipe the output to Format-List; I tried that, but still nothing. I have also tried running the command with invalid arguments, and again nothing.
I am open to using a different program, but due to business requirements, it would need to be a supported download.
I'm using PowerShell 4.0 and I just encountered the same problem of null output from Get-FileHash. The cause of my problem is different than the OP but I have found a solution to my problem and I figured I would post my findings for anyone who came to this page trying to solve the problem of null output (or seemingly incorrect output) from Get-FileHash.
The problem only happens (for me) when the path to the target file contains brackets [ ] and those brackets contain either zero characters or 2 or more characters.
EDIT: I now understand WHY this happens. The string is interpreted as Regular Expression (RegEx) so the square brackets [ ] take on their special RegEx meaning. The -LiteralPath tells PowerShell to interpret the string as a simple match (no RegEx).
Consider the following paths which refer to 4 existing text files (hypothetically):
C:\Test\My Text.txt
C:\Test\My [Text].txt
C:\Test\My [Te]xt.txt
C:\Test\My Text[].txt
The following command produces normal output:
Get-FileHash "C:\Test\My Text.txt"
but there will be null output if using the following commands:
Get-FileHash "C:\Test\My [Text].txt"
Get-FileHash "C:\Test\My [Te]xt.txt"
Get-FileHash "C:\Test\My Text[].txt"
This can be solved by using the -LiteralPath switch. For example:
Get-FileHash -LiteralPath "C:\Test\My [Text].txt"
Variables are expanded normally when using the -LiteralPath switch. For example:
(Get-ChildItem C:\Test).FullName | ForEach {
Get-FileHash -LiteralPath $_
}
If there is exactly 1 character between the brackets, the brackets will be ignored when using Get-FileHash.
Consider the following paths which refer to 3 existing text files (hypothetically), each with unique hash values:
C:\Test\My Text.txt
C:\Test\My Tex[t].txt
C:\Test\My[ ]Text.txt
Get-FileHash interprets all three of the following commands in exactly the same way ( the path is interpreted as C:\Test\My Text.txt ) and therefore each command has the exact same output despite each file having it's own unique hash value:
Get-FileHash "C:\Test\My Text.txt"
Get-FileHash "C:\Test\My Tex[t].txt"
Get-FileHash "C:\Test\My[ ]Text.txt"
P.S. I'm a very new programmer, please forgive me for any poor usage of terminology.
Get-FileHash, requires Windows PowerShell 4.0
Based on your comments you are at version 3, which is default on Win 2012 (non R2) Here how to check you PS version
You can update PS on Win 2012 (non R2) to version 4.0 or use Win 2012 R2
If you just run Get-FileHash on a PS version 3 system you should get
PS C:\> Get-FileHash
Get-FileHash : The term 'Get-FileHash' is not recognized as the name of a cmdlet, function, script file, or operable
program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ Get-FileHash
+ ~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Get-FileHash:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
On lower PS version you can use this function
Function Get-FileHashTSO([String] $FileName,$HashName = "SHA1")
{
$FileStream = New-Object System.IO.FileStream($FileName,[System.IO.FileMode]::Open)
$StringBuilder = New-Object System.Text.StringBuilder
[System.Security.Cryptography.HashAlgorithm]::Create($HashName).ComputeHash($FileStream)|%{[Void]$StringBuilder.Append($_.ToString("x2"))}
$FileStream.Close()
$StringBuilder.ToString()
}
store it as .ps (e.g. Get-FileHashTSO.ps1) file and call it like this
powershell -command "& { . C:\myScripts\Get-FileHashTSO.ps1 ; Get-FileHashTSO "C:\someLocation\someFile.iso" "SHA1" }"
I calculated the hash of all files on a drive and exported them to a .csv by using this:
Get-ChildItem C: -Recurse |
Get-FileHash |
Export-Csv -Path C:\Users\yourname\Documents\Output\hashes.csv -NoTypeInformation
Import-Csv -Path C:\Users\yourname\Documents\Output\hashes.csv
Why it works (I think):
Get-ChildItem <--gets everything under your path, in this case C:, and -Recurse gets all the files within folders. You can add limitations if needed.
Get-FileHash <--after you've said get these files, you're saying calculate the hashes
Export-Csv <--says, we're sending your hashes out as comma separated values file, which is crazy helpful, and -Path says put it HERE, -NoTypeInformation just removes the #TYPE row from the top of the .csv file, and versions of PowerShell before 6 need this.
Import-Csv <--says, bring that data into the file at this -Path
Be sure to have the .csv file created in that location before you run the script. The script won't create the container for you. There's no need to clear the data from the .csv file between runs, it clears itself.
I'm not really a programmer, hence the annoyingly lengthy explanation. Hope it helps others. Wouldn't have figured it out without Stack Overflow forums! Thanks everyone!!

trying to parse output file with powershell

I have a script that monitors a specific process and I have a working script that outputs it as such;
0 0 0
I am using this script to try to cut everything after the first "0" using this script
Get-Content –path c:\batchjobs\location\test.txt | Trim(Char[8,60]) > c:\batchjobs\location\test1.txt
the powershell ise keeps erroring out, here's the output, from that:
PS C:\batchjobs\location> C:\batchjobs\location\TestTrim.ps1
The term 'Trim' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name,
or if a path was included, verify that the path is correct and try again.
At C:\batchjobs\location\TestTrim.ps1:2 char:58
+ Get-Content –path c:\batchjobs\location\test.txt | Trim <<<< (Char[8,60]) > c:\batchjobs\location\test1.txt
+ CategoryInfo : ObjectNotFound: (Trim:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
any help would be greatly appreciated
Methods such as Trim and SubString require an object to operate on, you are not telling the script which object you mean. In the context you will want to indicate $_ for the result object. Also, Get-Content will return all the lines of the file, iterate over them using For-Each a.k.a %.
Get-Content -path c:\batchjobs\location\test.txt | % { $_.Substring(0,7).Trim(); } > c:\batchjobs\location\test1.txt
Assuming your files are of a size such that you won't start getting out of memory errors using get-content, then this approach is fine. If you want the first 8 chars of a huge file (maybe 1GB in size?), then the following approach might be a little more flexible:
$stream = [System.IO.StreamReader]"C:\Users\Ryan\Desktop\test.csv"
$i=0
#read characters until we get to 8 or end of the stream. won't load the while file into memory
$output = while(($i -lt 8) -and (!$stream.EndOfStream)){ [char]$stream.Read(); $i++ }
$stream.close()

I need help understanding PowerShell security and file access issues

I'm working with PowerShell, running a script (from my console) that includes this line:
$inpath = "C:\users\xxxxx\path\foo\bar"
and I keep getting this error:
Get-Content : Access to the path 'C:\users\xxxxx\path\foo\bar' is denied.
At C:\users\xxxxx\path\foo\testscript.ps1:53 char:12
+ Get-Content <<<< $txtfile | Get-WordCount -Exclude (Get-Content c:\temp\exclude.txt) | select -First 15
+ CategoryInfo : PermissionDenied: (C:\users\xxxxx\path\foo\bar:String) [Get-Content], UnauthorizedAcc
essException
+ FullyQualifiedErrorId : GetContentReaderUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetContentCommand
The scripts and target files are all located on my local drive. I can access the files in Explorer, view/edit/save them using NotePad, and do not have any permissions restrictions set. When I'm on the command line, I can run the get-content cmdlet successfully on files in my path. I can change directories PS C:> cd C:\users\xxxxx\path\foo\bar and successfully list what's there. Even more interesting, I can duplicate the line that's erroring in the script, and NOT receive an error on the command line.
PS C:\users\xxxxx\path\foo> $inpath = "C:\users\xxxxx\path\foo\bar"
PS C:\users\xxxxx\path\foo>
This makes me suspect that the 'Permission Denied' error is actually something else, or something vague enough that I've got no clue how to proceed with troubleshooting. Is it possible for PS to have different permissions than the user under which it's running? Has anyone seen this behavior before, and how did you solve the problem? I'm sure there's a simple solution that I don't know.
Get-Content : Access to the path 'C:\users\xxxxx\path\foo\bar' is denied.
At C:\users\xxxxx\path\foo\testscript.ps1:53 char:12
That path doesn't look like it is a file but a folder.
Are you sure you are appending the file name to the folder and passing that to Get-Content?
Windows gives Access Denied when you try and open a directory as if it were a file without passing extra flags; and .NET does not pass those flags (there are a few specific circumstances for opening a folder, but they do not apply here).
Get-Content read contents of file not folder. Please add . after your your folder path like below.
Get-Content "D:\Logs\*.*" | ?{($_|Select-String "test")}
If you want to go through all folders way under it then add -recurse like below:
Get-Content "D:\Logs\*.*" -Recurse | ?{($_|Select-String "test")}
Instead of this: (as per your comment)
foreach ($doc in $inpath) { do-function }
try this:
foreach ($doc in (gci $inpath)) { do-function }
You are doing a foreach on a string object instead of your folder items.

Trying to copy a group of files contained in a text file

I'm trying to copy a list of files from a txt file and as a newbie, I'm having a hard time.
Here is a bit of the text file. The real file has no extra lines, but I had to do that to :
"D:\Shared\Customer Care\Customer Care Common\Customers Contracted\Customers Contracted\Fred 44705"
"D:\Shared\Customer Care\Customer Care Common\Customers Contracted\Customers Contracted\Johnson 47227"
"D:\Shared\Customer Care\Customer Care Common\Customers Contracted\Customers Contracted\Daniel 35434"
"D:\Shared\Customer Care\Customer Care Common\Customers Contracted\Customers Contracted\Frank, John 48273"
I've tried enclosing the filename string in double-quotes as well.
Here's the simple script I'm trying to use:
Get-Content c:\users\scripts\files-to-fix.txt | Foreach-Object {copy-item $_ d:\junk}
The error I'm getting is:
Copy-Item : Cannot find drive. A drive with the name ''D' does not
exist. At C:\users\mhyman\scripts\copyfiles.ps1:2 char:81
+ Get-Content c:\users\mhyman\scripts\files-to-fix.txt |
Foreach-Object {copy-item <<<< $_ d:\junk}
+ CategoryInfo : ObjectNotFound: ('D:String) [Copy-Item],
DriveNotFoundException
+ FullyQualifiedErrorId :
DriveNotFound,Microsoft.PowerShell.Commands.CopyItemCommand
I know this is simple, but I would really appreciate some help.
I think it is the surrounding quotes that are causing the problem ( as indicated by the error saying that a drive of name "D is not found. Try this:
get-content c:\users\scripts\files-to-fix.txt | %{ copy-item $_.trim('"') d:\junk}
Of course, if you can control the txt file, enter the list without the quotes.
By your tags and drive letters and backslashes it is clearly a Windows environment your working in and although I'm not a PowerShell scripter, I'm a better than most batch scipter and use a For / If conditioanla statement sicne it is shorter and you feed it your file instead of parsing out the file into reduudc commands on a line, so in your example:
for /F %%t in (the text file.txt) do copy /q %%t d:\junk
And then you go home and never worry about until the next morning
Does powershell have a runas ornative mode that can parse older, more proven and stable DOS commands ?