How does Powershell know where to find modules for import? - c#

I am really at beginner level for working with commandlets and powershell stuff. I am invoking commandlets from C# using PowerShell API. I saw strange behaviours. While on different threads on stackoverfow, people explicitly importing modules using Import-Command or PSImportModule method, I can see if there is an assembly at $env:PSModulePath available already, it is automatically loaded. Does this behaviour is by default or due to criteria configurations which i m overlooking. I am running unit test in ms test environment.
I am using following code.
System.Management.Automation.PowerShell _powerShellInstance
_powerShellInstance.AddCommand("Connect-AzureRmAccount", false);
var output = _powerShellInstance.Invoke();
// Invoking my commandlets
_powerShellInstance.AddCommand("Get-LinkParameter", false);
The above command automatically loads the assembly from C:\Windows\system32\WindowsPowerShellModules\v1.0\Modules\. I have not created any runspace and no configuration sets. Just above automatically loading things. I need to confirm how exactly is the powershell and run space behaves. Because I need to clear how i need to install my commandlets on production machine then. How unit tests on production machine will access my commandlets to perfectly get loaded.

While it is good practice to explicitly load the modules you want using Import-Module, since Powershell 3.0 if a module is available at one of the locations returned by $env:PSModulePath, it will automatically get loaded by default if one of its cmdlets are invoked. Below is a breakdown of the different paths:
User Modules
$modulePath = "${env:UserProfile}\Documents\WindowsPowerShell\Modules"
Modules installed here are only available to the current user's Powershell session, and by default modules installed using Install-Module are saved here.
All User Modules
$modulePath = "${env:ProgramFiles}\WindowsPowerShell\Modules"
Modules installed here are available to any users' Powershell session.
System Modules
$modulePath = "${env:SystemRoot}\system32\WindowsPowerShell\v1.0\Modules"
Modules installed here are available system wide to any Powershell session, but should be kept clean for Windows to manage. Typically, you do not want to install your own modules here.
Adding Additional Module Paths
You can add additional paths to $env:PSModulePath similarly to how you would modify the $env:PATH variable for resolving executable paths. It is simply a semicolon ; delimited string of directories where modules reside, and if a module is available at any path in $env:PSModulePath, Powershell will know where to find it. And in fact, you may see that other installed tools may have added their own paths to $env:PSModulePath. A few of examples of programs/toolsets which do this are Microsoft SQL Studio, Microsoft System Center - Operations Manager, and the Chef Development Kit.
Importing a Module Not On the Path
As far as I know, you cannot load a Powershell module that is not a part of $env:PSModulePath. However, you can temporarily edit $env:PSModulePath to contain a directory with a module you want to load. For example, if you wanted to import a module named TestModule from some arbitrary path:
$env:PSModulePath += ';C:\Path\To\Temporary\ModuleDirectory'
Import-Module TestModule
where TestModule exists as a sub-folder of C:\Path\To\Temporary\ModuleDirectory
You do not need to back out the module path change when you are ready to end your Powershell session as the change above is temporary. Consequently, you would need to modify $env:PSModulePath in every session, so if TestModule was something you wanted to have available at all times for use, you can either copy it to one of the other directories in $env:PSModulePath or permanently add C:\Path\To\Temporary\ModuleDirectory to the PSModulePath environment variable.
A Note About UNC Paths
You can also add UNC (network) paths to $env:PSModulePath. However, I believe any remote module scripts will still be subject to the Powershell ExecutionPolicy set on the system.
And a Bit More About Installing Modules
By default, Install-Module installs a module to the User Modules directory, but you can control this a bit with the -Scope parameter. For example, the following commands will change the location a module is installed into:
# Install to the current user location (default behavior if scope unspecified)
Install-Module -Scope CurrentUser $moduleName
# Install to the all users location (requires elevated permissions)
Install-Module -Scope AllUsers $moduleName
Unfortunately, those are the only two locations PowerShell will assist you installing your modules to. The System modules are critical to the operation of PowerShell and should not be modified by end users, and additional paths added to $env:PSModulePath are likely managed by software outside of PowerShell, typically by MSIs or other installers.
Additionally, if you write software that ships with one or more PowerShell modules it's good practice to have your installer add a new directory to the system's %PSModulePath% and drop the module there, rather than installing to the standard AllUsers or CurrentUser path, as these are really meant for the end user to manage at their whim. Have your software update process update the module in this case. This has the benefit of preventing accidental modification or removal of the module to an incompatible version.

Related

Is there a way to properly access the isnetworkdeployed property from a complied EXE (using Quest PowerGUI)?

I have a small PowerShell script wrapped in an exe (using Quest Power GUI). This exe is then deployed to a UNC path using mageUI.exe (i.e. through a 'ClickOnce' deployment).
Now, there is a namespace available to us:
System.Deployment.Application
This namespace allows us to figure out if the tool is network deployed + the originating download URL/UNC of the exe.
So I added the following lines in my PowerShell script (which was then compiled into an exe by PowerGUI)
# Line 1. Load the assembly
[System.Reflection.Assembly]::LoadWithPartialName("System.Deployment")
# Line 2. Utilise methods in the assembly. Below line will give either false or true, depending if the caller is deployed as a 'ClickOnce' app.
[System.Deployment.Application.ApplicationDeployment]::IsNetworkDeployed
After publishing this exe as a 'ClickOnce' application (using mageUI.exe), putting it on a network share, and then executing from some other server (which has access to previously said share), I still get the following output:
# Output of Line 1 (This signifies the assembly was loaded successfully)
GAC Version Location
--- ------- --------
True v4.0.30319 C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Deployment\v...
# Output of Line 2
False
Not sure what I'm doing wrong. The property IsNetworkDeployed (Line 2) should have returned true.
Seeing that there is no solution using PowerGUI (since the script is extracted into a temp folder during execution), I had to do the following:
1. Create a 'caller' / 'wrapper' executable using [PS2EXE](https://gallery.technet.microsoft.com/scriptcenter/PS2EXE-Convert-PowerShell-9e4e07f1)
2. This executable becomes the 'entry point' while deploying as a clickOnce application.
3. Since the 'wrapper' is executed 'in-memory', the deployment methods/properties from System.Deployment work (if it's deployed through clickOnce).
4. There is some logic written in the wrapper exe which calls the second (which contains the actual working) executable. Ex:
IF ISNETWORKDEPLOYED, THEN:
PARSE THE URL ARGS / PREPARE THE ARGS AND PASS IT TO THE SECOND EXECUTABLE (which was compiled using Quest PowerGUI previously)
I am open to any other solutions.
As it appears that the concerned script does actually runs locally (as suggested), which appears to be caused by the exe wrapping, Instead, you might want to browse (C:, CD \Path) to your exe location and use the current location:
(get-location).Path
General Note
I would reconsider the overall design knowing that this wrapper is probably (mis)used to cover sensitive information (as hardcoded passwords), called Security through obscurity. If that is indeed the case, a (legal) hacker will easily shoot a hole into this. There several ways to deal with sensitive information in scripts by e.g. limiting the shared credentials and/or using the credentials of the current user instead.

Windows installer forbid certain install locations

Have an msi file that is run by the user manually. They need to be able to choose the install directory in most cases however we need to forbid certain install locations. E.g. Installing it to the root directory C:\ will cause all kinds of problems, so we need to either overwrite that decision (i.e. overwrite C:\ with C:\Program Files (x86)\xxx) or pop up with an error. Is there some way I can enforce this?
The msi in question has custom actions already however there doesn't seem to be a way to edit the install location from there.
Alternatively, the msi in this case is wrapped up in a WiX bundle so if we can forbid certain directories from there that would also be good. Cannot find a way to do this either though (only know how to edit the default with <Variable Name="InstallFolder" ...>)
Only other solution I can think of would be rather horrible: make a separate application that selects a directory that then runs the installer with acceptable directory.
Can this be done either through an msi or a WiX Bundle?
I am using the "Visual Studio 2013 Installer Projects" extension to build the msi.
As a contrary view:
In general this is a bad idea. In most cases the correct answer will be to install the application code to the appropriate Program Files folder (64-bit or x86) and the data files to data locations and so on, and the user should get no choice. It is not clear to me that a choice is a good idea when (for example) the Windows Certification rules say that your code must go to the Program Files location, so just do it right. Users simply care that the installed application works correctly, and if it fails when installed to some locations then the answer is to either 1) Fix the application so that it works or 2) use Program Files and give the user no choice.
Also, if you are using Visual Studio Installer projects then you can't write custom actions to do this because they all run too late to change the install location. You seem to have discovered this already. But you CAN hide the browse folder dialog and install to the default correct location.
The other issue is that it's not clear how you would define an "allowed" location. If it's not C:\, then can it be D:\SomeOtherLocation? Can it be an attached USB drive? Can it be a network share such as \\Servername\share? A mapped drive to a network share? There are likely to be any number of chosen locations that will fail the install or the app when it runs, and I don't think there can be a useful list of what's allowed. On top of that, let's say you have a 32-bit install and the user chooses the native Program Files folder on a 64-bit system, then it won't even go there - it will be redirected to the Program Files(x86) location. Finally, it's not clear what you do in silent install mode assuming the user specifies a location on the command line, it fails your test, then the install silently fails (because silent means silent, and the install might be unattended).
In other words, just install to Program Files and have done with it.
Custom Action: This will be short. Will check back later. I can't say I have bothered implementing this recently, but a custom action can certainly inspect the installation location and abort the setup or halt it - if the path selected is found to not be satisfactory. It should also be noted that MSI actively resists installing directly to the root of C:\ and stuff like that due to the way the Directory table is implemented.
GUI: I guess one way would be to run a custom action when the user clicks the Next button in the setup's destination path customization dialog which then does "whatever you want" in terms of checking the path, and then reports any errors. This involves a DoAction event hooked up to the OK or Next button on the path customization dialog.
Silent Mode:You can also hook up the same custom action to run in silent mode (or another custom action calling the same path check function) - to account for the fact that an undesirable path could also be specified for a silent installation. In that case the custom action should abort the setup after writing into the log file, instead of reporting the path problem to the user - which is what you would do from the dialog event mentioned above - obviously.
Github: I do not have WiX code for you to implement this available on this computer. I would hit github.com and search for other projects that use WiX - you will probably find something quickly - no money for nothing and WiX for free.
Based on the users being able to manually install it (and hence using the UI sequence), it might be easier to:
In the InstallUISequence, sequence the LaunchCondition action to just before the ExecuteAction action.
Then in the LaunchCondition table, add a condition like so:
Condition:
TARGETDIR~<<"C:\Program Files\"
Description:
You must install to the Program Files folder
What we're saying in the condition is:
If the TARGETDIR starts with "C:\Program Files\" (therefore the user can install anywhere under this folder) continue with the install. Otherwise throw an error.
Rather then preventing certain locations, I'd probably just enforce the Program Files folder as a best practise.

Powershell script works in one .NET Solution, not another

I am using a C# library I found that runs some remote Powershell scripts for Outlook Live. As a proof-of-concept that it works, I made a unit test that I can run (and step through for debugging) which merely calls a public static method inside this library which behind the scenes opens a remote Powershell session and runs a script.
This works just great in one of our Solutions, but it does not work when I run it in another Solution, even though both Solutions have the same two projects and test classes in them.. One Solution gives me an exception
There were errors in loading the format data file:
Microsoft.PowerShell, , C:\Users\xxxx.xxxx\AppData\Local\Temp\tmp_8c84e626-4399-420b-b874-9feeb3b1e195_tjlxhzzr.rlr\tmp_8c84e626-4399-420b-b874-9feeb3b1e195_tjlxhzzr.rlr.format.ps1xml : File skipped because of the following validation exception: File C:\Users\xxxx.xxxx\AppData\Local\Temp\tmp_8c84e626-4399-420b-b874-9feeb3b1e195_tjlxhzzr.rlr\tmp_8c84e626-4399-420b-b874-9feeb3b1e195_tjlxhzzr.rlr.format.ps1xml cannot be loaded because the execution of scripts is disabled on this system. Please see "get-help about_signing" for more details..
One attempt I made was to modify the PSCommand inside the C# library to have Unrestricted set and that did not solve the problem. However, if I open up the x86 Powershell and run set-executionpolicy Unrestricted my test will run in both Solutions. Changing the execution policy in the x64 version of Powershell had no effect on either Solution.
Is there some type of setting for permissions that is specific to a Solution? Neither the Web.Config or Global.asax should matter since I'm not loading any pages, so I don't know what else would take effect since I'm running the same unit test in both solutions with the same test runner (Testdriven.Net).
Any ideas? Thanks.
Run as administrator usually sets hklm otherwise it is set in hkcu
on a 64bit OS you need to run Set-ExecutionPolicy for 32bit and 64bit PSH separately
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell
to check
cd "hklm:\Software\Microsoft\Powershell\1\ShellIds\Microsoft Powershell"
dir

How do you use the standard library in IronPython?

I'll prefix this question with: No, Setting IRONPYTHONPATH is not the answer.
Anyway...
I was planning on using IronPython as a replacement for Powershell for a project, but I've been stumped before I've even started.
The very first thing I tried to do was use os.path, resulting in:
Traceback (most recent call last):
File "<string>", line 1, in <module>
ImportError: No module named os
After messing around I finally discovered I could use the standard library by adding it manually to the path:
import sys
sys.path.append(r"C:\Program Files\IronPython 2.7\Lib")
import os
However, this is a daft idea. Hard coding the path to the python library inside my scripts is a 100% guaranteed way of making them not work at some point.
I discovered this almost immediately when I tried to use the script on a windows 7 machine and the path was slightly different ('Program Files (x86)').
So, a couple of questions here:
1) Why is it so hard to use the standard library? At the very least I would have thought the interactive prompt in VS and basic ipy.exe would have this.
2) How can I determine the directory that iron python is installed in regardless of the system I'm using? (IronPython installer setting a var perhaps?)
Just a note here; yes, I have seen some other posts saying "set your IRONPYTHONPATH". This in unhelpful. If I have a blank machine that means I have to:
1) Install IronPython
2) Run some crazy powershell script to search out where-ever-the-heck the standard library was installed and set a global IRONPYTHONPATH variable to it.
3) Run python scripts
I'm looking for a better way.
--
Edit:
The fact I'm using this to do powershell like things is basically irrelevant, but I'm trying to achieve something like:
import clr
from System.Management.Automation import RunspaceInvoke
import os
scriptRoot = os.getcwd()
runSpace = RunspaceInvoke()
cmdPath64 = os.join(scriptRoot, "..\java\...")
cmdPath32 = os.join(scriptRoot, "..\java\...")
proc = runSpace.Invoke("Get-WmiObject Win32_Processor ... ")
if proc.AddressWidth == 32:
runSpace.Invoke(cmdPath32)
else:
runSpace.Invoke(cmdPath64)
I find that for ensuring that everything works for non-developer third parties, it's usually better to use pyc.py to create DLL's and and executable. I routinely create a DLL of the python standard modules and reference that in code. See my previous answer at this question IronPython: EXE compiled using pyc.py cannot import module "os"
It's a bit workaroundish but, given that the LIB directory of ironpython is installed under the x86 program files folder in 64bit systems and on the usual program files path on 32bit systems, you could do in this way:
import sys
import System
if System.IntPtr.Size * 8 == 32: # detect if we are running on 32bit process
sys.path.append(System.Environment.GetEnvironmentVariable("ProgramFiles") + "\IronPython 2.7\Lib")
else:
sys.path.append(System.Environment.GetEnvironmentVariable("ProgramFiles(x86)") + "\IronPython 2.7\Lib")
import os # it works !!
Here we use %ProgramFiles% and %ProgramFiles(x86)% to determine the path where IronPython is installed.
Quoting wikipedia about %ProgramFiles% variable (link):
%ProgramFiles%
This variable points to Program Files directory, which stores all the
installed program of Windows and others. The default on
English-language systems is C:\Program Files. In 64-bit editions of
Windows (XP, 2003, Vista), there are also %ProgramFiles(x86)% which
defaults to C:\Program Files (x86) and %ProgramW6432% which defaults
to C:\Program Files. The %ProgramFiles% itself depends on whether the
process requesting the environment variable is itself 32-bit or 64-bit
(this is caused by Windows-on-Windows 64-bit redirection).
This is very odd, because if you run the the IronPython installer, and then run C:\Program Files\IronPython 2.7\ipy.exe or C:\Program Files (x86)\IronPython 2.7\ipy.exe, you shouldn't need to do anything to have the stdlib available.
My guess is that you have more than one IronPython and you're running the wrong one, but only because I can't think of another reason this would happen. It's supposed to Just Work.

making software setup package issue

I am making software setup package, and previously I am using Inno Setup, and it works very good.
The current issue I met with Inno setup is, it does not support all languages for the setup UI, for example Simplified Chinese.
The setup project of VSTS 2008 supports almost all languages, but it does not support invoke another installer from the current installer to let end user install dependent software packages.
My program to publish is for Windows platform (Vista and XP), written in C# + VSTS 2008 + .Net 2.0.
Any advice for my problem?
thanks in advance,
George
As one of the comments to your question suggests, you may want to simply integrate the required language into your Inno Setup. You do that by adding the Languages section:
[Languages]
Name: "en"; MessagesFile: "compiler:Default.isl"
Name: "nl"; MessagesFile: "compiler:Languages\Dutch.isl"
This allows the UI to be displayed both in Englisch and Dutch. Other translations can be added accordingly.
The fact that Windows Installer does not allow "nested installations" (running an MSI from an MSI) can be annoying. You might, however, consider packaging the MSI installers into an UI-less (= silent) Inno Setup and have Inno Setup run the MSIs one by one.
EDIT
This shows you how you may run the EXE files to install your dependencies. Please note that they might be installed after your software. If it is required that they are installed before your software, you may need to code a little Pascal Script - this is explained in the help files.
[Files]
DestDir: {tmp}; Source: .\Files\sample.exe; Flags: deleteafterinstall;
[Run]
Filename: {tmp}\sample.exe; StatusMsg: Installing prerequisite
This includes file .\Files\sample.exe into the setup, copies it to the TEMP folder upon installation and removes it after the setup is done. Then, after copying your files, it runs TEMP\sample.exe and waits for it to finish.
EDIT 2
Regarding the OP's comment on the order of the items in the [Run] section:
There are two possible cases:
You're using Inno Setup to perform the actual installation of your software (copying files, registry entries, etc.) and additionally need to run the installers for the prerequisites.
You've got a separate installer for your software as well, and just need Inno Setup to run the installers for the prerequisites AND your software.
For case 1:
You do not need to put the your EXE file into the [Run] section at all, except you'd like to allow the user to start your application after setup as seen in many setups using a checkbox ("Run XYZ now?"). In that case, use the following line for your EXE:
Filename: {app}\yourprogram.exe; StatusMsg: Run the application; Flags: postinstall skipifsilent unchecked; Description: Run the application now
For case 2:
I'd order the entries in the [Run] section according to their dependencies. That is: first entry is the one that some others depend upon, last entry is your application setup. But I'm not sure about the order in which the entries are handled.
This might be answered in the docs for the [Run] section. When in doubt, try asking Jordan Russel (Inno Setup's author) for advice - he's a nice guy and when I last mailed him he was pretty quick replying.

Categories

Resources