├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── PITCHME.md ├── PSHonolulu ├── PSHonolulu.psd1 ├── PSHonolulu.psm1 └── public │ ├── Get-HonoluluServer.ps1 │ ├── Set-HonoluluServer.ps1 │ └── Show-Computer.ps1 ├── README.md ├── appveyor.yml ├── build.ps1 ├── deploy.PSDeploy.ps1 ├── docs ├── Quick-Start-Installation-and-Example.md ├── about.md ├── acknowledgements.md └── index.md ├── mkdocs.yml ├── module.build.ps1 ├── spec ├── module.Steps.ps1 └── module.feature └── tests ├── Feature.Tests.ps1 ├── Help.Tests.ps1 ├── Project.Tests.ps1 ├── Regression.Tests.ps1 └── Unit.Tests.ps1 /.gitignore: -------------------------------------------------------------------------------- 1 | /output/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "powershell.codeFormatting.openBraceOnSameLine": false, 4 | "powershell.codeFormatting.newLineAfterOpenBrace": true, 5 | "powershell.codeFormatting.preset": "Allman", 6 | "editor.formatOnSave": true, 7 | "editor.insertSpaces": true, 8 | "editor.tabSize": 4, 9 | "[json]": { 10 | "editor.tabSize": 2 11 | } 12 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Kevin Marquette 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /PITCHME.md: -------------------------------------------------------------------------------- 1 | # PSHonolulu 2 | 3 | Module for working with Project Hoholulu 4 | 5 | --- 6 | 7 | ### Install Project Honolulu from Microsoft 8 | 9 | [Windows Server Honolulu Evaluation](https://www.microsoft.com/en-US/evalcenter/evaluate-windows-server-honolulu) 10 | 11 | --- 12 | 13 | ### Install Module 14 | 15 | Install from the PSGallery and Import the module 16 | 17 | Install-Module PSHonolulu 18 | Import-Module PSHonolulu 19 | 20 | --- 21 | 22 | ### Launch Honolulu from PowerShell 23 | 24 | Show-Computer -ComputerName $ComputerName 25 | 26 | This will open Honolulu to the page for `$ComputerName`. 27 | This can be any computer, even one that was 28 | not imported into Honolulu ahead of time 29 | 30 | --- 31 | 32 | ### Open to a section 33 | 34 | Show-Computer -ComputerName $ComputerName -View Services 35 | 36 | This will open Honolulu to the 37 | services page for `$ComputerName`. 38 | 39 | --- 40 | 41 | ### With Pipeline Support 42 | 43 | Get-Content servers.txt | Show-Computer -View Events 44 | 45 | Pipe in a list of computers, 46 | it will open each one of them. 47 | 48 | --- 49 | 50 | ### What's next? 51 | 52 | For more information 53 | 54 | * [PSHonolulu.readthedocs.io](http://PSHonolulu.readthedocs.io) 55 | * [github.com/KevinMarquette/PSHonolulu](https://github.com/KevinMarquette/PSHonolulu) 56 | * [KevinMarquette.github.io](https://KevinMarquette.github.io) 57 | -------------------------------------------------------------------------------- /PSHonolulu/PSHonolulu.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'PSHonolulu' 3 | # 4 | # Generated by: Kevin Marquette 5 | # 6 | # Generated on: 10/10/2017 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'PSHonolulu.psm1' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '0.2.0' 16 | 17 | # Supported PSEditions 18 | # CompatiblePSEditions = @() 19 | 20 | # ID used to uniquely identify this module 21 | GUID = 'c730bcc4-7fcc-4afd-a74a-50dc45035573' 22 | 23 | # Author of this module 24 | Author = 'Kevin Marquette' 25 | 26 | # Company or vendor of this module 27 | CompanyName = 'Unknown' 28 | 29 | # Copyright statement for this module 30 | Copyright = '(c) 2017 Kevin Marquette. All rights reserved.' 31 | 32 | # Description of the functionality provided by this module 33 | Description = 'Module for working with Project Hoholulu' 34 | 35 | # Minimum version of the Windows PowerShell engine required by this module 36 | # PowerShellVersion = '' 37 | 38 | # Name of the Windows PowerShell host required by this module 39 | # PowerShellHostName = '' 40 | 41 | # Minimum version of the Windows PowerShell host required by this module 42 | # PowerShellHostVersion = '' 43 | 44 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 45 | # DotNetFrameworkVersion = '' 46 | 47 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 48 | # CLRVersion = '' 49 | 50 | # Processor architecture (None, X86, Amd64) required by this module 51 | # ProcessorArchitecture = '' 52 | 53 | # Modules that must be imported into the global environment prior to importing this module 54 | # RequiredModules = @() 55 | 56 | # Assemblies that must be loaded prior to importing this module 57 | # RequiredAssemblies = @() 58 | 59 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 60 | # ScriptsToProcess = @() 61 | 62 | # Type files (.ps1xml) to be loaded when importing this module 63 | # TypesToProcess = @() 64 | 65 | # Format files (.ps1xml) to be loaded when importing this module 66 | # FormatsToProcess = @() 67 | 68 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 69 | # NestedModules = @() 70 | 71 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 72 | FunctionsToExport = @('Get-HonoluluServer','Set-HonoluluServer','Show-Computer') 73 | 74 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 75 | CmdletsToExport = @() 76 | 77 | # Variables to export from this module 78 | VariablesToExport = @() 79 | 80 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 81 | AliasesToExport = @() 82 | 83 | # DSC resources to export from this module 84 | # DscResourcesToExport = @() 85 | 86 | # List of all modules packaged with this module 87 | # ModuleList = @() 88 | 89 | # List of all files packaged with this module 90 | # FileList = @() 91 | 92 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 93 | PrivateData = @{ 94 | 95 | PSData = @{ 96 | 97 | # Tags applied to this module. These help with module discovery in online galleries. 98 | # Tags = @() 99 | 100 | # A URL to the license for this module. 101 | # LicenseUri = '' 102 | 103 | # A URL to the main website for this project. 104 | # ProjectUri = '' 105 | 106 | # A URL to an icon representing this module. 107 | # IconUri = '' 108 | 109 | # ReleaseNotes of this module 110 | # ReleaseNotes = '' 111 | 112 | } # End of PSData hashtable 113 | 114 | } # End of PrivateData hashtable 115 | 116 | # HelpInfo URI of this module 117 | # HelpInfoURI = '' 118 | 119 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 120 | # DefaultCommandPrefix = '' 121 | 122 | } 123 | 124 | 125 | -------------------------------------------------------------------------------- /PSHonolulu/PSHonolulu.psm1: -------------------------------------------------------------------------------- 1 | [cmdletbinding()] 2 | param() 3 | Write-Verbose "This psm1 is replaced in the build output. This file is only used for debugging." 4 | Write-Verbose $PSScriptRoot 5 | 6 | Write-Verbose 'Import everything in sub folders' 7 | foreach ($folder in @('classes', 'private', 'public', 'includes', 'internal')) 8 | { 9 | $root = Join-Path -Path $PSScriptRoot -ChildPath $folder 10 | if (Test-Path -Path $root) 11 | { 12 | Write-Verbose "processing folder $root" 13 | $files = Get-ChildItem -Path $root -Filter *.ps1 -Recurse 14 | 15 | # dot source each file 16 | $files | where-Object { $_.name -NotLike '*.Tests.ps1'} | 17 | ForEach-Object {Write-Verbose $_.basename; . $_.FullName} 18 | } 19 | } 20 | 21 | Export-ModuleMember -function (Get-ChildItem -Path "$PSScriptRoot\public\*.ps1").basename 22 | -------------------------------------------------------------------------------- /PSHonolulu/public/Get-HonoluluServer.ps1: -------------------------------------------------------------------------------- 1 | function Get-HonoluluServer 2 | { 3 | <# 4 | .SYNOPSIS 5 | Get the Honolulu Server connection information 6 | 7 | .DESCRIPTION 8 | Get the confgiured Honolulu Server settings 9 | 10 | .EXAMPLE 11 | Get-HonoluluServer 12 | 13 | This will set the local computer as the Project Honolulu server 14 | 15 | .NOTES 16 | General notes 17 | #> 18 | 19 | [CmdletBinding()] 20 | param( ) 21 | 22 | begin 23 | { 24 | $path = Join-Path $env:homedrive $env:HOMEPATH 25 | $path = Join-Path $path '.pshonolulu' 26 | } 27 | process 28 | { 29 | try 30 | { 31 | If ( Test-Path $path -ErrorAction Ignore ) 32 | { 33 | Get-Content $path | ConvertFrom-JSON 34 | } 35 | else 36 | { 37 | [pscustomobject]@{ 38 | ComputerName = $env:COMPUTERNAME 39 | Port = 6516 40 | } 41 | } 42 | } 43 | catch 44 | { 45 | $PSCmdlet.ThrowTerminatingError( $PSItem ) 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /PSHonolulu/public/Set-HonoluluServer.ps1: -------------------------------------------------------------------------------- 1 | function Set-HonoluluServer 2 | { 3 | <# 4 | .SYNOPSIS 5 | Set the Honolulu Server connection information 6 | 7 | .DESCRIPTION 8 | Configure the local system to use a specified Honolulu Server 9 | 10 | .PARAMETER ComputerName 11 | The server name. Use localhost if this system is running the service 12 | 13 | .PARAMETER Port 14 | The port that Project Honolulu is listening on 15 | 16 | .EXAMPLE 17 | Set-HonoluluServer -ComputerName $env:ComputerName -Port 6516 18 | 19 | This will set the local computer as the Project Honolulu server 20 | 21 | .NOTES 22 | General notes 23 | #> 24 | 25 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] 26 | [CmdletBinding()] 27 | param( 28 | $ComputerName = $env:COMPUTERNAME, 29 | $Port = 6516 30 | ) 31 | 32 | begin 33 | { 34 | $path = Join-Path $env:homedrive $env:HOMEPATH 35 | $path = Join-Path $path '.pshonolulu' 36 | } 37 | 38 | process 39 | { 40 | try 41 | { 42 | $settings = @{ 43 | ComputerName = $ComputerName 44 | Port = $Port 45 | } 46 | $settings | ConvertTo-Json | Set-Content -Path $path 47 | } 48 | catch 49 | { 50 | $PSCmdlet.ThrowTerminatingError( $PSItem ) 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /PSHonolulu/public/Show-Computer.ps1: -------------------------------------------------------------------------------- 1 | function Show-Computer 2 | { 3 | <# 4 | .SYNOPSIS 5 | Show specified computer in Honolulu 6 | 7 | .DESCRIPTION 8 | Takes the specified computer and crafts the needed URL to load it in Project Honolulu 9 | 10 | .PARAMETER ComputerName 11 | Parameter description 12 | 13 | .PARAMETER View 14 | The view to show by default. 15 | 16 | .EXAMPLE 17 | Show-Computer 18 | 19 | Will show the local computer in Project Honolulu 20 | 21 | .EXAMPLE 22 | Show-Computer -ComputerName server02 23 | 24 | .NOTES 25 | General notes 26 | #> 27 | 28 | [CmdletBinding()] 29 | param( 30 | [parameter( ValueFromPipeline )] 31 | [alias('ServerName', 'CN')] 32 | [string[]] 33 | $ComputerName = $env:COMPUTERNAME, 34 | 35 | [ValidateSet( 36 | 'overview', 37 | 'certificates', 38 | 'devices', 39 | 'Events', 40 | 'Files', 41 | 'Firewall', 42 | 'UsersGroups', 43 | 'Network', 44 | 'Processes', 45 | 'Registry', 46 | 'RolesFeatures', 47 | 'Services', 48 | 'Storage', 49 | 'StorageReplica', 50 | 'VirtualMachines', 51 | 'VirtualSwitches', 52 | 'WindowsUpdate' 53 | )] 54 | [string] 55 | $View = 'overview' 56 | ) 57 | 58 | begin 59 | { 60 | $info = Get-HonoluluServer 61 | $uri = 'http://{0}:{1}' -f $info.ComputerName, $info.Port 62 | $View = $View.ToLower() 63 | } 64 | process 65 | { 66 | try 67 | { 68 | foreach ( $node in $ComputerName ) 69 | { 70 | $endpoint = "{0}/servermanager/connections/server/{1}/tools/{2}" -f $uri, $node.tolower(), $View 71 | Write-Verbose $endpoint 72 | Start-Process $endpoint 73 | } 74 | } 75 | catch 76 | { 77 | $PSCmdlet.ThrowTerminatingError( $PSItem ) 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PSHonolulu 2 | 3 | Module for working with Project Hoholulu 4 | 5 | ## GitPitch PitchMe presentation 6 | 7 | * [gitpitch.com/KevinMarquette/PSHonolulu](https://gitpitch.com/KevinMarquette/PSHonolulu) 8 | 9 | ## Getting Started 10 | 11 | Install Project Honolulu from Microsoft: 12 | 13 | [Windows Server Honolulu Evaluation](https://www.microsoft.com/en-US/evalcenter/evaluate-windows-server-honolulu) 14 | 15 | Install this Module from the PSGallery and Import the module 16 | 17 | Install-Module PSHonolulu 18 | Import-Module PSHonolulu 19 | 20 | ## Basic Commands 21 | 22 | If you installed Honolulu on your local computer, you should be able to open the details of any computer by running this command: 23 | 24 | Show-Computer -ComputerName $ComputerName 25 | Show-Computer -ComputerName $ComputerName -View Services 26 | 27 | The important detail here is that you don't need to first import `$ComputerName` into Honolulu first. You can specify any server in your organization where you have access to WinRM and WMI. 28 | 29 | If Honolulu is installed on a remote computer, you can set the address with this command: 30 | 31 | Set-HonoluluServer -ComputerName $ServerName -Port 6516 32 | Get-HonoluluServer 33 | 34 | ## More Information 35 | 36 | For more information 37 | 38 | * [PSHonolulu.readthedocs.io](http://PSHonolulu.readthedocs.io) 39 | * [github.com/KevinMarquette/PSHonolulu](https://github.com/KevinMarquette/PSHonolulu) 40 | * [KevinMarquette.github.io](https://KevinMarquette.github.io) 41 | 42 | 43 | This project was generated using [Kevin Marquette](http://kevinmarquette.github.io)'s [Full Module Plaster Template](https://github.com/KevinMarquette/PlasterTemplates/tree/master/FullModuleTemplate). 44 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # See http://www.appveyor.com/docs/appveyor-yml for many more options 2 | 3 | environment: 4 | NugetApiKey: 5 | secure: sqj8QGRYue5Vq3vWm2GdcCttqyOkt7NOheKlnmIUq1UcgVrmQezFArp/2Z1+G3oT 6 | 7 | # Allow WMF5 (i.e. PowerShellGallery functionality) 8 | os: WMF 5 9 | 10 | # Skip on updates to the readme. 11 | # We can force this by adding [skip ci] or [ci skip] anywhere in commit message 12 | skip_commits: 13 | message: /updated (readme|doc).*|update (readme|doc).*s/ 14 | 15 | build: false 16 | 17 | #Kick off the CI/CD pipeline 18 | test_script: 19 | - ps: . .\build.ps1 -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Description 3 | Installs and loads all the required modules for the build. 4 | Derived from scripts written by Warren F. (RamblingCookieMonster) 5 | #> 6 | 7 | [cmdletbinding()] 8 | param ($Task = 'Default') 9 | Write-Output "Starting build" 10 | 11 | # Grab nuget bits, install modules, set build variables, start build. 12 | Write-Output " Install Dependent Modules" 13 | Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null 14 | Install-Module InvokeBuild, PSDeploy, BuildHelpers, PSScriptAnalyzer -force -Scope CurrentUser 15 | Install-Module Pester -Force -SkipPublisherCheck -Scope CurrentUser 16 | 17 | Write-Output " Import Dependent Modules" 18 | Import-Module InvokeBuild, BuildHelpers, PSScriptAnalyzer 19 | 20 | Set-BuildEnvironment 21 | 22 | Write-Output " InvokeBuild" 23 | Invoke-Build $Task -Result result 24 | if ($Result.Error) 25 | { 26 | exit 1 27 | } 28 | else 29 | { 30 | exit 0 31 | } -------------------------------------------------------------------------------- /deploy.PSDeploy.ps1: -------------------------------------------------------------------------------- 1 | # Generic module deployment. 2 | # This stuff should be moved to psake for a cleaner deployment view 3 | 4 | # ASSUMPTIONS: 5 | 6 | # folder structure of: 7 | # - RepoFolder 8 | # - This PSDeploy file 9 | # - ModuleName 10 | # - ModuleName.psd1 11 | 12 | # Nuget key in $ENV:NugetApiKey 13 | 14 | # Set-BuildEnvironment from BuildHelpers module has populated ENV:BHProjectName 15 | 16 | # find a folder that has psd1 of same name... 17 | 18 | if ($ENV:BHProjectName -and $ENV:BHProjectName.Count -eq 1) 19 | { 20 | Deploy Module { 21 | By PSGalleryModule { 22 | FromSource output\$ENV:BHProjectName 23 | To PSGallery 24 | WithOptions @{ 25 | ApiKey = $ENV:NugetApiKey 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /docs/Quick-Start-Installation-and-Example.md: -------------------------------------------------------------------------------- 1 | # Installing PSHonolulu 2 | 3 | Install Project Honolulu from Microsoft: 4 | 5 | [Windows Server Honolulu Evaluation](https://www.microsoft.com/en-US/evalcenter/evaluate-windows-server-honolulu) 6 | 7 | Install PSHonolulu from the PowerShell Gallery 8 | 9 | Find-Module PSHonolulu | Install-Module 10 | 11 | Import Module 12 | 13 | Import-Module PSHonolulu 14 | 15 | # Basic Commands 16 | 17 | If you installed Honolulu on your local computer, you should be able to open the details of any computer by running this command: 18 | 19 | Show-Computer -ComputerName $ComputerName 20 | 21 | If Honolulu is installed on a remote computer, you can set the address with this command: 22 | 23 | Set-HonoluluServer -ComputerName $ServerName -Port 6516 -------------------------------------------------------------------------------- /docs/about.md: -------------------------------------------------------------------------------- 1 | # What is PSHonolulu 2 | 3 | Module for working with Project Hoholulu 4 | 5 | Authored by Kevin Marquette 6 | -------------------------------------------------------------------------------- /docs/acknowledgements.md: -------------------------------------------------------------------------------- 1 | I want to thank everyone that has submitted feedback,ideas and contributions to this project. Some individuals deserve as special thank you for helping make this project a success. 2 | 3 | ## Kevin Marquette 4 | 5 | The original author of this project is Kevin Marquette. All of this was made possible by the effort Kevin Marquette put into getting this project off the ground and the many hours that were spent making everything work. 6 | 7 | ## Kevin Marquette 8 | 9 | This project was generated using a [Plaster template](https://github.com/KevinMarquette/PlasterTemplates) that he put together and shared with the community. It is modeled after the way he build his projects. A lot of the base structure for this project was put in place by that Plaster template. [kevinmarquette.github.io](http://kevinmarquette.github.io) 10 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # PSHonolulu Docs 2 | 3 | PSHonolulu uses ReadTheDocs to host our documentation. This allows us to keep our docs in the repository, without the various limitations that come with the built in GitHub repo wiki. 4 | 5 | If you would like to contribute to the project or these docs, place make a pull request to the project on GitHub. 6 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: PSHonolulu 2 | repo_url: https://github.com/KevinMarquette/PSHonolulu 3 | theme: readthedocs 4 | pages: 5 | - Home: index.md 6 | - PSHonolulu Basics: 7 | - What is PSHonolulu: about.md 8 | - Quick Start: Quick-Start-Installation-and-Example.md 9 | - Acknowledgements: acknowledgements.md 10 | -------------------------------------------------------------------------------- /module.build.ps1: -------------------------------------------------------------------------------- 1 | $script:ModuleName = 'PSHonolulu' 2 | 3 | $script:Source = Join-Path $BuildRoot $ModuleName 4 | $script:Output = Join-Path $BuildRoot output 5 | $script:Destination = Join-Path $Output $ModuleName 6 | $script:ModulePath = "$Destination\$ModuleName.psm1" 7 | $script:ManifestPath = "$Destination\$ModuleName.psd1" 8 | $script:Imports = ( 'private', 'public', 'classes' ) 9 | $script:TestFile = "$PSScriptRoot\output\TestResults_PS$PSVersion`_$TimeStamp.xml" 10 | 11 | Task Default Build, Pester, UpdateSource, Publish 12 | Task Build CopyToOutput, BuildPSM1, BuildPSD1 13 | Task Pester Build, ImportModule, UnitTests, FullTests 14 | 15 | Task Clean { 16 | $null = Remove-Item $Output -Recurse -ErrorAction Ignore 17 | $null = New-Item -Type Directory -Path $Destination 18 | } 19 | 20 | Task UnitTests { 21 | $TestResults = Invoke-Pester -Path Tests\*unit* -PassThru -Tag Build -ExcludeTag Slow 22 | if ($TestResults.FailedCount -gt 0) 23 | { 24 | Write-Error "Failed [$($TestResults.FailedCount)] Pester tests" 25 | } 26 | } 27 | 28 | Task FullTests { 29 | $TestResults = Invoke-Pester -Path Tests -PassThru -OutputFormat NUnitXml -OutputFile $testFile -Tag Build 30 | if ($TestResults.FailedCount -gt 0) 31 | { 32 | Write-Error "Failed [$($TestResults.FailedCount)] Pester tests" 33 | } 34 | } 35 | 36 | Task Specification { 37 | 38 | $TestResults = Invoke-Gherkin $PSScriptRoot\Spec -PassThru 39 | if ($TestResults.FailedCount -gt 0) 40 | { 41 | Write-Error "[$($TestResults.FailedCount)] specification are incomplete" 42 | } 43 | } 44 | 45 | Task CopyToOutput { 46 | 47 | Write-Output " Create Directory [$Destination]" 48 | $null = New-Item -Type Directory -Path $Destination -ErrorAction Ignore 49 | 50 | Get-ChildItem $source -File | 51 | where name -NotMatch "$ModuleName\.ps[dm]1" | 52 | Copy-Item -Destination $Destination -Force -PassThru | 53 | ForEach-Object { " Create [.{0}]" -f $_.fullname.replace($PSScriptRoot, '')} 54 | 55 | Get-ChildItem $source -Directory | 56 | where name -NotIn $imports | 57 | Copy-Item -Destination $Destination -Recurse -Force -PassThru | 58 | ForEach-Object { " Create [.{0}]" -f $_.fullname.replace($PSScriptRoot, '')} 59 | } 60 | 61 | Task BuildPSM1 -Inputs (Get-Item "$source\*\*.ps1") -Outputs $ModulePath { 62 | 63 | [System.Text.StringBuilder]$stringbuilder = [System.Text.StringBuilder]::new() 64 | foreach ($folder in $imports ) 65 | { 66 | [void]$stringbuilder.AppendLine( "Write-Verbose 'Importing from [$Source\$folder]'" ) 67 | if (Test-Path "$source\$folder") 68 | { 69 | $fileList = Get-ChildItem "$source\$folder\*.ps1" | Where Name -NotLike '*.Tests.ps1' 70 | foreach ($file in $fileList) 71 | { 72 | $shortName = $file.fullname.replace($PSScriptRoot, '') 73 | Write-Output " Importing [.$shortName]" 74 | [void]$stringbuilder.AppendLine( "# .$shortName" ) 75 | [void]$stringbuilder.AppendLine( [System.IO.File]::ReadAllText($file.fullname) ) 76 | } 77 | } 78 | } 79 | 80 | Write-Output " Creating module [$ModulePath]" 81 | Set-Content -Path $ModulePath -Value $stringbuilder.ToString() 82 | } 83 | 84 | Task NextPSGalleryVersion -if (-Not ( Test-Path "$output\version.xml" ) ) -Before BuildPSD1 { 85 | $galleryVersion = Get-NextPSGalleryVersion -Name $ModuleName 86 | $galleryVersion | Export-Clixml -Path "$output\version.xml" 87 | } 88 | 89 | Task BuildPSD1 -inputs (Get-ChildItem $Source -Recurse -File) -Outputs $ManifestPath { 90 | 91 | Write-Output " Update [$ManifestPath]" 92 | Copy-Item "$source\$ModuleName.psd1" -Destination $ManifestPath 93 | 94 | $bumpVersionType = 'Patch' 95 | 96 | $functions = Get-ChildItem "$ModuleName\Public\*.ps1" | Where-Object { $_.name -notmatch 'Tests'} | Select-Object -ExpandProperty basename 97 | 98 | $oldFunctions = (Get-Metadata -Path $manifestPath -PropertyName 'FunctionsToExport') 99 | 100 | $functions | Where {$_ -notin $oldFunctions } | % {$bumpVersionType = 'Minor'} 101 | $oldFunctions | Where {$_ -notin $Functions } | % {$bumpVersionType = 'Major'} 102 | 103 | Set-ModuleFunctions -Name $ManifestPath -FunctionsToExport $functions 104 | 105 | # Bump the module version 106 | $version = [version] (Get-Metadata -Path $manifestPath -PropertyName 'ModuleVersion') 107 | $galleryVersion = Import-Clixml -Path "$output\version.xml" 108 | if ( $version -lt $galleryVersion ) 109 | { 110 | $version = $galleryVersion 111 | } 112 | Write-Output " Stepping [$bumpVersionType] version [$version]" 113 | $version = [version] (Step-Version $version -Type $bumpVersionType) 114 | Write-Output " Using version: $version" 115 | 116 | Update-Metadata -Path $ManifestPath -PropertyName ModuleVersion -Value $version 117 | } 118 | 119 | Task UpdateSource { 120 | Copy-Item $ManifestPath -Destination "$source\$ModuleName.psd1" 121 | } 122 | 123 | Task ImportModule { 124 | if ( -Not ( Test-Path $ManifestPath ) ) 125 | { 126 | Write-Output " Modue [$ModuleName] is not built, cannot find [$ManifestPath]" 127 | Write-Error "Could not find module manifest [$ManifestPath]. You may need to build the module first" 128 | } 129 | else 130 | { 131 | if (Get-Module $ModuleName) 132 | { 133 | Write-Output " Unloading Module [$ModuleName] from previous import" 134 | Remove-Module $ModuleName 135 | } 136 | Write-Output " Importing Module [$ModuleName] from [$ManifestPath]" 137 | Import-Module $ManifestPath -Force 138 | } 139 | } 140 | 141 | Task Publish { 142 | # Gate deployment 143 | if ( 144 | $ENV:BHBuildSystem -ne 'Unknown' -and 145 | $ENV:BHBranchName -eq "master" -and 146 | $ENV:BHCommitMessage -match '!deploy' 147 | ) 148 | { 149 | $Params = @{ 150 | Path = $BuildRoot 151 | Force = $true 152 | } 153 | 154 | Invoke-PSDeploy @Verbose @Params 155 | } 156 | else 157 | { 158 | "Skipping deployment: To deploy, ensure that...`n" + 159 | "`t* You are in a known build system (Current: $ENV:BHBuildSystem)`n" + 160 | "`t* You are committing to the master branch (Current: $ENV:BHBranchName) `n" + 161 | "`t* Your commit message includes !deploy (Current: $ENV:BHCommitMessage)" 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /spec/module.Steps.ps1: -------------------------------------------------------------------------------- 1 | Given 'the module was named (\S*)' { 2 | Param($Name) 3 | $Script:ModuleName = $Name 4 | 5 | $path = "$PSScriptRoot\.." 6 | $module = Get-ChildItem -Path $path -Recurse -Filter "$ModuleName.psm1" -verbose 7 | $module | should not benullorempty 8 | $module.fullname | Should Exist 9 | 10 | $Script:ModuleSource = "$PSScriptRoot\..\$ModuleName" 11 | $Script:ModuleOutput = "$PSScriptRoot\..\Output\$ModuleName" 12 | } 13 | 14 | Given 'we use the (\S*) root folder' { 15 | Param($root) 16 | Switch ($root) 17 | { 18 | 'Project' 19 | { 20 | $script:BaseFolder = Resolve-Path "$PSScriptRoot\.." | Select -ExpandProperty Path 21 | } 22 | 'ModuleSource' 23 | { 24 | $script:BaseFolder = $Script:ModuleSource 25 | } 26 | 'ModuleOutput' 27 | { 28 | $script:BaseFolder = $Script:ModuleOutput 29 | } 30 | } 31 | } 32 | 33 | Then 'it (will have|had) a (?\S*) (file|folder).*' { 34 | Param($Path) 35 | 36 | Join-Path $script:BaseFolder $Path | Should Exist 37 | } 38 | 39 | When 'the module (is|can be) imported' { 40 | { Import-Module $ModuleOutput -Force } | Should Not Throw 41 | } 42 | 43 | Then 'Get-Module will show the module' { 44 | Get-Module -Name $ModuleName | Should Not BeNullOrEmpty 45 | } 46 | 47 | Then 'Get-Command will list functions' { 48 | Get-Command -Module $ModuleName | Should Not BeNullOrEmpty 49 | } 50 | 51 | Then '(function )?(?\S*) will be listed in module manifest' { 52 | Param($Function) 53 | (Get-Content $ModuleSource\$ModuleName.psd1 -Raw) -match [regex]::Escape($Function) | Should Be $true 54 | } 55 | 56 | Then '(function )?(?\S*) will contain (?.*)' { 57 | Param($Function, $Text) 58 | $Command = Get-Command $Function -Module $ModuleName 59 | $match = [regex]::Escape($Text) 60 | ($Command.Definition -match $match ) | Should Be True 61 | } 62 | 63 | Then '(function )?(?\S*) will have comment based help' { 64 | Param($Function) 65 | $help = Get-Help $Function 66 | $help | Should Not BeNullOrEmpty 67 | } 68 | 69 | Then 'will have readthedoc pages' { 70 | { Invoke-Webrequest -uri "https://$ModuleName.readthedocs.io" -UseBasicParsing } | Should Not Throw 71 | } 72 | 73 | Then '(function )?(?\S*) will have a feature specification or a pester test' { 74 | param($Function) 75 | 76 | $file = Get-ChildItem -Path $PSScriptRoot\.. -Include "$Function.feature", "$Function.Tests.ps1" -Recurse 77 | $file.fullname | Should Not BeNullOrEmpty 78 | } 79 | 80 | Then 'all public functions (?.*)' { 81 | Param($Action) 82 | $step = @{keyword = 'Then'} 83 | $AllPassed = $true 84 | foreach ($command in (Get-Command -Module $ModuleName )) 85 | { 86 | $step.text = ('function {0} {1}' -f $command.Name, $Action ) 87 | 88 | Invoke-GherkinStep $step -Pester $Pester -Visible 89 | If ( -Not $Pester.TestResult[-1].Passed ) 90 | { 91 | $AllPassed = $false 92 | } 93 | 94 | $step.keyword = 'And' 95 | } 96 | $AllPassed | Should be $true 97 | } 98 | 99 | Then 'will be listed in the PSGallery' { 100 | Find-Module $ModuleName | Should Not BeNullOrEmpty 101 | } 102 | 103 | Given 'we have (?(public)) functions?' { 104 | param($folder) 105 | "$psscriptroot\..\*\$folder\*.ps1" | Should Exist 106 | } 107 | 108 | Then 'all script files pass PSScriptAnalyzer rules' { 109 | 110 | $Rules = Get-ScriptAnalyzerRule 111 | $scripts = Get-ChildItem $BaseFolder -Include *.ps1, *.psm1, *.psd1 -Recurse | where fullname -notmatch 'classes' 112 | 113 | $AllPassed = $true 114 | 115 | foreach ($Script in $scripts ) 116 | { 117 | $file = $script.fullname.replace($BaseFolder, '$') 118 | 119 | 120 | context $file { 121 | 122 | foreach ( $rule in $rules ) 123 | { 124 | It " [$file] Rule [$rule]" { 125 | 126 | (Invoke-ScriptAnalyzer -Path $script.FullName -IncludeRule $rule.RuleName ).Count | Should Be 0 127 | } 128 | } 129 | } 130 | 131 | If ( -Not $Pester.TestResult[-1].Passed ) 132 | { 133 | $AllPassed = $false 134 | } 135 | } 136 | 137 | $AllPassed | Should be $true 138 | } 139 | -------------------------------------------------------------------------------- /spec/module.feature: -------------------------------------------------------------------------------- 1 | Feature: A proper community module 2 | As a module owner 3 | In order to have a good community module 4 | I want to make sure everything works and the quality is high 5 | 6 | Background: we have a module 7 | Given the module was named PSHonolulu 8 | 9 | 10 | Scenario: Should have correct project structure and files 11 | Given we use the project root folder 12 | Then it will have a readme.md file for general information 13 | And it will have a LICENSE file 14 | And it will have a tests\*.Tests.ps1 file for Pester 15 | And it will have a spec\*.feature file for Gherkin 16 | And it will have a spec\*.Steps.ps1 file for Gherkin 17 | And it will have a build.ps1 file for builds 18 | And it will have a *.build.ps1 file for builds 19 | And it will have a *.PSDeploy.ps1 file for deployments 20 | And it will have a .gitignore file to ignore build artifacts 21 | And it will have a appveyor.yml file for build automation 22 | 23 | 24 | Scenario: Should have correct module structure in source 25 | Given we use the ModuleSource root folder 26 | Then it will have a *.psd1 file for module manifest 27 | And it will have a *.psm1 file for module 28 | And it will have a public folder for public functions 29 | And it will have a public\*.ps1 file for a public function 30 | 31 | Scenario: Should have correct module structure in source 32 | Given we use the ModuleOutput root folder 33 | Then it will have a *.psd1 file for module manifest 34 | And it will have a *.psm1 file for module 35 | 36 | Scenario: the module source should import 37 | Given we use the ModuleSource root folder 38 | And it had a *.psd1 file 39 | When the module is imported 40 | Then Get-Module will show the module 41 | And Get-Command will list functions 42 | 43 | Scenario: The built Module should import 44 | Given we use the ModuleOutput root folder 45 | And it had a *.psd1 file 46 | When the module is imported 47 | Then Get-Module will show the module 48 | And Get-Command will list functions 49 | 50 | Scenario: Public function features 51 | Given the module is imported 52 | And we have public functions 53 | Then all public functions will be listed in module manifest 54 | And all public functions will contain cmdletbinding 55 | And all public functions will contain ThrowTerminatingError 56 | 57 | Scenario: Should be well documented 58 | Given the module is imported 59 | And we use the project root folder 60 | And we have public functions 61 | Then it will have a readme.md file for general information 62 | And all public functions will have comment based help 63 | And function Node will have a feature specification or a pester test 64 | And all public functions will have a feature specification or a pester test 65 | And will have readthedoc pages 66 | And it will have a PITCHME.md file for project promotion 67 | 68 | @PSScriptAnalyzer @Slow 69 | Scenario: Should pass PSScriptAnalyzer rules 70 | Given we use the ModuleSource root folder 71 | Then it will have a public\*.ps1 file for a public function 72 | And all script files pass PSScriptAnalyzer rules 73 | When we use the ModuleOutput root folder 74 | Then all script files pass PSScriptAnalyzer rules 75 | 76 | @Slow 77 | Scenario: Should be published 78 | Given the module can be imported 79 | Then will be listed in the PSGallery 80 | 81 | -------------------------------------------------------------------------------- /tests/Feature.Tests.ps1: -------------------------------------------------------------------------------- 1 | $projectRoot = Resolve-Path "$PSScriptRoot\.." 2 | $script:ModuleName = 'PSHonolulu' 3 | 4 | Describe "Basic function feature tests" -Tags Build { 5 | 6 | } 7 | 8 | -------------------------------------------------------------------------------- /tests/Help.Tests.ps1: -------------------------------------------------------------------------------- 1 | $projectRoot = Resolve-Path "$PSScriptRoot\.." 2 | $script:ModuleName = 'PSHonolulu' 3 | 4 | Describe "Help tests for $moduleName" -Tags Build { 5 | 6 | $functions = Get-Command -Module $moduleName 7 | $help = $functions | % {Get-Help $_.name} 8 | foreach ($node in $help) 9 | { 10 | Context $node.name { 11 | 12 | it "has a description" { 13 | $node.description | Should Not BeNullOrEmpty 14 | } 15 | it "has an example" { 16 | $node.examples | Should Not BeNullOrEmpty 17 | } 18 | foreach ($parameter in $node.parameters.parameter) 19 | { 20 | if ($parameter -notmatch 'whatif|confirm') 21 | { 22 | it "parameter $($parameter.name) has a description" { 23 | $parameter.Description.text | Should Not BeNullOrEmpty 24 | } 25 | } 26 | } 27 | } 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /tests/Project.Tests.ps1: -------------------------------------------------------------------------------- 1 | $projectRoot = Resolve-Path "$PSScriptRoot\.." 2 | $script:ModuleName = 'PSHonolulu' 3 | $moduleRoot = "$projectRoot\$ModuleName" 4 | 5 | Describe "PSScriptAnalyzer rule-sets" -Tag Build { 6 | 7 | $Rules = Get-ScriptAnalyzerRule 8 | $scripts = Get-ChildItem $moduleRoot -Include *.ps1, *.psm1, *.psd1 -Recurse | where fullname -notmatch 'classes' 9 | 10 | foreach ( $Script in $scripts ) 11 | { 12 | Context "Script '$($script.FullName)'" { 13 | 14 | foreach ( $rule in $rules ) 15 | { 16 | It "Rule [$rule]" { 17 | 18 | (Invoke-ScriptAnalyzer -Path $script.FullName -IncludeRule $rule.RuleName ).Count | Should Be 0 19 | } 20 | } 21 | } 22 | } 23 | } 24 | 25 | 26 | Describe "General project validation: $moduleName" -Tags Build { 27 | 28 | It "Module '$moduleName' can import cleanly" { 29 | {Import-Module (Join-Path $moduleRoot "$moduleName.psm1") -force } | Should Not Throw 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Regression.Tests.ps1: -------------------------------------------------------------------------------- 1 | $projectRoot = Resolve-Path "$PSScriptRoot\.." 2 | $script:ModuleName = 'PSHonolulu' 3 | 4 | Describe "Regression tests" -Tag Build { 5 | 6 | Context "Github Issues" { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/Unit.Tests.ps1: -------------------------------------------------------------------------------- 1 | $projectRoot = Resolve-Path "$PSScriptRoot\.." 2 | $script:ModuleName = 'PSHonolulu' 3 | 4 | Describe "Basic function unit tests" -Tags Build { 5 | 6 | } 7 | --------------------------------------------------------------------------------