diff --git a/Contributing.md b/Contributing.md new file mode 100644 index 0000000..55afda9 --- /dev/null +++ b/Contributing.md @@ -0,0 +1,50 @@ +# How to Contribute + +Contributions to PSRemotely would be quite welcome and helpful. This page describes some ideas and guidelines around contributing. + +# What Should I Contribute? + +There are many ways to contribute, including: + +* Open an issue for... + * A bug you find + * A feature you think would be valuable + * A question on using PSRemotely (you might not be the only one with the question) +* Edit the docs to... + * Improve or expand on [existing docs](https://PSRemotely.readthedocs.io) + * Document a deployment type + * Write a 'How do I...' scenario +* Submit pull requests to... + * Improve or expand on the [Readme.md](https://github.com/DexterPOSH/PSRemotely/blob/master/README.md) or this Contributing.md + * Provide bug fixes, new features, cleaner code, or other improvements + +# How Do I Contribute? + +All of these require that you [have a GitHub account](https://github.com/signup/free). + +* [Submit an issue](https://github.com/DexterPOSH/PSRemotely/issues) + * Use the search box and flip through open/closed issues to avoid duplication + * If the issue is for a bug fix, provide reproducible code. If you can't, and you think an issue is still warranted, provide your code, and related details on your environment +* Contribute to [the docs](https://PSRemotely.readthedocs.io) + * Fork the repo + * Checkout and work in the master branch + * Organization is described in the [mkdocs.yml](https://github.com/DexterPOSH/PSRemotely/blob/master/mkdocs.yml) file. If you add a file or section, mkdocs.yml needs to know + * mkdocs.yml points to markdown files in [the docs folder](https://github.com/DexterPOSH/PSRemotely/tree/master/docs) + * Images are stored and accessible from docs/images + * Commit changes + * Submit a pull request to the master branch +* Submit a pull request + * Fork the repo + * Checkout and work in the master branch + * Where possible, add Pester tests for your change + * Submit your pull request to the master branch + +# Additional Resources +* [General GitHub documentation](https://help.github.com/) +* [GitHub forking documentation](https://guides.github.com/activities/forking/) +* [GitHub pull request documentation](https://help.github.com/send-pull-requests/) +* [GitHub Flow guide](https://guides.github.com/introduction/flow/) +* [GitHub's guide to contributing to open source projects](https://guides.github.com/activities/contributing-to-open-source/) + +More to come, just getting this out there. Thanks to RamblingCookieMonster's PSDeploy module for the module organization. + diff --git a/psake.ps1 b/InvokeBuild.ps1 similarity index 74% rename from psake.ps1 rename to InvokeBuild.ps1 index 0b74de6..28fa48e 100644 --- a/psake.ps1 +++ b/InvokeBuild.ps1 @@ -1,12 +1,27 @@ +<# +.Synopsis + Build script invoked by Invoke-Build. + +.Description + TODO: Declare build parameters as standard script parameters. Parameters + are specified directly for Invoke-Build if their names do not conflict. + Otherwise or alternatively they are passed in as "-Parameters @{...}". +#> + +# TODO: [CmdletBinding()] is optional but recommended for strict name checks. +[CmdletBinding()] +param( +) # PSake makes variables declared here available in other scriptblocks # Init some things -Properties { +# TODO: Move some properties to script param() in order to use as parameters. + # Find the build folder based on build system - $ProjectRoot = $ENV:BHProjectPath - if(-not $ProjectRoot) - { - $ProjectRoot = $PSScriptRoot - } + $ProjectRoot = $ENV:BHProjectPath + if(-not $ProjectRoot) + { + $ProjectRoot = $PSScriptRoot + } $Timestamp = Get-date -uformat "%Y%m%d-%H%M%S" $PSVersion = $PSVersionTable.PSVersion.Major @@ -18,11 +33,11 @@ Properties { { $Verbose = @{Verbose = $True} } -} -Task Default -Depends Deploy +# TODO: Default task. If it is the first then any name can be used instead. +task . Deploy -Task Init { +task Init { $lines Set-Location $ProjectRoot "Build System Details:" @@ -30,7 +45,7 @@ Task Init { "`n" } -Task Test -Depends Init { +task Test Init, { $lines foreach ($TestType in @('Unit','Integration')) { @@ -40,9 +55,6 @@ Task Test -Depends Init { # Gather test results. Store them in a variable and file $TestResults = Invoke-Pester -Path "$ProjectRoot\Tests\$TestType" -PassThru -OutputFormat NUnitXml -OutputFile "$ProjectRoot\$TestFile" - - "`n`tSTATUS: Integration testing with PowerShell $PSVersion" - # In Appveyor? Upload our tests! #Abstract this into a function? If($ENV:BHBuildSystem -eq 'AppVeyor') { @@ -57,7 +69,7 @@ Task Test -Depends Init { # Need to tell psake or it will proceed to the deployment. Danger! if($TestResults.FailedCount -gt 0) { - Write-Error "Failed '$($TestResults.FailedCount)' $TestType tests, build failed" + throw "Failed '$($TestResults.FailedCount)' $TestType tests, build failed" break # break out if any of the test fails } "`n" @@ -65,7 +77,7 @@ Task Test -Depends Init { } -Task Build -Depends Test { +task Build Test, { $lines # Load the module, read the exported functions, update the psd1 FunctionsToExport @@ -83,7 +95,7 @@ Task Build -Depends Test { } } -Task Deploy -Depends Build { +task Deploy Build, { $lines $Params = @{ @@ -92,4 +104,4 @@ Task Deploy -Depends Build { Recurse = $false # We keep psdeploy artifacts, avoid deploying those : ) } Invoke-PSDeploy @Verbose @Params -} \ No newline at end of file +} diff --git a/PSRemotely/PSRemotely.json b/PSRemotely/PSRemotely.json index 707027d..4c28b85 100644 --- a/PSRemotely/PSRemotely.json +++ b/PSRemotely/PSRemotely.json @@ -7,6 +7,6 @@ } ], "ArtifactsRequired":[ - "DeploymentManifest.xml" + "" ] } \ No newline at end of file diff --git a/PSRemotely/PSRemotely.psd1 b/PSRemotely/PSRemotely.psd1 index 3321a41..2a49271 100644 Binary files a/PSRemotely/PSRemotely.psd1 and b/PSRemotely/PSRemotely.psd1 differ diff --git a/PSRemotely/PSRemotely.psm1 b/PSRemotely/PSRemotely.psm1 index 637b6e8..88fe016 100644 --- a/PSRemotely/PSRemotely.psm1 +++ b/PSRemotely/PSRemotely.psm1 @@ -28,6 +28,9 @@ $PSRemotely = ConvertPSObjectToHashTable -InputObject $jsonObject # module variables $PSRemotely.Add('NodeMap', @()) $PSRemotely.Add('sessionHashTable', @{}) +# Setting the default value for New-PSSession to use the EnabletNetworkAccess switch +# Found an issue with the Appveyor VM, which won't let the tests in place create the loopback sessions. +$PSDefaultParameterValues=@{"New-PSSession:EnableNetworkAccess"=$True} # create an alias for PSRemotely New-Alias -Name Remotely -Value PSRemotely -Description "Remote Ops validation, using PSRemotely." diff --git a/PSRemotely/private/ConfigurationData.ps1 b/PSRemotely/private/ConfigurationData.ps1 index d8ae1e8..2218d29 100644 --- a/PSRemotely/private/ConfigurationData.ps1 +++ b/PSRemotely/private/ConfigurationData.ps1 @@ -25,7 +25,7 @@ Function Test-ConfigData { # Check for the duplicate nodeName Values if ($NodeNames.Contains($node['NodeName'])) { # Duplicate node in the list - throw ("There is a duplicate NodeName '{0}' in the configurationData passed in." -f $node['NodeName']) + throw $("There is a duplicate NodeName '{0}' in the configurationData passed in." -f $node['NodeName']) } $null = $NodeNames.Add($node['NodeName']) @@ -41,8 +41,8 @@ Function Update-ConfigData { [Hashtable]$ConfigurationData ) - if ($configurationData.AllNodes | Where -Property NodeName -eq '*') { - $AllNodeSettings = $configurationData.AllNodes | Where -Property NodeName -eq '*' + if ($configurationData.AllNodes | Where-Object -Property NodeName -eq '*') { + $AllNodeSettings = $configurationData.AllNodes | Where-Object -Property NodeName -eq '*' } # Copy all the settings for the nodes diff --git a/PSRemotely/public/PSRemotely.ps1 b/PSRemotely/public/PSRemotely.ps1 index 683adb8..253de7d 100644 --- a/PSRemotely/public/PSRemotely.ps1 +++ b/PSRemotely/public/PSRemotely.ps1 @@ -100,7 +100,7 @@ function PSRemotely # Define the AllNodes variable in current scope Write-VerboseLog -Message 'Creating the AllNodes global scope variable' - New-Variable -Name AllNodes -Value $configurationData.AllNodes -Scope Script -Force + New-Variable -Name AllNodes -Value $configurationData.AllNodes -Scope Global -Force if ($AllNodes.NodeName) { Write-VerboseLog -Message 'Creating sessions' @@ -145,7 +145,7 @@ function PSRemotely & $Body # invoke the body Write-VerboseLog -Message 'Clearing the AllNodes global variable' - Remove-Variable -Name AllNodes -Scope Script -Force -ErrorAction SilentlyContinue + Remove-Variable -Name AllNodes -Scope Global -Force -ErrorAction SilentlyContinue } diff --git a/README.md b/README.md index d1dfe26..cec0c0f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build status](https://ci.appveyor.com/api/projects/status/w22ytbuyjr7a10ia/branch/master?svg=true)](https://ci.appveyor.com/project/DexterPOSH/psremotely/branch/master) [![Documentation Status](http://psremotely.readthedocs.io/en/latest/?badge=latest)](http://psremotely.readthedocs.io/en/latest/?badge=latestt) +[![Build status](https://ci.appveyor.com/api/projects/status/w22ytbuyjr7a10ia/branch/master?svg=true)](https://ci.appveyor.com/project/DexterPOSH/psremotely/branch/master) [![Documentation Status](https://readthedocs.org/projects/psremotely/badge/?version=latest)](https://readthedocs.org/projects/psremotely/badge/?version=latest) PSRemotely ============ @@ -31,7 +31,7 @@ PSRemotely workflow is as under : Well this is a term coined by us for some of the infrastucture validation being done for Engineered solutions. So taking liberty to use this here. -Suppose, you already have below existing Pester test for some nodes in our environment: +Suppose, you already have below Pester test for some nodes in our environment: ```powershell Describe 'Bits Service test' { @@ -47,7 +47,8 @@ Describe 'Bits Service test' { } } ``` -If you want to run the very same tests on the remote node named say AD, then below is how you use PSRemotely. +If you want to target the very same tests on the remote node named say Node1,Node2 & Node3 from your current workstation, you can use PSRemotely. + Usage with PSRemotely: @@ -71,10 +72,10 @@ PSRemotely { } } ``` -Once you have the tests file ready, below is how you invoke the PSRemotely framework to start the remote ops validation : +Once you have the tests file ready , save it with a .PSRemotely.ps1 extension, below is how you invoke the PSRemotely framework to start the remote ops validation : ```powershell -Invoke-PSRemotely -Script +Invoke-PSRemotely -Script .PSRemotely.ps1 ``` Output of the above is a JSON object, if the tests pass then an empty JSON object array of *TestResult* is returned @@ -90,7 +91,7 @@ otherwise the Error record thrown by Pester is returned : ], "Result": true, - "Name": "Service test" + "Name": "Bits Service test" } ] } @@ -120,7 +121,7 @@ otherwise the Error record thrown by Pester is returned : ``` ## More Information -The [PSRemotely docs](psremotely.readthedocs.io) will include more information, including : +The [PSRemotely docs](http://psremotely.readthedocs.io/) will include more information, including : * PSRemotely Basics * PSRemotely Examples @@ -139,11 +140,11 @@ Thanks goes to : ## TO DO PSRemotely in its current form works for our needs to validate some of the Engineered solutions. -But it has the potential to grow to be much bigger, below are some of the items in the wishlist : +But it has the potential to grow into something bigger, below are some of the items in the wishlist : - Use PowerShell classes, and add support for providers for nodes running either On-prem or on Cloud (AWS or Azure). - Integrate with JEA, since PSRemotely uses PSSession endpoints these can be locked down using JEA on the nodes. -- More unit tests, lacking far behind in this aspect. +- More unit tests, lacking far behind in this aspect. More focus on it once we start working with the classes implementation. - Faster background processing of the remote node jobs, using runspaces. Feel free to submit any ideas, bugs or pull requests. \ No newline at end of file diff --git a/Tests/Integration/Basic.Integration.Tests.ps1 b/Tests/Integration/Basic.Integration.Tests.ps1 index 1ef9382..5ede2b8 100644 --- a/Tests/Integration/Basic.Integration.Tests.ps1 +++ b/Tests/Integration/Basic.Integration.Tests.ps1 @@ -12,14 +12,16 @@ $ArtifactsPath = "$Env:BHPSModulePath\lib\Artifacts" # Import the TestHelpers Get-ChildItem -Path "$env:BHProjectPath\Tests\TestHelpers\*.psm1" | Foreach-Object { - Import-Module -Name $PSItem -verbose -Force + Remove-Module -Name $PSitem.BaseName -Force -ErrorAction SilentlyContinue # reload the module, the script module might have changes + Import-Module -Name $PSItem.FullName -Force } # use a dummy artifact with PSRemotely- Set-PSRemotelyToUseDummyArtifact -Path $RemotelyJSONFile Copy-DummyArtifact -Path "$ArtifactsPath\DeploymentManifest.xml" - +Get-PSSession | Remove-PSSession # clean up older PSSessions created during the Unit tests +Remove-Variable -Name AllNodes,PSRemotely -Scope Global -Force -ErrorAction SilentlyContinue # Just a fail safe Remove-Module $ENV:BHProjectName -ErrorAction SilentlyContinue Import-Module (Join-Path $ENV:BHProjectPath $ENV:BHProjectName) -Force @@ -155,7 +157,7 @@ try { } It 'Should return more details about the test failed in the TestResult' { - $Object.Tests[0].TestResult.Describe | Should BeExactly 'Bits Service Test' + $Object.Tests[0].TestResult.Describe | Should Be 'Bits Service Test' $Object.Tests[0].TestResult.Name | Should BeExactly 'Should be running' $Object.Tests[0].TestResult.Result | Should Be 'Failed' $Object.Tests[0].TestResult.ErrorRecord | Should NOT BeNullOrEmpty diff --git a/Tests/Integration/ConfigData.Integration.Tests.ps1 b/Tests/Integration/ConfigData.Integration.Tests.ps1 index 143c360..7b7a754 100644 --- a/Tests/Integration/ConfigData.Integration.Tests.ps1 +++ b/Tests/Integration/ConfigData.Integration.Tests.ps1 @@ -169,7 +169,7 @@ Foreach ($RemotelyTestFile in $RemotelyTestFiles) { } It 'Should return more details about the test failed in the TestResult' { - $Object.Tests[0].TestResult.Describe | Should BeExactly 'Bits Service Test' + $Object.Tests[0].TestResult.Describe | Should Be 'Bits Service Test' $Object.Tests[0].TestResult.Name | Should BeExactly 'Should be running' $Object.Tests[0].TestResult.Result | Should Be 'Failed' $Object.Tests[0].TestResult.ErrorRecord | Should NOT BeNullOrEmpty diff --git a/Tests/Integration/Generic.Integration.Tests.ps1 b/Tests/Integration/Generic.Integration.Tests.ps1 index 2a5fb16..7ac149f 100644 --- a/Tests/Integration/Generic.Integration.Tests.ps1 +++ b/Tests/Integration/Generic.Integration.Tests.ps1 @@ -12,7 +12,8 @@ $ArtifactsPath = "$Env:BHPSModulePath\lib\Artifacts" # Import the TestHelpers Get-ChildItem -Path "$env:BHProjectPath\Tests\TestHelpers\*.psm1" | Foreach-Object { - Import-Module -Name $PSItem -verbose -Force + Remove-Module -Name $PSitem.BaseName -Force -ErrorAction SilentlyContinue # reload the module, the script module might have changes + Import-Module -Name $PSItem.FullName -Force } Remove-Module $ENV:BHProjectName -ErrorAction SilentlyContinue diff --git a/Tests/Integration/ReRunTest.Integration.Tests.ps1 b/Tests/Integration/ReRunTest.Integration.Tests.ps1 index 218ebc9..07785f3 100644 --- a/Tests/Integration/ReRunTest.Integration.Tests.ps1 +++ b/Tests/Integration/ReRunTest.Integration.Tests.ps1 @@ -21,7 +21,8 @@ Import-Module (Join-Path $ENV:BHProjectPath $ENV:BHProjectName) -Force # Import the TestHelpers Get-ChildItem -Path "$env:BHProjectPath\Tests\TestHelpers\*.psm1" | Foreach-Object { - Import-Module -Name $PSItem -verbose -Force + Remove-Module -Name $PSitem.BaseName -Force -ErrorAction SilentlyContinue # reload the module, the script module might have changes + Import-Module -Name $PSItem.FullName -Force } try { @@ -117,7 +118,7 @@ try { } It 'Should return more details about the test failed in the TestResult' { - $Object.Tests[0].TestResult.Describe | Should BeExactly 'Bits Service Test' + $Object.Tests[0].TestResult.Describe | Should Be 'Bits Service Test' $Object.Tests[0].TestResult.Name | Should BeExactly 'Should be running' $Object.Tests[0].TestResult.Result | Should Be 'Failed' $Object.Tests[0].TestResult.ErrorRecord | Should NOT BeNullOrEmpty @@ -187,7 +188,7 @@ try { } It 'Should return more details about the test failed in the TestResult' { - $Object.Tests[0].TestResult.Describe | Should BeExactly 'Bits Service Test' + $Object.Tests[0].TestResult.Describe | Should Be 'Bits Service Test' $Object.Tests[0].TestResult.Name | Should BeExactly 'Should be running' $Object.Tests[0].TestResult.Result | Should Be 'Failed' $Object.Tests[0].TestResult.ErrorRecord | Should NOT BeNullOrEmpty diff --git a/Tests/Unit/Node.tests.ps1 b/Tests/Unit/Node.tests.ps1 index 576fab7..b0769df 100644 --- a/Tests/Unit/Node.tests.ps1 +++ b/Tests/Unit/Node.tests.ps1 @@ -8,10 +8,13 @@ $PSVersion = $PSVersionTable.PSVersion.Major Remove-Module $ENV:BHProjectName -ErrorAction SilentlyContinue Import-Module (Join-Path $ENV:BHProjectPath $ENV:BHProjectName) -Force + + + InModuleScope -ModuleName $ENV:BHProjectName { Get-Service -Name WinRM | Restart-Service - $Session = New-PSSession -ComputerName Localhost + $Session = New-PSSession -ComputerName Localhost -EnableNetworkAccess Describe 'Node' { diff --git a/Tests/Unit/bootstrap.Tests.ps1 b/Tests/Unit/bootstrap.Tests.ps1 index aea1da8..d4b90ff 100644 --- a/Tests/Unit/bootstrap.Tests.ps1 +++ b/Tests/Unit/bootstrap.Tests.ps1 @@ -8,7 +8,6 @@ $PSVersion = $PSVersionTable.PSVersion.Major Remove-Module $ENV:BHProjectName -ErrorAction SilentlyContinue Import-Module (Join-Path $ENV:BHProjectPath $ENV:BHProjectName) -Force - InModuleScope -ModuleName $ENV:BHProjectName { # Module Preamble - put initialization code here $ModuleRequired = @( # Array of the hashtables for FullyQualifedNames for modules @@ -21,7 +20,7 @@ InModuleScope -ModuleName $ENV:BHProjectName { ModuleVersion='1.1.10'; }) Get-Service -Name WinRM | Restart-Service - $Session = New-PSSession -ComputerName Localhost + $Session = New-PSSession -ComputerName Localhost -EnableNetworkAccess Describe "BootStrapRemotelyNode $PSVersion" -Tags UnitTest { @@ -76,6 +75,7 @@ Describe "BootStrapRemotelyNode $PSVersion" -Tags UnitTest { It "Should archive the existing tests files on the PSRemotely path on the node" { Assert-MockCalled -CommandName CleanupPSRemotelyNodePath -Times 1 -Exactly -Scope Context + } It 'Should update the PSRemotely variable at the beginning and end of the bootstrap function' { diff --git a/appveyor.yml b/appveyor.yml index 4406096..015aefb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -23,4 +23,4 @@ build: false #Kick off the CI/CD pipeline test_script: - - ps: . .\build.ps1 \ No newline at end of file + - ps: . .\build.ps1 diff --git a/build.ps1 b/build.ps1 index 8e7d201..395f849 100644 --- a/build.ps1 +++ b/build.ps1 @@ -45,9 +45,10 @@ function Resolve-Module # Grab nuget bits, install modules, set build variables, start build. Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null -Resolve-Module Psake, Pester, BuildHelpers, PSScriptAnalyzer +Resolve-Module InvokeBuild, Pester, BuildHelpers, PSScriptAnalyzer, PSDeploy + Set-BuildEnvironment -Invoke-psake .\psake.ps1 -exit ( [int]( -not $psake.build_success ) ) \ No newline at end of file +Invoke-Build -File .\InvokeBuild.ps1 +# exit ( [int]( -not $psake.build_success ) ) diff --git a/docs/Quick-Start.-Installation-and-Example.md b/docs/Quick-Start.-Installation-and-Example.md index 335163c..76c3464 100644 --- a/docs/Quick-Start.-Installation-and-Example.md +++ b/docs/Quick-Start.-Installation-and-Example.md @@ -16,7 +16,6 @@ Get-Command -Module PSRemotely # Get help for the module and a command - Get-Help about_PSRemotely Get-Help Invoke-PSRemotely -full # *.tests.ps1 based operation validation ``` @@ -24,15 +23,12 @@ All you need is a *.Tests.ps1 file that tell PSRemotely about your remote operation validation. Here's a quick example. -Here are some source files I want to deploy: -[![Source](images/DirFrom.png)](images/DirFrom.png) - -Here's my *.tests.ps1 file +Here's my sample.PSRemotely.ps1 file ```powershell Remotely { - Node localhost { + Node AD, DNS { Describe 'Bits Service test' { $BitsService = Get-Service -Name Bits @@ -41,7 +37,7 @@ Remotely { $BitsService | Should Not BeNullOrEmpty } - it 'Should be running' { + It 'Should be running' { $BitsService.Status | Should be 'Running' } } @@ -54,10 +50,12 @@ Remotely { We invoke the PSRemotely similar to Invoke-Pester: ```powershell -PS C:\PSRemotely> Invoke-PSRemotely +PS C:\PSRemotely> Invoke-PSRemotely .\sample.PSRemotely.ps1 ``` -Your Remote operation validations are parsed and carried out: +Your Remote operation validations are parsed and carried out on the remote nodes and output is +presented in the JSON format. + + -[![GCI Output](images/QuickStart.AfterInvoke.png)](images/QuickStart.AfterInvoke.png) diff --git a/psremotely.psdeploy.ps1 b/psremotely.psdeploy.ps1 index 667b952..7b85d2d 100644 --- a/psremotely.psdeploy.ps1 +++ b/psremotely.psdeploy.ps1 @@ -22,7 +22,8 @@ if( $env:BHPSModulePath -and $env:BHBuildSystem -ne 'Unknown' -and $env:BHBranchName -eq "master" -and - $env:BHCommitMessage -match '!deploy' + $env:BHCommitMessage -match '!deploy' -and + $env:NugetApiKey -ne $Null ) { Deploy Module { @@ -40,7 +41,8 @@ else "Skipping deployment: To deploy, ensure that...`n" + "`t* You are in a known build system (Current: $ENV:BHBuildSystem)`n" + "`t* You are committing to the master branch (Current: $ENV:BHBranchName) `n" + - "`t* Your commit message includes !deploy (Current: $ENV:BHCommitMessage)" | + "`t* Your commit message includes !deploy (Current: $ENV:BHCommitMessage) `n" + + "`t* The NugetAPIKey in the environment varaible is not defined"| Write-Host }