What exactly does the Resolve-Path cmdlet do? - powershell

I've started working with Powershell lately on my Windows 10 system. I've created a profile file, and I wanted to put in a variable which links to a folder in my documents section. However, I didn't want to hard-code the path because I knew I'd need to go in and re-write it if my profile name got changed in a hard drive transfer like it did the last time I sent it in for repairs. So my first thought was to put in something like this:
$Var = $(~\Documents\Folder)
But that spat back an error. Then I learned of the Resolve-Path Cmdlet, which appeared at a glance to be what I needed. However, when I did this:
$Var = $(Resolve-Path ~\Documents\Folder)
... I got this:
>$Var
Path
----
C:\Users\Username\Documents\Folder
>
Which seemed like a problem. However, when I tried to cd $Var, it worked successfully, which confused me greatly. I figured that the extraneous Path header in the result would cause an error.
What exactly does Resolve-Path do, and why does it still get interpreted correctly when passed into cd? And additionally, is there any way to make Resolve-Path not include the extraneous information and return only the expanded path?

The reason you are seeing the header Path is because Resolve-Path is returning an object of type System.Management.Automation.PathInfo and when this is not captured into a variable it is outputted as a string to the console in a readable format IE:
ObjProperty1 ObjProperty2 ObjProperty3 ...
--------- --------- --------- ...
Value1 Value2 Value3 ...
This object works with cd as PowerShell is smart enough to parse the object ($var) before cd is run. PowerShell will return the value of the path property to cd meaning cd will see a string "C:\Users\Username\Documents\Folder" and not the object headers.
If you want to just return the path without the header use the Select cmdlet's -ExpandProperty parameter:
$var = Resolve-Path '~\Documents\Folder' | select -ExpandProperty Path
If you would like more information on Resolve-Path MSDN is a good place to start (link).

Easy Example for a Resolve-Path job,
Let's say you don't know the office version in your computer and want to get the correct path, Resolve-Path can help...
$TestPath = 'C:\Program Files (x86)\Microsoft Office\*\Winword.exe'
$Path = (Resolve-Path $TestPath).Path

Resolve-Path is a good way to find a nested file or folder with wildcards in the middle of the folder path (not just in the path's Leaf).
Best use case I've seen is, when a restore request comes into the SD and the claim is that an entire folder disappeared. Someone probably moved the folder into (under) an adjacent folder in their GUI. Easily find that folder like this:
PS> Resolve-Path 'folderOldLocation\*\folderName'
PS> Resolve-Path 'folderOldLocation\*\*\folderName'
PS> # Going deeper is unlikely to find it
PS> # and will take much longer to return
It's a very quick way to check for a quick fix to an otherwise time-consuming restore request.

Related

Choose which CSV to import when running a PowerShell script

I get a CSV every week that our finance team puts in a shared drive. I have a script for that CSV that I run once I get it.
The first command of the script is of course Import-Csv.
The problem is, the finance team insists on naming the file differently each time plus they don't always put it in the same location within the drive.
As a result, I have to first hunt for the file, put it into the directory that the script points to and then rename the file.
I've tried talking to the team about putting it in the same location and making sure the filename is the same but they only follow the instructions for a couple of weeks before just doing whatever.
Ideally, I'd like for it so that when I run the script, there would be a popup that would ask me to pick a CSV (Similar to how it looks when you do "Save As" on an Office Document).
Anyway for this to be done within PowerShell?
You can access .Net classes and interface with the forms library to instantiate and take input from the standard FileOpen dialog. Something like below:
Using Namespace System.Windows.Forms
$FileBrowser = [OpenFileDialog]::new()
$FileBrowser.InitialDirectory = 'c:\temp'
$FileBrowser.Filter = 'Comma Separated Values (*.csv) | *.csv'
[Void]$FileBrowser.ShowDialog()
$CsvFile = $FileBrowser.FileName
Then use $CsvFile int he Import-Csv command.
You can change the .InitialDirectory property to make navigating a little more convenient.
Use the .Filter property to limit the file open display to CSV files, to make things that much more convenient.
Also, use the [Void] class to prevent the status return (usually 'OK' or 'Cancel') from echoing to the screen.
Note: A simple Google search will turn up many examples. I refined some of the work from here. That will also document some of the other properties if you want to explore etc.
If you are willing to settle for a selection box that doesn't look as nice as the Save As dialog, you can use Out-Gridview. Something along these lines might help.
$filenames =
#(Get-ChildItem -Path C:\temp -Recurse -Filter *.csv |
Sort-Object LastWriteTime -Descending |
Out-GridView -Title 'Choose a file' -PassThru)
$csvfile = $filenames[0].FullName
Import-Csv $csvfile | More
The -Path specifies a directory that contains all the locations where your csv file might be delivered. The sort is just to put the recently written files at the top of the grid. This supposedly makes selection easier. The #() wrapper merely makes sure the result stored in $filenames is an array.
You would do something else with the results of Import-Csv.
Steven's response certainly satisfies your original question, but an alternative would be to let PowerShell do the work. If you know the drive, and you know the name of the file this week, you can pass the name to your script and let it search the drive filtering on the specific csv file you need. Make it recursive, and open the only file that matches. Sorry, didn't have time yesterday to include code. Here's a function that returns the full file path when provided with a top level search path and a filename with possible wildcards.
function gfp { $result=gci $args[0] -recurse -include $args[1]; return ($result.DirectoryName + "\" + $result.Name) }
Example: gfp "d:\rootfolder" "thisweeksfilename.csv"

Powershell: Cannot read a specific Registry-Value

I am struggling to read this REG-value via Powershell 5:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\SWD\DAFWSDProvider\urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/uri:urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/01]
"Driver"="{6bdd1fc6-810f-11d0-bec7-08002be2092f}\\0000"
Even the autocomplete-function in Powershell showing me the REG-path to that key is not working properly.
Why is it failing? How can I get this value?
This is the code which is surprisingly NOT working as expected:
$sub = 'urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/uri:urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/01'
get-Item -literalPath "HKLM:\SYSTEM\CurrentControlSet\Enum\SWD\DAFWSDProvider\$sub"
Here a screenshot of the subkey that I cannot read:
I could now drill it down to this situation:
subkey 'urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42' -> OK
subkey 'uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/u' -> OK
subkey 'urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/u' -> fail!
subkey 'urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/u' under HKLM:\Software -> OK
Using Sysinternals Process Explorer, I've discovered what happens.
PowerShell replaces the forward slashes in the path unconditionally with backslashes, even when you use -LiteralPath.
That's clearly a bug.
To work around it, you can use the PSPath of the registry key, apparently PowerShell leaves those alone. For the local registry, the PSPath always starts like this:
Microsoft.PowerShell.Core\Registry::
and after that goes on with the regular key name as it would appear in RegEdit.
$path = "Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\SWD\DAFWSDProvider\urn:uuid:e3248000-80ce-11db-8000-30055c83410f/uri:e3248000-80ce-11db-8000-30055c83410f/PrinterService"
Get-Item $path
PSPaths are an integral part of anything that Powershell treats as one of its drives. You can select them, or access the .PSPath property:
$path = "Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\SWD\DAFWSDProvider"
Get-ChildItem $path | Select -ExpandProperty PSPath
(Get-Item C:\).PSPath
At the end it turns out, that I had to use a different Syntax for the REG-Path to make the call work - very strange!
See this code:
$prefix1 = "Registry::HKEY_LOCAL_MACHINE"
$prefix2 = "HKLM:"
$subDir = "urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/uri:urn:uuid:cfe92100-67c4-11d4-a45f-0026abfabc42/01"
get-item "$prefix1\SYSTEM\CurrentControlSet\Enum\SWD\DAFWSDProvider\$subDir"
get-item "$prefix2\SYSTEM\CurrentControlSet\Enum\SWD\DAFWSDProvider\$subDir"
The first "get-item" call using prefix1 is working fine while the second one is not returning anything back.
Lession learned: Better use the longer REG-Prefix like in the original PSPATH to avoid any unexpected side-effects.
From Microsoft's PowerShell documentation, you can decide if you would like to view the entries as a list or to retrieve a single registry key.
https://learn.microsoft.com/en-us/powershell/scripting/samples/working-with-registry-entries?view=powershell-7.1

How to return full path to both folders AND files?

I have been tasked with auditing a file server at work, to review directory and file ownership, and subsequent meta data. I have scowered the internet to enable me to string together a script that returns the data required. The script I have created so far is as follows:
Get-ChildItem -r -Path '\\SERVERNAME.wan.net\launshared\Training' |
Select DirectoryName, UNC_Path_Parent_DIR, Name, Type, Extension,
#{N='Owner';E={$_.GetAccessControl().owner}},
#{N='SizeInKb';E={$_.Length/1kb}}, CreationTime, LastAccessTime,
LastWriteTime |
epcsv 'C:\Users\USERNAME\Desktop\Information Management\TrainingTEST - 2018- SCRIPT_RESULT.xls' -En UTF8 -NoType -Delim ','
Please ignore the UNC_Path_Parent_DIR entry, this simply inserts a field header with no returned data in the exported CSV file.
The script works fine, albeit with one exception...it does not return the full UNC path for the directories! Please note, the full UNC path is returned for the files but NOT the directories!
I have searched the internet for a solution, enquired with our IT guys, however cannot for the life of me locate a solution that will list the full UNC path to both directories AND files.
The UNC path is not a property of the objects coming from Get-ChildItem. What this means is that it needs to be added manually. One can do this, just like you did, by adding a custom property with an expression in Select-Object.
Please keep in mind that when others read your code it's always nicer to format things and use the full parameter name. It makes it easier to read and understand, also for you when you read it again in 10 years time ;)
I'll help you on your way by letting you know that you can find the code that gets the UNC path here and here. The only thing left is to implement it in the correct place within the Select-Object. I leave that as an exercise for you:
Get-ChildItem -Recurse -Path '\\fsabbey12.wan.net\launshared\Training' | Select-Object FullName, DirectoryName,
Name, Type, Extension,
#{N='UNC';E= {
# Code to get the UNC Path
}},
#{N='Owner';E= {$_.GetAccessControl().owner}},
#{N='SizeInKb';E={$_.Length/1kb}},
CreationTime, LastAccessTime, LastWriteTime
I think this will get you started.
I'll leave it here too as additional possible way (although #DarkLite1 answer covers the question quite fully).
It's possible that FullName will fulfill your needs without any custom expression.
If you use Get-ChildItem like this:
Get-ChildItem -Path \\servername.domain.com\C$\foldername\
Then using | Select FullName will give you path in the UNC format:
\\servername.domain.com\C$\foldername\\FirstSubfolder
Keep in mind that it applies only to the situation when you reference the remote folder, not folder from the machine you run the script on. If you use FullName on local folder you'll get format like this:
C:\FolderName\FirstSubfolder

Get all references to a given PowerShell module

Is there a way to find a list of script files that reference a given module (.psm1)? In other words, get all files that, in the script code, use at least 1 of the cmdlets defined in the module.
Obviously because of PowerShell 3.0 and above, most of my script files don't have an explicit Import-Module MODULE_NAME in the code somewhere, so I can't use that text to search on.
I know I can use Get-ChildItem -Path '...' -Recurse | Select-String 'TextToSearchFor' to search for a particular string inside of files, but that's not the same as searching for any reference to any cmdlet of a module. I could do a search for every single cmdlet in my module, but I was wondering if there is a better way.
Clarification: I'm only looking inside of a controlled environment where I have all the scripts in one file location.
Depending on the scenario, the callstack could be interesting to play around with. In that case you need to modify the functions which you want to find out about to gather information about the callstack at runtime and log it somewhere. Over time you might have enough logs to make some good assumptions.
function yourfunction {
$stack = Get-PSCallStack
if ($stack.Count -gt 1) {
$stack[1] # log this to a file or whatever you need
}
}
This might not work at all in your scenario, but I thought I throw it in there as an option.

Change path separator in Windows PowerShell

Is it possible to get PowerShell to always output / instead of \? For example, I'd like the output of get-location to be C:/Documents and Settings/Administrator.
Update
Thanks for the examples of using replace, but I was hoping for this to happen globally (e.g. tab completion, etc.). Based on Matt's observation that the separator is defined by System.IO.Path.DirectorySeparatorChar which appears in practice and from the documentation to be read-only, I'm guessing this isn't possible.
It's a good question. The underlying .NET framework surfaces this as System.IO.Path.DirectorySeparatorChar, and it's a read/write property, so I figured you could do this:
[IO.Path]::DirectorySeparatorChar = '/'
... and that appears to succeed, except if you then type this:
[IO.Path]::DirectorySeparatorChar
... it tells you that it's still '\'. It's like it's not "taking hold". Heck, I'm not even sure that PowerShell honours that particular value even if it was changing.
I thought I'd post this (at the risk of it not actually answering your question) in case it helps someone else find the real answer. I'm sure it would be something to do with that DirectorySeparatorChar field.
Replace "\" with "/".
PS C:\Users\dance2die> $path = "C:\Documents and Settings\Administrator"
PS C:\Users\dance2die> $path.Replace("\", "/")
C:/Documents and Settings/Administrator
You could create a filter (or function) that you can pipe your paths to:
PS C:\> filter replace-slash {$_ -replace "\\", "/"}
PS C:\> Get-Location | replace-slash
C:/