Powershell: Get the default directory from inside a cmdlet - powershell

I'm writing a powershell cmdlet. From inside of my cmdlet BeginProcessing() method, I want to be able to retrieve the the directory that was the default directory at the time the cmdlet was invoked.
Example:
If the user does this:
cd \myDirectory
invoke-mycmdlet
I want for my code to know that the default shell directory was c:\myDirectory.
When I access Environment.CurrentDirectory, it's always c:\windows\system32
I've seen a similar post on SO where the poster needed to set Environment::Current directory from inside the shell using get-location. That won't work for me.
Basically, my cmdlet does some file system stuff, and I want the user to be able to just cd\ into a directory, and execute my cmdlet, with it operating on the directory that they switched into -- just like you would expect it to work from the old Command Console.

You might want to try this instead, CurrentLocation.Path could also point to other provider paths, such as the registery.
this.SessionState.Path.CurrentFileSystemLocation.Path

You know, I always seem to find it right after I post -- regardless of how long I spent looking before reaching for SO!
So, my cmdlet inherits from PsCmdlet. I found that I could get the path I wanted from
this.SessionState.Path.CurrentLocation.Path
(where "this" is a cmdlet class that inherits from PsCmdlet)

Try the Get-Location cmdlet. It should be the script's current executing location, rather than the powershell host startup folder.

Related

Write to Profile File After Installing PowerShell Module with PowerShellGet

I have a custom PowerShell module with two cmdlets. I have it successfully, but manually, deployed on my machine. However, I deployed it by placing the binary file and module manifest in a location, and then registering the module. I also had to manually write an Import-Module command into my 'all users' profile.
Now I am sure I can deploy this module with Publish-Module, but how do I get the Install-Module to write the Import-Module statement to the profile file?
As of PowerShell 3.0, a module is automatically imported when a command from the module is invoked. This was a brilliant on Microsoft's part; however, it did require that modules are located in a location where PowerShell looks for modules by default. Makes sense. You can see those locations by running the following command:
$env:PSModulePath -split ';'
Is there a reason you'd rather not use one of the paths stored in the above environmental variable? That said, I'd keep your code out of the "C:\Windows\System32..." path. The other options are better: "C:\Program Files\PowerShell\Modules" (AllUsers) and "C:\Users\tommymaynard\Documents\PowerShell\Modules" (CurrentUser). Depending on your PowerShell version/OS, those path could be different. You won't need to write an Import-Module command into a $PROFILE script if you get the module into a preferred location. Maybe you already know this, but maybe not.
You're not going to get Install-Module to write to any of the $PROFILE scripts.
$PROFILE | Select-Object -Property *
Well, not by default anyway. You could write your own Install-Module function, that runs PowerShellGet's Install-Module function, and includes writing to various $PROFILE scripts. The problem is that you'll need to include logic so you don't blow away the contents of someone's $PROFILE script if it's not empty, and only append to it.
Seriously though, this is turning into a lot of work, when you could drop the module into a location where PowerShell can find it on its own.
Edit: It just occurred to me, you can add a value/path to the $env:PSModulePath environmental variable. It's a single string with semi-colon delimiters:
$env:PSModulePath.GetType().Name
Therefore, it'd look like this:
$env:PSModulePath += ';C:\Another\Path'
That's great and all, but again how might you stage this, right? It takes you back to the write-to-all-the-$PROFILE-scripts problem,... although you may be able to update the variable via Group Policy Preferences. Again, probably better to just relocate your module.

Get-Service issue with powershell

For some reason, when I run Get-Service in powershell, it throws an error. If I run Set-Service first however, Get-Service will work until I open powershell again. I can't seem to find this issue anywhere online. Has anyone experienced this and if so, is there a solution?
Error: "Program 'Get-Service' failed to run: No application is associated with the specified file for this operationAt line:1 char:1"
tl;dr
Remove file C:\Windows\System32\Get-Service from your system - it shouldn't be there.
After that, Get-Service will start working again.
PowerShell tries to open the perhaps accidentally created, extension-less C:\Windows\System32\Get-Service file on your system first, if you haven't imported module Microsoft.PowerShell.Management yet in the session.
That PowerShell tries to open a non-executable file - one without a filename extension - that it locates via $env:PATH is in itself problematic - see this answer.
The Microsoft.PowerShell.Management module, which contains the Get-Service and Set-Service cmdlets (among other commands), is normally imported on demand the first time you try to execute one of its commands in a session.
Technically, since the module is located in a known location, it is subject to module auto-loading.
However, if the module isn't imported yet, an external program / document file of the same name located in one of the directories listed in environment variable $env:PATH takes precedence, and it is executed / opened by default.
Since the file in your case was had no filename extension, the Windows shell did not know how to open it, resulting in the error message you saw (if you double-clicked the file in File Explorer, you'd get "How do you want to open this file?" dialog instead).
As a result, trying to execute a command named Get-Service by itself does not trigger import of the Microsoft.PowerShell.Management module - the external file keeps getting called.
By contrast, since the Set-Service cmdlet is not shadowed by an external file, calling it does implicitly import the module.
Once the module has been imported, submitting command Get-Service calls the cmdlet rather than the external file, because PowerShell's regular command precedence then kicks in, where cmdlets take precedence over external programs with the same name.
While this situational distinction makes the execution behavior hard to predict, consistently giving precedence to known cmdlets, irrespective of whether their module happens to have been imported into the current session yet or not, is not an option for performance reasons: see the discussion in since-closed GitHub issue #10853.
To unambiguously invoke a command from a given module, prefix the command name with the module name followed by \:
That is, even without the module imported, you could have invoked the Get-Service cmdlet as follows:
Microsoft.PowerShell.Management\Get-Service

Get-Process : A positional parameter cannot be found that accepts argument 'Get-ChildItem'

I am not a scripter at all. Someone else had created this script for me and it has previously worked. The only thing that has changed is the drive letter (which I did change in the script - it is currently drive E). But it is not working now. All it is supposed to do is pull back a list of files in a specified folder and save it as a text file in that directory; in this case, it's my karaoke song collection.
When I run the script now, I get:
Get-Process : A positional parameter cannot be found that accepts argument Get-ChildItem.
Here is the original script:
PS C:\Users\Tina> Get-ChildItem "F:\My Music\Karaoke\*.*" | Set-Content "F:\My Music\Karaoke\test.txt"
I'd like to make it so that it just pulls back all .mp3's, if that's possible, too. Thanks in advance for your help!
Since you appear to be copying and pasting this to the command line I will assume there was a typo that caused this issue. After a couple of quick tests to try and guess what the accident was I was unable to replicate exactly. Not being a scripter might make this harder but I recommend saving this code to a ps1 file so that you can just double click on it.
Get-ChildItem "F:\My Music\Karaoke\*.mp3" | Set-Content "F:\My Music\Karaoke\test.txt"
Warning
In order for the this file to work for you you have to allow PowerShell to execute it. If you run the shell as administrator once and run this code
Set-ExecutionPolicy remotesigned
It will allow your script to run. Keep in mind this is a site for scripters to get help. You should expect answers like this.

Running a cmd in powershell referencing a UNC path

I’m in the process of creating a powershell script to check OU users against users already configured for file share archiving but I’ve hit a stumbling block. I can query AD to get a list of users per OU and their home directories, dumping all of the details out to text files for logs and basing subsequent queries on. Once I have these details I try to run a dos command, (Enterprise Vault) Archivepoints.exe passing variables to it. The command would usually be :
Archivepoints.exe find \\fopserver045v\ouone_users$
When I try to run the following code I get an error.
$app="D:\Enterprise Vault\ArchivePoints.exe"
$EVArg = "find"
$VolLine = "\\fopserver045v\ouone_users_r$"
Invoke-Item "$app $EVArg $VolLine"
Invoke-Item : Cannot find path 'D:\Enterprise Vault\ArchivePoints.exe find \fopserver045v\ouone_users_r$' because it does not exist.
At first I thought it was missing the first backslash of the UNC path that was causing the issue but I'm no longer sure.
The script and command run on the EV server and the UNC bath doesn't actually go to the server, it's only a reference path within EV so it's not a credentials issue.
I need to be able to log the output to file too if possible.
What should the code look like and should I be using invoke-command or Invoke-Expression instead ?
Thanks
Don't use Invoke-Item. External commands should be run using the call operator (&). You can use splatting for the argument list.
$app="D:\Enterprise Vault\ArchivePoints.exe"
$arguments = "find", "\\fopserver045v\ouone_users_r$"
& $app #arguments

Why won't this powershell script accept parameters?

myscript.ps1
Param(
[Parameter(Mandatory=$true,Position=1)]
[string]$computerName
)
echo "arg0: " $computerName
CMD.exe
C:\> myscript.ps1 -computerName hey
Output:
cmdlet myscript.ps1 at command pipeline position 1
Supply values for the following parameters:
computerName: ddd
arg0:
ddd
I'm simply trying to work with Powershell parameters in CMD, and I can't seem to get a script to take one. I see sites saying to precede the script with .\ but that doesn't help. I added the mandatory line to see if Powershell was reading a parameter or not, and it's clearly not. The parameter computerName is obviously the word "hey". The Param block is the very first thing in the script. Powershell appears to recognize a parameter computerName, but no matter how I enter the command, it never thinks I'm actually entering parameter.
What the heck's wrong with my syntax?
By default, Powershell will not run scripts that it just happens to find in your current directory. This is intended by Microsoft as a security feature, and I believe that it mimics behavior found in unix shells.
Powershell will run scripts that it finds in your search path. Your search path is stored in $env:path.
I suspect that you have a script named "myscript.ps1" in some other directory that is on your search path.
I have had this happen to me before. The symptom I saw was that the parameter list seemed different than what I had defined. Each script had a different parameter list, so the script bombed when I fed it a parameter list intended for the other script. My habit is to not rely on parameter position, so this problem was easy to find.
The addition of ".\" to the script ".\myscript.ps1" should force the shell to use the .ps1 file in your current directory. As a test, I would specify the full path to the file you are trying to execute (If there are spaces in the path, be sure to wrap the path in "quotes") or change it to some totally crazy name that won't be duplicated by some other file (like "crazyfishpants.ps1") and see if the shell still finds the file.
You can get into similar problems if you have a function ("Get-Foo") that is loaded out of a module or profile with the same name as a script file ("Get-Foo.ps1"). You may wind up running something other than what you intend.
Position values should be 0-based (0 for the first parameter). That said, I can't duplicate what you're seeing on either PowerShell 2.0 or 3.0.
Thank you all for your very informative responses. It looks like my question was slightly edited before I submitted it, in that the text leads you to believe that I was entering this command directly in Powershell.
I was actually running the command for the script in CMD, which totally explains why it was not passing parameters to the Powershell script. Whoever green-lighted my question probably changed C:\> to PS> thinking that I made a typo.
I assumed that if I could run the script straight from CMD, I could send parameters to it on CMD's command line, but apparently that's not the case. If I run the script in Powershell, it indeed works just fine, I'm now seeing.
My ultimate goal was to allow users to run the Powershell script from CMD. It's looking like I can make a batch file that accepts parameters, and then start powershell and send those parameters to the PS script. And so, in the batch file, I should do something like:
powershell -File C:\myscript.ps -computerName %1
This enigma was probably solved 100 times over on this site, and I apologize for the confusion. Thank you again, for your responses.