DSC PullServer automation with GitLab
Prerequisites
- Install a Pullserver 1
- Install GitLab runner on the Pullserver with the "shell" executor and tags "shell", "powershell5", "windows" and "pullserver".
- Create a gitlab project where the Project Name is the name of your intended DSC config. e.g. "Finance", omitting spaces and punctuation.
- Register the Gitlab runner with your DSC configuration project in GitLab.
- Stop the Gitlab Runner service and open c:\Gitlab-Runner\config.toml in a text editor.
- Change the
[[runners]]>shellsetting from "pwsh" to "powershell" and restart the Gitlab Runner service. - Add a .gitignore file with contents
[PROJECT TITLE]/to ignore the compiled mof. - Add .gitlab-ci.yml file to your project with the following contents
stages:
- dependencies
- test
- run
manage_dependencies:
stage: dependencies
tags:
- shell # <-- executor, must change config.toml shell from "pwsh" to "powershell"
- powershell5 # <-- only use runner with powershell 5
- windows # <-- only use windows runner
- pullserver # <-- only use runner with Pullserver installed
script: |
# Configure Environment
$ErrorActionPreference = 'Stop'
Set-Location -Path $env:CI_PROJECT_DIR
# Initialize variables
[string[]]$detectedmodules = @()
[string[]]$stagedmodules = @()
[string[]]$ignoremodules = "PSDesiredStateConfiguration"
[string]$modulespath = 'C:\Program Files\WindowsPowerShell\DscService\Modules'
write-warning -Message "This script does not upgrade existing modules. Please test your scripts with modules already installed on the pull server."
write-output "Setting PSModulePath..."
$env:PSModulePath = 'C:\Program Files\WindowsPowerShell\Modules;C:\Program Files (x86)\WindowsPowerShell\Modules;C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules'
Write-Output "Detecting required modules..."
Get-Content -Path "$($env:CI_PROJECT_DIR)\$($env:CI_PROJECT_TITLE).ps1" | Where-Object {$_ -match '(import-dscresource\s+)'} | ForEach-Object {
$detectedmodules += $_.ToString().Trim().Split(" ") | Select-Object -Last 1
}
$detectedmodules = $detectedmodules | Where-Object{$_ -notcontains $ignoremodules}
Write-Output "Getting currently staged modules..."
Get-ChildItem -Path $modulespath -Filter *.zip | ForEach-Object {
$stagedmodules += $_.Name.ToString().Trim().Split("_")[0]
}
Write-Output "Checking for modules in the runners's powershell library..."
foreach($detectedmodule In $detectedmodules){
write-output "Checking for $detectedmodule..."
$checkmod = Get-DscResource -Module $detectedmodule -WarningAction SilentlyContinue
If($checkmod){
write-output "$detectedmodule already installed."
}else{
Write-Output "Installing $detectedmodule..."
Install-Module -Name $detectedmodule
}
}
Write-Output "Identifying modules missing from the pullserver repository..."
$missingmodules = $detectedmodules | Where {$stagedmodules -NotContains $_}
foreach($missingmodule In $missingmodules){
Write-Output "Downloading $missingmodule..."
Save-Module -Name $missingmodule -Path .
Write-Output "Unhiding files to enable compression..."
Get-ChildItem -path ".\$missingmodule\$version\*" -force -Recurse | where{$_.Attributes -match "hidden"} | foreach{$_.Attributes=""}
Write-Output "Compressing module..."
$version = (Get-ChildItem -Path ".\$missingmodule").Name
$archive = '.\' + $missingmodule + '_' + $version + '.zip'
Compress-Archive -Path ".\$missingmodule\$version\*" -DestinationPath $archive
Write-Output "Creating checksum..."
New-DscChecksum -Path $archive -OutPath .
write-output "Copying modules to module share..."
Copy-Item -Path $archive -Destination $modulespath
write-output "Copying checksum to module share..."
Copy-Item -Path "$archive.checksum" -Destination $modulespath
}
only:
- pushes
test_dsc:
stage: test
tags:
- shell
- powershell5
- windows
- pullserver
script: |
BeforeAll {
. "$($env:CI_PROJECT_DIR)\$($env:CI_PROJECT_TITLE).ps1"
}
Describe 'Pester-Test' {
It 'Passes Script Analyzer' {
Invoke-ScriptAnalyzer -Path "$($env:CI_PROJECT_DIR)\$($env:CI_PROJECT_TITLE).ps1" -Severity Error | Should -BeNullOrEmpty
}
It 'Generated MOF' {
"$($env:CI_PROJECT_DIR)\$($env:CI_PROJECT_TITLE)\localhost.mof" | Should -Exist
}
}
AfterAll {
If(Test-Path -Path "$($env:CI_PROJECT_DIR)\$($env:CI_PROJECT_TITLE)\localhost.mof"){Remove-Item -Path "$($env:CI_PROJECT_DIR)\$($env:CI_PROJECT_TITLE)\localhost.mof"}
}
only:
- pushes
run_dsc:
stage: run
tags:
- shell
- powershell5
- windows
- pullserver
script: |
# Configure Environment
$ErrorActionPreference = 'Stop'
Set-Location -Path $env:CI_PROJECT_DIR
# Initialize variables
[string]$configpath = 'C:\Program Files\WindowsPowerShell\DscService\Configuration'
write-output "Setting PSModulePath..."
$env:PSModulePath = 'C:\Program Files\WindowsPowerShell\Modules;C:\Program Files (x86)\WindowsPowerShell\Modules;C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules'
Write-Output "Writing MOF..."
Try{
& "$($env:CI_PROJECT_DIR)\$($env:CI_PROJECT_TITLE).ps1" -Verbose
}Catch{
Throw $_.Exception.Message
}
Write-Output "Creating Checksum..."
New-DscChecksum -Path "$($env:CI_PROJECT_DIR)\$($env:CI_PROJECT_TITLE)\localhost.mof"
Write-Output "Renaming MOF file..."
Rename-Item -Path "$($env:CI_PROJECT_DIR)\$($env:CI_PROJECT_TITLE)\localhost.mof" -NewName "$($env:CI_PROJECT_TITLE).mof"
Write-Output "Renaming checksum file..."
Rename-Item -Path "$($env:CI_PROJECT_DIR)\$($env:CI_PROJECT_TITLE)\localhost.mof.checksum" -NewName "$($env:CI_PROJECT_TITLE).mof.checksum"
Write-Output "Copying MOF to $configpath..."
Copy-Item -Path "$($env:CI_PROJECT_DIR)\$($env:CI_PROJECT_TITLE)\$($env:CI_PROJECT_TITLE).mof" -Destination $configpath
Write-Output "Copying checksum to $configpath..."
Copy-Item -Path "$($env:CI_PROJECT_DIR)\$($env:CI_PROJECT_TITLE)\$($env:CI_PROJECT_TITLE).mof.checksum" -Destination $configpath
only:
- master
1 If your Pullserver cannot reach the internet, consider creating a local Nuget server to host required modules and add a Register-PSrepository command to the Configure Environment section of the Dependencies stage to register your internal Nuget server. You will need to upload all required modules and resources to the internal Nuget Server.
Document Actions
