Recently I was dealing with solving the bootstrap process on the Linux container which is running Powershell Core version in order to update the Nuget feed server.

This might sound, but it’s not really a trivial task, that’s one of the reasons why I decided to blog about this topic.

Scenario: We have our Powershell repository hosted on GitLab where our modules are stored, there is also a Nuget Server which is acting as a local package repository(Module repository) for our infrastructure, infra is being hosted on AWS. Obviously, we want our Nuget server to stay up to date with the code that we have stored in GitLab repo.

Steps:

Install Powershell Core per documentation on your Nuget server, configure the server to receive SSH connections(Powershell remoting over SSH), since we are doing everything from the code, there is a snippet for accomplishing just that. I have to point out that since our infra is on AWS, we are using lots of their services in combination with AWSPowershell Module.

https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows?view=powershell-6

https://docs.microsoft.com/en-us/powershell/scripting/learn/remoting/ssh-remoting-in-powershell-core?view=powershell-6

# We have stored preconfigured sshd_config in S3, I am sharing how it looks like as well.

PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no
Subsystem powershell c:\pwsh\pwsh.exe -sshs -NoLogo -NoProfile

# Now Powershell part
$PSCore ='https://github.com/PowerShell/PowerShell/releases/download/v6.2.2/PowerShell-6.2.2-win-x64.msi'
$PSCoreFile = $PSCore.Split("/")[-1]
Invoke-WebRequest -Uri $PSCore -Outfile "C:\$PSCoreFile"
msiexec.exe /package "C:\$PSCoreFile" /quiet ; start-sleep 3
Get-WindowsCapability -Online |?{$_.name -like '*ssh*'} | Add-WindowsCapability -Online
New-Item -ItemType SymbolicLink -Path C:\pwsh -Value 'C:\Program Files\PowerShell\6'
Set-Service SSHD -StartupType Automatic ; Start-Service SSHD
$SSHConfig = Get-S3object -BucketName S3-Bucket | ?{$_.key -like "*sshd_config*"}
$SSHConfig | Copy-S3Object -LocalFile $env:programdata\ssh\sshd_config
$CIKey = (Get-SSMParameter -Name "nuget/ci_public_key" -WithDecryption $true).value
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[system.io.file]::WriteAllLines("$env:userprofile\.ssh\authorized_keys",$CIKey,$Utf8NoBomEncoding)
Restart-Service SSHD

Now it’s time to bootstrap the Linux container on the GitLab site.

This is how our .gitlab-ci.yml file looks like, first we export needed variables to access some of the AWS services, then we install OpenSSH client which is needed by Powershell Core, then we install dot net core, NuGet client tool, and dot net core SDK, last step is actually invocation of the Powershell script.

- export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
- export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
- export AWS_DEFAULT_REGION=our-region-1
- export PATH="$PATH/root/.dotnet"
- apt-get update -y
- apt-get install openssh-client -y
- apt-get install wget -y
- wget https://dot.net/v1/dotnet-install.sh
- chmod +x dotnet-install.sh
- ./dotnet-install.sh
- apt install gnupg ca-certificates -y
- apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
- echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | tee /etc/apt/sources.list.d/mono-official-stable.list
- apt update -y
- apt install mono-devel -y
- apt-get install curl -y
- curl -o /usr/local/bin/nuget.exe https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
- chmod +x "/usr/local/bin/nuget.exe"
- wget -q https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
- dpkg -i packages-microsoft-prod.deb
- apt-get install apt-transport-https -y
- apt-get update -y
- apt-get install dotnet-sdk-2.2 -y
- pwsh -command ".\build.ps1"

Now we are coming to the part of build.ps1 file which is the final step of our Pipeline.

Install-Module PowershellGet -Repository PSGallery -Confirm:$false -Force
New-Alias -Name Nuget -Value "/usr/local/bin/nuget.exe"
$TargetHost = "nuget.feeed.server"
Register-PSRepository -Name $TargetHost -SourceLocation "http://$($TargetHost):8443/nuget" -PublishLocation "http://$($TargetHost):8443/nuget"
Install-Module AWSPowershell -Repository $TargetHost -Confirm:$false -Force; Import-Module AWSPowershell
$SshKey = (Get-SSMParameter -Name "nuget/ci_private_key" -WithDecryption:$true).value
$SshKey | out-file .\id_rsa
chmod 400 id_rsa
mkdir ~/.ssh
ssh-keyscan -H $TargetHost >> ~/.ssh/known_hosts
$Session = New-PSSession -HostName $TargetHost -UserName "ouradmin" -KeyFilePath .\id_rsa
Invoke-Command -Session $Session -ScriptBlock {
# We wipe with our magic the module from the Nuget feed server.
}
Copy-Item -ToSession $Session -Path .\ModuleName\ -Recurse -Destination "C:\Windows\System32\WindowsPowerShell\v1.0\Modules\" -Force
$ApiKey = (Get-SsmParameter -Name "nuget/api_key" -WithDecryption:$true).value
Copy-Item -Path .\ModuleName\ -Recurse -Destination "/root/.local/share/powershell/Modules/"
Publish-Module -Name ModuleName -NuGetApiKey $ApiKey -Repository $TargetHost -Confirm:$false