Skip to content

andyfeller/powershell-poc

Repository files navigation

powershell-poc

This is a proof of concept on how you can develop, build, and package PowerShell projects within GitHub leverage GitHub flow and techniques and Azure DevOps Artifacts.

Diagram showing GitHub Flow and Azure DevOps Artifacts side-by-side, highlighting some of the key concepts in this proof of concept

This project aims to satisfy several goals in its design:

  1. Building and releasing PowerShell scripts and modules as software artifacts
  2. Creating repeatable processes that can be reviewed and audited
  3. Integrating into native PowerShell workflows
  4. Support phased deployment and rollback of installed scripts and modules
  5. Minimizing management of additional in-network resources
  6. Secure secret management

Why recommend Azure DevOps Artifacts?

As of October 2021, the PowerShellGet 3.0 beta with NuGet v3 support has a couple of known issues using NuGet-based GitHub Packages as a PowerShell repository:

This only impacts PowerShell projects; developers on dotnet projects leverage nuget and dotnet nuget CLIs with NuGet GitHub Packages repositories as expected:

Azure DevOps Artifacts is the only managed PowerShell repository solution with NuGet v2 support recommended by Microsoft capable of supporting private PowerShell repositories.

Supported PowerShell versions in GitHub Actions

GitHub-hosted runners have support for PowerShell 5.1 and/or 7.x depending on the virtual machine type and shell used:

Virtual machine \ PowerShell version 5.1 7.x Per-minute rate End of life
macos-10.15 pwsh $0.08
macos-11 pwsh $0.08
ubuntu-18.04 pwsh $0.008
ubuntu-20.04 pwsh $0.008
windows-2016 powershell pwsh $0.016 March 15, 2022
windows-2019 powershell pwsh $0.016
windows-2022 powershell pwsh $0.016

For differences and history around PowerShell 5.1 (Desktop) and 7.x (Core), read more.

For information on how GitHub-hosted runners are billed, read more.

For confirming the version of PowerShell

The following action workflow will show both PowerShell 5.1 and 7.x are installed on windows-* runners:

on:
  workflow_dispatch:
jobs:
  check-powershell-versions:
    strategy:
      matrix:
        runner:
          - windows-2016
          - windows-2019
          - windows-2022
    runs-on: ${{ matrix.runner }}
    steps:
      - run: |
          Get-Host
        shell: powershell

      - run: |
          Get-Host
        shell: pwsh

resulting in:

Name             : ConsoleHost
Version          : 5.1.20348.230
InstanceId       : b6a8f124-9444-4c21-9af8-5299d854b274
UI               : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture   : en-US
CurrentUICulture : en-US
PrivateData      : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
DebuggerEnabled  : True
IsRunspacePushed : False
Runspace         : System.Management.Automation.Runspaces.LocalRunspace

and

Name             : ConsoleHost
Version          : 7.1.5
InstanceId       : 58ae196a-c589-4e2f-aa78-c1b76f69cf1e
UI               : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture   : en-US
CurrentUICulture : en-US
PrivateData      : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
DebuggerEnabled  : True
IsRunspacePushed : False
Runspace         : System.Management.Automation.Runspaces.LocalRunspace

PowerShell repository structure

This repository is a proof of concept and it is evolving as we learn from others and exercise ideas. One of those areas is the relation of repository design and building artifacts.

This repository has been designed to support multiple modules and/or scripts:

├── docs                #  documentation, images, and other assets
│   └── assets
│
├── modules             #  directory containing modules, will publish on merge to main
│   └── PSHello
│       ├── Private     #  directory containing module's private functions that should not be exported
│       ├── Public      #  directory containing module's public functions that should be exported
│       └── en-US       #  directory containing module's help docs (en-US locale)
│
├── scripts             #  directory containing scripts, will publish on merge to main
│
└── tests               #  directory containing tests for modules and scripts

Creating new PowerShell scripts and modules

Creating new scripts

Scripts are contained within the scripts directory and are published individually when merged to the main branch.

Scripts should be created using the New-ScriptFileInfo cmdlet and include basic information like:

$scriptFileInfo = @{
  Path = "./scripts/PSHelloWrapper.ps1"
  Version = "0.0.1"
  Author = "andyfeller@github.com"
  Description = "Simple wrapper around PSHello module"
}

New-ScriptFileInfo @scriptFileInfo

Creating new modules

Modules are contained within the modules directory and are published individually when merged the main branch.

Modules should be created using the New-ModuleManifest cmdlet and include basic information like:

$moduleManifest = @{
  Path = "./modules/PSHello/PSHello.psd1"
  Version = "0.0.1"
  Author = "andyfeller@github.com"
  Description = "Simple module to print Hello world!"
}

New-ModuleManifest @moduleManifest

Releasing PowerShell scripts and modules

The modules and scripts contained in the above directories will be released whenever changes are merged to the main branch. In order to make it easier for contributors, modules and scripts will be released with the revision portion of their version set to the action run number before publishing. Ideally, contributors would update the version whenever changes are made, but to err is human.

Want to give special thanks to @agazoth's "Increment Build and Version in PSake" article as it highlighted the use of Update-ModuleManifest and Update-ScriptFileInfo to update version numbers in the release process!

Setup

Prerequisites

Setup Azure DevOps Artifacts for published scripts and modules

The following is how a maintainer would setup the necessary Azure DevOps Artifacts feed and tokens for private PowerShell repository this proof of concept is based on:

  1. Create Azure DevOps Artifacts feed and personal access token (PAT) for CI

    Only Packaging (Read & Write) permissions needed

    Screenshot of Azure DevOps personal access tokens needed by maintainer for GitHub Actions to publish to Azure Artifacts feed

    Consider disabling any unnecessary Azure DevOps services

    Screenshot of Azure DevOps project containing Azure Artifacts feed, demonstrating everything but Azure Artifact disabled

    Azure Artifact feeds support both NuGet v2 and v3 APIs

    Replacing /v3/index.json with /v2/ should allow this feed to be used by PowerShellGet.

    Screenshot of NuGet-based Azure Artifacts feed, demonstrating NuGet configuration and commands to work with feed

  2. Create GitHub Actions secret with Azure DevOps Artifacts PAT

    GitHub Action secrets are obscured in runner logs

    Screenshot of GitHub repository settings for actions secrets available to actions runners including secret of Azure Artifacts PAT to publish artifacts

Setup host where scripts and modules will be installed

  1. Ensure strong cryptography (TLS 1.2) is enabled for Azure DevOps

    As of Febuary 2020, Azure DevOps Artifacts requires hosts have TLS 1.2 supported, which was a response to changing PCI compliance standards.

    To confirm whether TLS 1.2 supported is enabled
    [Net.ServicePointManager]::SecurityProtocol
    Tls, Tls11, Tls12
    To enable TLS 1.2 support
    Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319' -Name SchUseStrongCrypto -Value 1 -PropertyType 'Dword' -Force | Out-Null
    Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319' -Name SystemDefaultTlsVersions -Value 1 -PropertyType 'Dword' -Force | Out-Null
    
    If ([System.Environment]::Is64BitOperatingSystem) {
      Set-ItemProperty -Path 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319' -Name SchUseStrongCrypto -Value 1 -PropertyType 'Dword' -Force | Out-Null
      Set-ItemProperty -Path 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319' -Name SystemDefaultTlsVersions -Value 1 -PropertyType 'Dword' -Force | Out-Null
    }

    Afterwards, close and reopen PowerShell terminal.

  2. Install SecretManagement and SecretStore modules from the PowerShell Gallery to securely hold credentials:

    Install-Module Microsoft.PowerShell.SecretManagement, Microsoft.PowerShell.SecretStore -Repository PSGallery
  3. Create secret vault for storing Azure DevOps Artifacts credentials:

    Register-SecretVault -Name powershell-poc -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault
  4. Create secrets in secret vault for authenticating to Azure DevOps Artifacts:

    Set-Secret -Name powershell-poc-username
    Set-Secret -Name powershell-poc-password
    
    $credential = New-Object -TypeName PSCredential -ArgumentList (Get-Secret powershell-poc-username), (Get-Secret powershell-poc-password)
    Set-Secret -Name powershell-poc-credential -Secret $credential

    You will be prompted for a password to secure a secret vault the first time a secret is saved:

    Creating a new powershell-poc vault. A password is required by the current store configuration.
    Enter password:
    ********************
    Enter password again for verification:
    ********************
    
  5. Register PowerShell repository using the read-only packages PAT

    $psRepository = @{
      Name = "powershell-poc"
      SourceLocation = "https://pkgs.dev.azure.com/andyfeller/powershell-poc/_packaging/powershell-poc/nuget/v2"
      ScriptSourceLocation = "https://pkgs.dev.azure.com/andyfeller/powershell-poc/_packaging/powershell-poc/nuget/v2"
      InstallationPolicy = "Trusted"
      Credential = (Get-Secret powershell-poc-credential)
    }
    
    Register-PSRepository @psRepository
    Get-PSRepository
  6. Install and invoke PSHello module:

    Install-Module PSHello -Repository powershell-poc -Credential (Get-Secret powershell-poc-credential)
    Write-PSHelloWorld
  7. Install and invoke PSHelloWrapper script:

    Install-Script PSHelloWrapper -Repository powershell-poc -Credential (Get-Secret powershell-poc-credential)
    Get-InstalledScript | Format-List *

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published