Choosing destination when installing a chocolatey packages with Puppet - nuget

I have created a chocolatey package that is going to get deployed with Puppet to my test server. The package is basically just a bunch of static resources (CSS/JS) zipped together:
Install-ChocolateyZipPackage 'WebResources' '\\teamcity\WebResources.zip' "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
What would be the best way, using chocolatey, to extract these files to my website folder? It's just at the moment putting everything in the default c:/chocolatey/lib folder.
I've been thinking of 2 ways:
Include a script file that copies the files.
changing the chocolatey env variable to point to my site.
None of them feels right. Any other suggestions?

You are telling the package to install the files to your wherever the script file is running from. This "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" is the destination. If you want to have that change, you should probably put an environment variable on the machine you are targeting and then have it use that if available and default back to this...

Related

PowerShell Module Deployment Duplication

I am using Azure DevOps to deploy PowerShell modules to a server. This release task deploys the modules to the directory C:\Windows\System32\WindowsPowerShell\v1.0\Modules\. I am able to use the modules once they are deployed to this folder successfully.
If I modify one of the modules and re-release it the file in C:\Windows\System32\WindowsPowerShell\v1.0\Modules\ gets updated, however the old version of the module is still used when running from a batch file using pwsh.
I discovered that the module file also exists in the following paths:
C:\Program Files\PowerShell\Modules\
C:\Program Files\PowerShell\6\Modules\
When deploying the new version using Azure DevOps the old version in the above two directories are not updated. Manually updating the module in those locations fixes the problem.
Why is the module file being copied into those two additional paths?
Should those copies be overwritten when a new version of the module is deployed?
What is the correct way of deploying a module in this scenario?
Powershell uses different paths to load modules. Use $env:PSModulePath -split ";" to know which are the paths being used.
The difference between each path is user scope and usage scope (e.g. made for custom modules or windows official modules).
Now, by default, PS looks for the latest version of each module across all the paths. So maybe the old version is being run because at the time you re-deploy. You are not updating the module version in the Module Manifest, so if PS see they are the "same" version it gets the last one loaded on the PSModulePath.
Take a look at this awesome post for more details: Everything you wanted to know about PowerShell's Module Path
Now to your questions.
Why is the module file being copied into those two additional paths?
This could be a server configuration or the script that you are using to deploy.
Should those copies be overwritten when a new version of the module is deployed?
Not necessarily, if the versions are maintained correctly. On the post shared says how to check the versions of each module.

Chocolatey: Make an install script

In order to make install scripts and understanding existing ones, I would like to know what happens behind the scenes of the typical:
choco install notepadplusplus
I found the following here:
Installation
Chocolatey uses Nuget.Core to retrieve the package from the source.
Choco determines if it self-contained or has automation scripts - PowerShell scripts (*.ps1 files), and soon to be open to Scriptcs files in the 0.9.10.x timeframe (I know, right?!).
Choco takes a registry snapshot for later comparison.
If there are automation scripts, choco will run those. They can contain whatever you need to do, if they are PowerShell you have the full power of Posh (PowerShell), but you should try to ensure they are compatible with Posh v2+.
Choco compares the snapshot and determines uninstaller information and saves that to a .registry file.
Choco snapshots the folder based on all files that are currently in the package directory.
Choco looks for executable files in the package folder and generates shims into the $env:ChocolateyInstall\bin folder so those items are available on the path. Those could have been embedded into the package or brought down from somewhere (internet, ftp, file folder share, etc) and placed there.
That given,
How can I get the .nupkg package URL? In general it seems like this:
https://chocolatey.org/api/v2/package/package-name
Which is the .nupkg package download directory?
Where is the content of the .nupkg package extracted by default? This is important since chocolateyInstall.ps1 sometime uses Split-Path -Parent $MyInvocation.MyCommand.Definition.
"Scriptcs files in the 0.9.10.x timeframe" is rather cryptic. Can you give some references?
Is Posh v2+ simply short for Powershell or is a specific technology?
There are several executable files in $env:ChocolateyInstall\lib without a link in $env:ChocolateyInstall\bin. For example, the mpv.exe of the mpv player is not linked.
Yes, that download URL seems correct. The download directory is always into the Chocolatey installation folder, then lib\packageName, and this is where contents are extracted to.
Right now, installation scripts are only written in PowerShell. This comment is referring to the ability to write in installation scripts in C#, using the ScriptCS run time. Currently, this isn't yet supported.
Yes, this is just a short way of referring to PowerShell.
In the case of the mpv package, you will notice that there is an mpv.exe.ignore file. The presence of this file in the package prevents a shim being created.

How to share a Powershell psm1 across Azure Functions

You can create a modules folder and place a psm1 file there for auto loading. Is there a way to share that psm1 across functions in the same App Service?
Yes, you may move your modules folder up the directory tree and place it under wwwroot, e.g. D:\home\site\wwwroot\mymodules. Auto-loading will not occur in this setup, so you will need to explicitly add the Import-Module command in your PowerShell script, e.g.
Import-Module "D:\home\site\wwwroot\mymodules\MyScript.psm1";
If you suspect that MyScript.psm1 is already installed on the system and need to override it with your version, add the -Global flag as follows
Import-Module "D:\home\site\wwwroot\mymodules\MyScript.psm1" -Global;

Must Powershell modules be placed in separate folders?

Using PowerShell 2.0
According to what I've read online, user-created powershell modules must each reside in their own directory. For example if I create a module called MyModule.psm1 it must reside in a folder called MyModule and reside in any directory listed in $env:PSModulePath.
If I have many modules for a project, it seems silly to me to have to create a separate folder for each one. Is this really necessary? Why? Any elegant way around it?
If you want to just have the .psm1 file, you can import it by giving the path to the file itself rather than just the name of the module.
For example:
import-module c:\mymodules\folder\themodule.psm1
With this technique you can have as many modules as you want in the same folder. I don't know that I'd recommend this, but it does work.
PowerShell modules can consist of more than one file, for instance for providing multi-language help or by splitting the functionality into several files. So yes, it's necessary.
It's not ideal, but you could create junctions (MSDN) using Sysinternals junction.exe. That would allow you to store all of the actual data in one folder, but reference it using different paths.
[MSDN] Hard Links and Junctions
Assume that you had three (3) stand-alone .psm1 module files in a folder named Modules in your $env:UserProfile\Documents\WindowsPowerShell\Modules directory.
You can use several junction.exe commands to create junctions (links) that point to the "real" Modules folder on the filesystem.
$ModulePath = '{0}\Modules' -f (Split-Path -Path $Profile.CurrentUserCurrentHost -Parent);
junction.exe $ModulePath\Foo $ModulePath\Modules;
junction.exe $ModulePath\Bar $ModulePath\Modules;
junction.exe $ModulePath\Trevor $ModulePath\Modules;
Here is what it looks like when you navigate into one of the junctions, for example, Foo.
As far as Windows PowerShell is concerned, you are in the Foo directory, which matches the Foo.psm1 file name. It should ignore the rest of the files in that folder.
Now, you can run Get-Module -ListAvailable, and you should see a list of the modules in your user module directory.

Installing PowerShell module persistently for all users

I'm installing a PowerShell module via Octopus Deploy onto a number of different servers. For testing purposes, I went with the guidance of Microsoft's documentation for installing PowerShell Modules.
This worked fine, but as the documentation stated, my changes would be visible only for the current session. That is, if I were to do the following:
$modulePath = [Environment]::GetEnvironmentVariable("PSModulePath", [EnvironmentVariableTarget]::Machine)
# More practically, this would be some logic to install only if not present
$modulePath += ";C:\CustomModules"
[Environment]::SetEnvironmentVariable("PSModulePath", $modulePath, [EnvironmentVariableTarget]::Machine)
When running this installer automatically on tentacle servers, future PowerShell sessions do not appear to see the newly installed modules.
How can I install a PowerShell module in a profile agnostic way so that every PowerShell session started can see it?
PowerShell can only "see" modules installed in one of the directories listed in $env:PSModulePath. Otherwise you'll have to import the module with its full path.
To make a new module visible to all users you basically have two options:
Install the module to the default system-wide module directory (C:\Windows\system32\WindowsPowerShell\v1.0\Modules).
Modify the system environment so that PSModulePath variable already contains your custom module directory (e.g. via a group policy preference).
The latter will only become effective for PowerShell sessions started after the modification was made, though.
This profile applies to all users and all shells.
%windir%\system32\WindowsPowerShell\v1.0\profile.ps1
After taking the steps you spelled out in your question (which I think is the general way to go), I found two ways to get the new module source recognized by Powershell:
Restart the machine. (Works every time.)
Reset the PSModulePath in each open session.
$env:PSModulePath=[Environment]::GetEnvironmentVariable("PSModulePath", "Machine")
I found this was necessary to run in both normal and elevated prompts to get this to work without restarting in each type of prompt. (See also the conversation # Topic: PSModulePath.)