Powershell running a cmdlet from a called script - powershell

I have a script that calls another script (with arguments). The called script contains the Install-WindowsFeature cmdlet. When I run the script, the called script runs, but returns the following error:
Install-WindowsFeature : The term 'Install-WindowsFeature' 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
Of course, I can run the cmdlet just fine from a PowerShell console. I can also run the called script from a PowerShell console and Install-WindowsFeature works fine. So, is it something to do with calling a script from a script that runs a cmdlet? Here is my calling code:
$script = "C:\Path\script.ps1"
$argumentList = #()
$argumentlist += ("-Arg1", "value")
$argumentlist += ("-Arg2", "value")
$argumentlist += ("-Arg3", "value")
Invoke-Expression "$script $argumentList"
In the called script, I called Install-WindowsFeature as below:
if ($someValue) { Invoke-Command -ScriptBlock {Install-WindowsFeature -Name RSAT-AD-Tools} }
I've also tried it as below:
if ($someValue) { Install-WindowsFeature -Name RSAT-AD-Tools }
12/16/16 EDIT: This script is running in a GUI built with Sapien PowerShell Studio. When I change the build type to "Native" it works. I have to reset my lab to check, but I suspect it will also run if I just run it in the x64 context. This article explains why I think this matters.
Running on Windows Server 2012 R2

Unless you've got a compelling reason for it, let's see if we can clean up your calling pattern a bit - and hopefully make your other issues go away by avoiding the contortionism.
Rather than creating your parameter list as a string, take advantage of parameter splatting. It's good to get out of the habit of treating PowerShell like other scripting languages that don't work with objects.
$splat = #{
Arg1 = "value1";
Arg2 = "value2";
Arg3 = "value3"
}
& c:\path\script.ps1 #splat
Using that on a script.ps1 something like this:
param(
$Arg1,
$Arg2,
$Arg3
)
Write-Host "Arg1 = $Arg1, Arg2 = $Arg2, Arg3 = $Arg3
You'll get an expected output of:
Arg1 = value1, Arg2 = value2, Arg3 = value3
Once you've got that, there's probably no reason to use Invoke-Command on the call to Install-WindowsFeature, unless you're leaving out details like invoking remotely to a server. Invoke-Command { Install-WindowsFeature } still works fine for me on Server 2012R2 with PowerShell 5, and there's no reason it shouldn't.
This assumes you're running this script on a Server that support Install-WindowsFeature, as the other comments point out. Client Windows doesn't support Install-WindowsFeature, and the RSAT tools are installed via a stand-alone RSAT .MSU package, which you manage differently.
Install-WindowsFeature is natively provided with Server Manager on Server 2012R2 - there's no need to Import-Module... unless you've done something to your profile or fouled up your modules folders. One of the earlier versions of Windows Server needed it - but that was a couple versions back. Likewise, Add-WindowsFeature was the old name - and it's still available as an alias for Install-WindowsFeature.
I'm assuming you've tried Install-WindowsFeature directly from the command line to ensure it's in working order, and Get-Module Install-WindowsFeature looks reasonable.
PS C:\Windows\system32> get-module ServerManager
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 2.0.0.0 ServerManager {Get-WindowsFeature, Install-WindowsFeature, Uninstall-Win...
While we're on the topic, there's very little reason to drop to DISM on Server that supports Install-WindowsFeature, and a number of reasons not to.
Server Manager and several other tools (including Win32_ServerFeature) rely on the feature states parsed and understood by the WMI provider used by Install-WindowsFeature. It's possible to enable the right set of features using DISM, but needs attention and detail. Enabling only "part" of a role and feature may get the functionality you want for specific cases, but the role or feature may not show up as installed in Get-WindowsFeature, may not be uninstallable via Remove-WindowsFeature, and may not offer relevant UI features in Server Manager like monitoring health of the role, viewing relevant events, or offering tools for administering it.
Install-WindowsFeature integrates with additional code from the role & features you're installing, and may run additional health and pre-requisite checks to ensure your correctly configured.
DISM featurenames tend to change more often than the role & feature name of Server Manager, so your script portability will be better.
There are other points, but I won't go into them since DISM was primarily a comment fork.

You are probably right, it seems like the script gets executed with x86 PowerShell. I want to share a snippet with you which I use for scripts that needs to run in a specific environment (e. g. x64 PowerShell).
The script restarts itself in a x64 PowerShell if its started as x86 process. Just put this at the top of your script:
# Reinvoke the script as x64 if its called from a x86 process.
if ($env:Processor_Architecture -eq "x86")
{
&"$env:windir\sysnative\WindowsPowerShell\v1.0\powershell.exe" -noprofile -file $myinvocation.Mycommand.path -executionpolicy bypass
exit
}
Also note that the Install-WindowsFeature cmdlet doesn't work on all windows versions so consider to use dism instead:
$featuresToInstall = #('RSAT-AD-Tools')
$dismParameter = #('/online', '/Enable-Feature', ($featuresToInstall | % { '/FeatureName:{0}' -f $_ }), '/NoRestart')
dism #dismParameter

The running environment determines what modules you have access to. When I created this project in PowerShell Studio, it took the default of x86 (a 32 bit environment). This worked but because I was running it on Windows Server 2012 R2 (a 64-bit environment) I did not have access to the powershell modules such as Import-Module and Install-WindowsFeature. Changing this to a x64 project resolved my issue.
In order to avoid this scenario, it is important to make sure you run PowerShell scripts and modules in the architecture that is native to the OS. You can do this by setting the project correctly (if using a tool like PowerShell Studio) or by verifying that you are running in the native mode for that OS with the code Martin Brandl provided.

Related

What is the purpose of ImportSystemModules?

What is the purpose of ImportSystemModules? Using help ImportSystemModules -Full produces an empty shell description.
PS 09:12 C:\src
>get-command *module*
CommandType Name Version Source
----------- ---- ------- ------
Function Find-Module 2.2.1 PowerShellGet
Function Get-InstalledModule 2.2.1 PowerShellGet
Function **ImportSystemModules**
Function InModuleScope 3.4.0 Pester
Function Install-Module 2.2.1 PowerShellGet
...
From Roger Lipscombe's blog, it used to do the following:
This runs Powershell.exe -ImportSystemModules, which, in turn, runs the ImportSystemModules command. You can call this command as part of your profile, if you want these modules loaded each time you run PowerShell.
Various places on the Internet state that it loads the available modules from C:\Windows\System32\WindowsPowerShell\v1.0\Modules. Among these are modules for managing IIS, Hyper-V, etc.
It turns out that it also loads snap-ins from C:\Users\rogerl\Documents\WindowsPowerShell\Snapins
However, according to another SO answer:
The -ImportSystemModules switch has no impact in v3, looks like it is going away.
And the Powershell changelog states for v6 beta 9 states the argument has been removed entirely:
Remove parameters -importsystemmodules and -psconsoleFile from powershell.exe. (#4995)
And indeed, running Get-Command ImportSystemModules on Powershell Core v6 cannot find the command anymore:
> Get-Command ImportSystemModules
Get-Command : The term 'ImportSystemModules' is not recognized as the name of a cmdlet, function, script file, or operable program.
As to why it's still there and defined in v5.1 and earlier? Perhaps it's for backwards compatibility, v2 compatibility was guaranteed for a long time.

Why does Chocolatey hang when using Powershell ISE without the `-y` switch?

When using PowerShell ISE with Chocolatey to install applications, if I forget the -y switch, it hangs waiting on some sort of "confirmation" that's not popping up anywhere?
I have to Ctrl+Alt+Del to kill PowerShell ISE and Chocolatey and it leaves things in half-way state.
This is what it looks like below:
In addition to the comments to the OP above, regarding PowerShell ISE not supporting (most) interactive console applications...
It is worth remembering that the REPL window in PowerShell_ISE.exe is not just some sort of docked PowerShell.exe console. Most of the time the user experience is the same, but this hides a number of differences:
https://blogs.msdn.microsoft.com/powershell/2009/04/17/differences-between-the-ise-and-powershell-console/
Both these executables are host applications that run a PowerShell runspace (engine). You can even write your own application that "hosts" PowerShell. It is the host application that determines the user experience.
PowerShell.org: The Shell vs The host
Spiceworks.com: The Shell vs The Host
Writing a Windows PowerShell Host
And finally, for the most curious:
How PowerShell works
I think I wrote this answer more for my own benefit; it's a useful refresher for me as I get asked this by colleagues every now and again...
It's simply because PoSH ISE is not a thing to use for user interactive .exe commands.
If you .exe or whatever expects a response, when in the ISE you have to provide it.
You can easily prove this is not a Chocolatey thing by trying any other .exe that kicks out interactive stuff. For example, just type:
nslookup in the script pane and F8 to run it, or type it in the console pane and hit enter
Either way, the console will just hang, waiting for a interactive response that you cannot provide.
You can still use interactive commands like nslookup in the PoSH ISE, but you have to provide all parameters. For example:
nslookup microsoft.com
nslookup -type=mx microsoft.com
nslookup -q=soa microsoft.com
PS 5.1 even kicks out an error message now.
nslookup
Cannot start "nslookup". Interactive console applications are not supported.
To run the application, use the Start-Process cmdlet or use "Start PowerShell.exe" from the File menu.
To view/modify the list of blocked console applications, use $psUnsupportedConsoleApplications, or consult online help.
At line:0 char:0
You can easily shell out to the PowerShell console host temporarily this way.
Here is a function I have in my profile for such efforts.
Function Start-ConsoleCommand
{
[CmdletBinding()]
[Alias('scc')]
Param
(
[string]$ConsoleCommand,
[switch]$PoSHCore
)
If ($PoSHCore)
{Start-Process pwsh -ArgumentList "-NoExit","-Command &{ $ConsoleCommand }" -Wait}
Else
{Start-Process powershell -ArgumentList "-NoExit","-Command &{ $ConsoleCommand }" -Wait}
}
So, just type
scc -ConsoleCommand choco install winmerge
It'll pop the console host and stay open until you close it.
Update
As per request of - Alex Kwitny
PoSHGet default has only two repositories,
nuget
PSGallery
but you can add your own or another.
You use the below cmdlets to make this happen.
I have not had to use Chocolatey in a while, but taking a quick look and my archives, the below is what I used
Set up chocolatey repository
Find-Module
Get-Module
Find-Package
Get-Package
Get-PackageProvider
Get-PackageSource
Get-PackageSource -Provider chocolatey
Register-PackageSource -Name chocolatey -Provider Chocolatey -Trusted -Location http://chocolatey.org/api/v2/ -Verbose
Find-Module
Get-Module
Find-Package
Get-Package

Local Powershell script to trigger VBS script inside Citrix instance

I am trying to get a local powershell script to trigger a VBS script inside a citrix instance. The events should be this:
Citrix Instance opening Windows Explorer ----> Network Path of script typed into the windows explorer session
I'm using the WfIcaLib.dll (ICOSDK) that came with the Citrix receiver install. Documentation PDF for the Citrix ICOSDK is available here
So this is the code I'm using, which works PERFECTLY in Powershell command line, but when I use the 32-bit ISE, it does nothing other than telling me the DLL has been loaded. I get no errors, but the Citrix Client never actually opens like it does when I run the same exact commands through Powershell command line.
#load Citrix ICA Client SDK
[System.Reflection.Assembly]::LoadFile("C:\Program Files (x86)\Citrix\ICA Client\WfIcaLib.dll")
$ICA = New-Object WFICALib.ICAClientClass
$ICA.Address = "***.***.***.***:****"
$ICA.Application = "Windows ExplorerFED6"
$ICA.Username = "******"
$ICA.Domain = "**"
$ICA.Launch = $true
$ICA.Outputmode = [WfIcaLib.OutputMode]::OutputModeNormal
$ICA.SetProp("Password", "*********")
$ICA.TWIMode=$true
$ICA.Connect()
Any ideas?
EDIT: SOLVED - after reopening under 32-bit ISE and getting code to work, I could not run the .ps1 file since it kept defaulting to 64-bit (even if using Open With on 32-bit powershell version). Running the script via command prompt or 32-bit powershell console both worked.
Using any method suggested by Mike Garuccio worked just fine. I will most likely end up using a Task Scheduler to run the script.
It looks like the problem is a versioning one, which you can deal with using start-job (was originally going to do this with a runspace but that's a lot more code for no real benefit). This will start a job to run your code under 32-bit powershell, it should still place any GUI elements or popups on screen but if you need to get any data out from the script later you'll need to receive the job. code would look something like below.
$SB = {
[System.Reflection.Assembly]::LoadFile("C:\Program Files (x86)\Citrix\ICA Client\WfIcaLib.dll")
$ICA = New-Object WFICALib.ICAClientClass
$ICA.Address = "***.***.***.***:****"
$ICA.Application = "Windows ExplorerFED6"
$ICA.Username = "******"
$ICA.Domain = "**"
$ICA.Launch = $true
$ICA.Outputmode = [WfIcaLib.OutputMode]::OutputModeNormal
$ICA.SetProp("Password", "*********")
$ICA.TWIMode=$true
$ICA.Connect()
}
Start-Job -ScriptBlock $SB -RunAs32
get-job | Receive-Job
Alternatively, you could also use a simple .bat file as a launcher with something like C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -file yourscript.ps1
or if you need everything to be wrapped in powershell and the job does not work you can use the solution Here that basically does the same thing but from within powershell (drop the if statement they use tho, just use the stuff contained inside it like below, with any changes you need to make wrt the profile and interactive settings.)
&"$env:windir\syswow64\windowspowershell\v1.0\powershell.exe" -noninteractive -noprofile -file "C:\Path o\script.ps1" -executionpolicy bypass
hope one of those works for you!

Powershell On Windows Server 2008 vs. Windows XP?

I'm writing a PowerShell script for a particular client. I tested the script on several of our desktop machines, and the tried the script on the client's system. I ran into the following issues:
Issue #1
As a good little Do Bee, I defined a bunch of help text, so when you run Get-Help on my script, it would give you detailed help. I used the following syntax:
<#
Get-Help
.SYNOPSIS
C> FileWatcher.ps1 -FilePath <FileName> -SenderEmail Bob#Foo.com ^
-ToEmail Joe#Bar.com -SmtpServer smtp.foo.com
.DESCRIPTION
Blah, blah, blah...
#>
On my machine, it works, and this is recognized as comment. However, on the client's machine, this produced an error as soon as it saw the C> which it thought was a redirect. Getting rid of the #> and <# and putting # in front of each line got rid of this problem, and brought us Issue #2.
Issue #2
I defined a bunch of parameters like this:
Param (
[ValidateScript({Test-Path $_ -PathType 'Leaf'})]
[Parameter(
Position=0,
HelpMessage="File you want to watch")]
$FilePath = "\\rke032\QuickCon\wincommlog.000",
[String]
[Parameter(
blah, blah, blah
PowerShell coughed on [ValidateScript({Test-Path $_ -PathType 'Leaf'})] saying it wasn't a valid type.
As I said, we tested this on a wide variety of Windows machines. I have a Windows XP machine. It's PowerShell version # 6.0.6002.1811. On another machine that's running Windows 7, the PowerShell version is 6.1.7600.
On the client's machine (a Windows 2008 Server) which is giving us these errors, the version is 6.0.6001.18000.
We ran the Powershell scripts by bringing up a PowerShell window, and then typing in the script's name. The ExecutionPolicy is set to Unrestricted. The script has a *.ps1 suffix on the end. I can't believe there's that big a difference between revision 6.0.6002 and 6.0.6001 to have a problem with unrecognized syntax. Is there something else going on?
Compare the output of $PSVersionTable and not the build version. In particular the PSVersion property is interesting. I guess you have PS1 on one machine and PS2 on another. The extension is .ps1 regardless of the PowerShell version.
This guess is reinforced by noticing that block comments don't work and neither do parameter attributes.

powershell v2 remote features?

Just listened to Hansellminutes podcast. He had a talk with two Microsoft PS developers. They mentioned PS V2 remoting features.
I have some scripts based on PS v1. In terms of remoting commands or executions, I installed PS on local and a remote machines. Then I use PsExec.exe to push bat on remote to execute PS scripts. Now I am thinking to take advantage of PS V2.
To simple questions I have, to get a list of files on local, I can use the following codes:
$fs = Get-Item -Path $Path | Where { !$_.PSIsContainer ... } # more constrains in ...
if ( $fs -ne $null )
{
# continue to work on each file in the collection
...
}
What is the equivalent command to get a collection of files from a remote? I prefer to get a similar collection of file objects back so that I can access to their properties.
The second question is how to exec a command on remote with external application? I tried to use WIM Process before, but I could not get WMI class working on a case of Windows 2008 server. Then I used PsExec.exe to push a bat to a remote to execute PS script. It works in the cases. However, the problem I have to install PS on the remote as well. I am going to working another remote. I'll try to avoid to install PS on the remote. Can I take PS V2 advantage to execute a command on a remote Windows? What's the new commands?
By the way, normally, I have to pass user name and pwd to a remote. I guess in PS I have to pass user/pwd as well.
You can either put your code above in a script file and invoke it on a remote computer using V2 remoting like so:
PS> Invoke-Command remotePCName -file c:\myscript.ps1
You will need to be running with admin privs (elevated if UAC enabled) in order to use remoting. The command above will copy the script to the remote machine, execute it and return deserialized objects. These objects are essentially property bags. They are not "live" objects and setting properties on them like IsReadOnly will not affect the remote file. If you want to set properties then do it in your script that executes on the remote PC.
The option if you have a little bit of script is to use a scriptblock like so:
PS> Invoke-Command remotePCName { Get-Item C:\*.txt | Where {$_.IsReadOnly }
You can execute native commands (EXE) on the remote computer in either script or a scriptblock. You only need to make sure the EXE is available on the remote PC.
Regarding credentials, if you're on a domain and you have admin privs on the remote computer you won't need to pass credentials as your default credentials should work. If you need to run as a specific user then use the -Credential parameter on Invoke-Command like so:
PS> $cred = Get-Credential
PS> icm remotePCName { gci c:\windows\system32 -r *.sys } -credential $cred
Regarding your last comment, no PowerShell will use Windows integrated security so you should not have to pass any username or password unless you wanted to run it as a different user.
If you haven't yet enabled PS remoting, every time I've tried I've had to actually turn off UAC while I was enabling remoting (then I could re-enable UAC once remoting was enabled). Running Enable-PSRemoting from an elevated command prompt was not enough and the error message was not at all useful.
EDIT: I've just confirmed in a fresh Windows 7 VM that this is not an issue. It could have been a beta issue that I am no longer experiencing as I've been using beta/rc/ctp of PowerShell and Windows 7 for a long time.