Create pure powershell Nuget module for PowerShellGet - powershell

What I want
I want to publish number of PowerShell scripts as Nuget package to be used on build systems.
I want to use PowerShellGet to do installation work for me and version management.
I don't want those scripts to be part of any Visual Studio solution, but as standalone scripts.
Usage scenario
On any system, with configured Nuget provider user executes:
Install-Module MyModule
From that moment all exports from that module permanently available for this user.
Also user can call that command again to update version of those scripts.
What I've done
You can find current state of package here: GitHub
I've added and configured Nuget provider to our local Nuget server
To do this call Get-PackageProvider -Name NuGet -ForceBootstrap and Set-PSRepository -Name My_Nuget_Repo -SourceLocation http://my-nuget/api -InstallationPolicy Trusted
Created proper module, which can be imported locally by Import-Module
Created and published Nuget package with that module
Problem
I can install that package by Install-Module cmdlet and I can see it later in Get-InstalledModule list.
But, no functions are available.
Also, no matter what, but Install-Module not calling any of scripts from my package:
Not calling ScriptsToProcess from MyModule.psd1
Not calling Install.ps1 from tools folder
Not calling Init.ps1 from tools folder
Cmdlets exported by module not available and module can't be imported by Import-Module
(Same package works properly when installed from Visual Studios Install-Package MyModule, scripts are called, PowerShell module is imported).
Investigation
Since PowerShellGet is based on OneGet it seems that problem is in Install-Package cmdlet (which is called inside Install-Module cmdlet).
When I'm executing Install-Package MyModule from Visual Studio Install.ps1 and Init.ps1 are called. But same command from pure PowerShell doing nothing.

After long reverse engineering I've found the root cause
Technical reason
Magical tag PSModule has to be added to <Tags> in nuspec file.
Real reason
You shouldn't create nuspec file and pack nuget package manually at all. Use Publish-Module cmdlet instead.
How to do it properly
I've updated powershellget-module GitHub with:
Example of minimal module which can be published
A way how to use local folder as Nuget feed
Publishing, installation and usage of that module
Reference script with no dependencies which does it all locally, so you can study it
Check it out.

Related

How can I ensure that a 3rd party module is loaded in my custom script module?

I have a custom script module and one of the functions depends on the AWSPowerShell module. I attempted to put Import-Module AWSPowerShell inside that function, but when this runs on the server it fails and says no valid module is found. If I try Import-Module AWSPowerShell from the command line on the same server running as the same user it works fine. There seems to be something wrong with calling Import-Module from inside another module.
I saw that NestedModules can be used to specify dependencies so I added NestedModules #('AWSPowerShell') to the module manifest and removed the Import-Module AWSPowerShell from the function that needs it.
Now the error about AWSPowerShell happens at the point where I import my own custom module from the calling script.
The exact error is: Import-Module : The module to process 'AWSPowerShell', listed in field 'NestedModules' of module manifest 'C:\Program Files\WindowsPowerShell\Modules\...MyModule.psd1' was not processed because no valid module was found in any module directory.
I did notice that the AWSPowerShell module is installed in a different folder path than my custom module. My module is in C:\Program Files\WindowsPowerShell\Modules and AWSPowerShell is in C:\Program Files (x86)\AWS Tools\PowerShell.
How can I set this up so that AWSPowerShell can be loaded for use inside my module?
Update
I made a copy of the AWSPowerShell module folder under C:\Program Files\WindowsPowerShell\Modules and now the module imports successfully using the NestedModules method. Everything I saw about NestedModules appeared to be for combining modules that were being developed for a bigger solution. I'm not sure about using this technique for importing a 3rd party module like this. I may be misusing this feature.
The NestedModule module-manifest (.psd1) entry is for modules that are bundled with your module and available in your module's code only.
RequiredModules is for importing independently pre-installed modules, which are imported into the global scope when your module is imported, which means that their commands are also available to the caller.
Note: This answer shows how to specify RequiredModules modules to be installed on demand by declaring them as external dependencies, but the limitation appears to be that both the module and its dependency must come from the same repository and must be installed with Install-Module.

Can a PowerShell module be installed using PowershellGet from a local dir or a zip served by HTTP server?

A Powershell module can be installed using the old community-provided PsGet module from a local directory or a zip from a remote HTTP server:
PSGet\Install-Module -ModulePath "C:\path\to\source\of\MyModule"
PsGet\Install-Module -ModuleUrl "http://path/to/MyModule.zip"
Is it possible to do the same with the PowershellGet module?
It seems PowershellGet can only install modules from NuGet repos.
Any suggestion is appreciated.
I tried this and the only way we got it to work was to create a temporary PowerShell repository on the local machine (using Register-PSRepository) and then use Install-Module (specifying your new repo) to install the cmdlet THEN clean up the temporary repository. Quite a faff.

PowerShell Gallery vs NuGet Gallery vs MSI vs Snap-Ins

I'm quite confused at the differences here and would appreciate some clarification.
What I think is that in PowerShell 1.0, there were Modules (commands written in PowerShell) and Snap-Ins (a dynamic link library that implements cmdlets and providers). However, the lines became blurred as Modules can contain DLLs that the Cmdlets and Functions in the module can call, so eventually Snap-Ins are now effectively redundant.
Then came NuGet packages, which seems to be some kind of package deployment system (build by Microsoft??). This is not PowerShell specific in the way that the PowerShell Gallery is, and NuGet packages can deploy any kind of functionality to a Windows system. Is it correct to think of NuGet as the successor of MSI as a package deployment method?
So, we have PowerShell Gallery (only for PowerShell modules) and NuGet (for anything, but also contains PowerShell packages). What is a Package and how is it defined differently from a Module in the context of PowerShell?
Then, I notice that PowerShell have *-Package and *-Module Cmdlets, so you can run:
find-module *7zip*
find-package *7zip*
The same four items are returned here, but where are these Cmdlets pointing? If you run install-module is the Module installed from the PowerShell Gallery, and if you run install-package is an equivalent package (that is not a Module, but a package, whatever that is!) installed from NuGet?
Any clarification on the above would be greatly appreciated as I'm finding it hard to find clear definitions of what all of these things are and how they interact with each other (and indeed, what is the best way for me to approach installing them and upkeep)?

Copying MSOnline PowerShell module as a workaround

I am in a tricky situation where I need to install the MSOnline PowerShell module on a PC and it needs to install NuGet first. Unfortunately, the company firewall does not trust the NuGet website, so the PowerShell cmdlets fail at installation.
I was wondering whether I could simply copy a directory containing the MSOnline module from one PC to my target PC. Is this a viable workaround? Or is there another workaround?
I understand that ideally, I should try to have the website whitelisted, but there is a question of timing here.
As exlpained in the comment of #TheMadTechnician you can copy the module from the $env:PSMdulePath the your requested target machines.
If you've multiple machines for module distribtion you can setup your own private Powershell gallery via a network share as explained here (if that's possible in your company). Based on that multiple machines can use the Get-PSRepository, Find-Module -Repository MyRepository, Install-Module -Name MyModule -Repository MyRepository ,.. cmdlets for module administration. I'm using this a private Powershell gallery to distribute some Git tools about multiple dev-machines. You could also publish own Powershell modules in this gallery, in the background NuGet is used. So your machine with access to NuGet could store the newest module via Save-Module (maybe via a background job) and re-publish it to your private gallery.
You should also check if the MSOnlinemodule has any RequiredPackages that you also need to copy. You can do this via (where I used PowerShellGet as example):
PS C:\> (Get-Module -ListAvailable | ? Name -eq PowerShellGet ).RequiredModules
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 1.0.0.1 PackageManagement
Maybe this helps.
From a computer that has access you could use the Save-Module cmdlet to save the module to a path, then copy that folder to the machine in question that's blocked. Then you can either place the folder in the $env:PSModulePath, or load it explicitly with the full path to the folder.
On a computer that has access to the PowerShell Gallery you can run:
Find-Module MSOnline|Save-Module -Path $home\Downloads
Then copy the MSOnline folder from your downloads folder on that computer to the target computer, and place it in one of the folders listed in $env:PSModulePath, or when you want to load the module you can do it explicitly by defining the path to the module.

Select-AzureRMSubscription not recognized

Trying to set up my VSTS windows build agent to accommodate for powershell steps within VSTS but having some problems. Initially it reported that the AzurePS system capability was not present, so I made sure to install the latest Azure modules via the powershell gallery. Now, the AzurePS capability is present but certain powershell commands do not seem to work.
This may not be related to VSTS at all but rather just a configuration issue with my specific powershell installation / configuration. The full error is as follows:
The term 'Select-AzureRMSubscription' 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.
This error can be seen both in the VSTS release logs of the particular definition I am trying to execute, as well as when attempting to run the Select-AzureRMSubscription command directly on the box itself.
According to This MS Docs for Get-AzSubscription it's located in the module Az.Accounts.
So I had to run:
Install-module Az -AllowClobber -Force
Import-module Az
to make the command avaliable to me.
For me, updating the AzureRM module did the trick:
Update-Module -Name AzureRM
Depending on your system, you may get some messages while installing
Powershell requires NuGet provider version '2.8.5.201' or newer to
interact with NuGet-based repositories. The NuGet provider must be
available in 'C:\Program Files\PackageManagement\ProviderAssemblies'
or
'C:\Users\<...>\AppData\Local\PackageManagement\ProviderAssemblies'.
You can also install the NuGet provider by running
Install-PackagePovider -Name NuGet -MinimumVersion 2.8.5.201 -Force'.
Do you want PowerShellGet to install and import the NuGet provider
now?
[Yes] {No] [Suspend]
Choose "Yes"
You are installling the modules form an untrusted repository. If you
trust this repository, change its installationPolicy value by running
the Set-PSRepository cmdlet. Are you sure you want to install the
modules from 'PSGallery'?
[Yes] [Yes to All] [No] [No to All] [Suspend]
Choose "Yes to all"
Up to three modules will be installed/updated and directly after that, the Select-AzureRmSubscription should work immediately.
Select-AzureRmSubscription b0cabaca-1234-1337-abcd-bebedada1337
# note: this subscription GUID is completely fictional.
# To get the correct one, query your subscriptions with
# Get-AzureRmSubscription
You have to make sure that certain Azure Powershell modules are installed for certain cmdlets to work. You can check which modules are available by running Get-Module -ListAvailable. Select-AzureRMSubscription requires AzureRM module.
You could just install Azure Powershell with its modules using webPI - I've never had problems using it and I've heard that installing PS modules using gallery could cause problems (see here).
You can run azure PowerShell with Azure PowerShell step/task.
Edit your build definition
Click Add build step
Select Deploy catalog > Add Azure PowerShell step/task
the OS in question is server 2012
To install PowerShell on windows server 2012, we should add the .NET Framework 3.5 feature and add the windows PowerShell 2.0 Engine feature.
About how to add features, refer to the link.
Then we can download PowerShell installation package, and install it.
Then we can use windows server 2012 PowerShell to login azure.
You need to use the Set-AzContext,
Select-AzSubscription -SubscriptionName 'SubName' | Set-AzContext