Background
I have a .NET application that I would like to install inside a Nano Server Docker container, specifically build 1809. The app is basically a REST server which will receive a REST request and depending on the contents of the JSON, invoke a particular PowerShell cmdlet on a particular remote system and return the results in JSON format.
I was able to create a Nano Server Docker container with both PowerShell and .NET Core installed. However, I ultimately realized that there is no WinRM installed on the container and therefore it is impossible to invoke remote PowerShell cmdlets.
My host system is Windows Server 2019 Datacenter, Version 1809 (OS Build 17763.379). I am using Docker Desktop for Windows (Version 2.0.0.3) with Windows containers enabled.
Dockerfile
Here is the Dockerfile I am using. I created it by combining portions of the Dockerfile from here and here.
# escape=`
# Args used by from statements must be defined here:
ARG fromTag=1809
ARG InstallerVersion=nanoserver
ARG InstallerRepo=mcr.microsoft.com/powershell
ARG NanoServerRepo=mcr.microsoft.com/windows/nanoserver
# Use server core as an installer container to extract PowerShell,
# As this is a multi-stage build, this stage will eventually be thrown away
FROM ${InstallerRepo}:$InstallerVersion AS installer-env
# Arguments for installing PowerShell, must be defined in the container they are used
ARG PS_VERSION=6.2.0
ARG PS_PACKAGE_URL=https://github.com/PowerShell/PowerShell/releases/download/v$PS_VERSION/PowerShell-$PS_VERSION-win-x64.zip
SHELL ["pwsh", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
ARG PS_PACKAGE_URL_BASE64
RUN Write-host "Verifying valid Version..."; `
if (!($env:PS_VERSION -match '^\d+\.\d+\.\d+(-\w+(\.\d+)?)?$' )) { `
throw ('PS_Version ({0}) must match the regex "^\d+\.\d+\.\d+(-\w+(\.\d+)?)?$"' -f $env:PS_VERSION) `
} `
$ProgressPreference = 'SilentlyContinue'; `
if($env:PS_PACKAGE_URL_BASE64){ `
Write-host "decoding: $env:PS_PACKAGE_URL_BASE64" ;`
$url = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($env:PS_PACKAGE_URL_BASE64)) `
} else { `
Write-host "using url: $env:PS_PACKAGE_URL" ;`
$url = $env:PS_PACKAGE_URL `
} `
Write-host "downloading: $url"; `
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12; `
New-Item -ItemType Directory /installer > $null ; `
Invoke-WebRequest -Uri $url -outfile /installer/powershell.zip -verbose; `
Expand-Archive /installer/powershell.zip -DestinationPath \PowerShell
# -------------------------------------------------------------------------------------------------------------------------------------------------------
# Retrieve .NET Core SDK
USER ContainerAdministrator
ENV DOTNET_SDK_VERSION 2.2.401
RUN Invoke-WebRequest -OutFile dotnet.zip https://dotnetcli.blob.core.windows.net/dotnet/Sdk/$Env:DOTNET_SDK_VERSION/dotnet-sdk-$Env:DOTNET_SDK_VERSION-win-x64.zip; `
$dotnet_sha512 = 'ed83eb5606912cd78d7696fbdc8e8074afa95fda84eec57b078d7371848ad15fe91aaf521b85e77c69b844a7b036a2c0b7b6cac87a8e356643980d96b689af93'; `
if ((Get-FileHash dotnet.zip -Algorithm sha512).Hash -ne $dotnet_sha512) { `
Write-Host 'CHECKSUM VERIFICATION FAILED!'; `
exit 1; `
}; `
`
Expand-Archive dotnet.zip -DestinationPath dotnet; `
Remove-Item -Force dotnet.zip
# -------------------------------------------------------------------------------------------------------------------------------------------------------
# Install PowerShell into NanoServer
FROM ${NanoServerRepo}:${fromTag}
# Copy PowerShell Core from the installer container
ENV ProgramFiles="C:\Program Files" `
# set a fixed location for the Module analysis cache
LOCALAPPDATA="C:\Users\ContainerAdministrator\AppData\Local" `
PSModuleAnalysisCachePath="$LOCALAPPDATA\Microsoft\Windows\PowerShell\docker\ModuleAnalysisCache" `
# Persist %PSCORE% ENV variable for user convenience
PSCORE="$ProgramFiles\PowerShell\pwsh.exe" `
# Set the default windows path so we can use it
WindowsPATH="C:\Windows\system32;C:\Windows"
# Set the path
ENV PATH="$WindowsPATH;C:\Program Files\PowerShell;C:\Program Files\dotnet;"
COPY --from=installer-env ["\\PowerShell\\", "$ProgramFiles\\PowerShell"]
# intialize powershell module cache
RUN pwsh `
-NoLogo `
-NoProfile `
-Command " `
$stopTime = (get-date).AddMinutes(15); `
$ErrorActionPreference = 'Stop' ; `
$ProgressPreference = 'SilentlyContinue' ; `
while(!(Test-Path -Path $env:PSModuleAnalysisCachePath)) { `
Write-Host "'Waiting for $env:PSModuleAnalysisCachePath'" ; `
if((get-date) -gt $stopTime) { throw 'timout expired'} `
Start-Sleep -Seconds 6 ; `
}"
# -------------------------------------------------------------------------------------------------------------------------------------------------------
COPY --from=installer-env ["/dotnet", "/Program Files/dotnet"]
# -------------------------------------------------------------------------------------------------------------------------------------------------------
USER ContainerAdministrator
EXPOSE 80/tcp
EXPOSE 5985/tcp
EXPOSE 5986/tcp
EXPOSE 7777/tcp
EXPOSE 7778/tcp
CMD ["pwsh.exe"]
Docker Commands
Here are the Docker commands I am using to create and access the Docker container (note the directory contains a single Dockerfile with the contents above):
docker build C:\powershell-nanoserver1809-with-dotnet-2.2.401
docker create -t --name NanoServerHelloWorld -h NanoServer -i <ID_RETURNED_FROM_PREVIOUS_COMMAND>
docker start -i NanoServerHelloWorld
PowerShell and WinRM commands that fail
On other systems I can use the following PowerShell code to create a CimSession to a remote system and subsequently invoke a PowerShell cmdlet:
$u = "REMOTE_DOMAIN\REMOTE_USERNAME";
$pw = "REMOTE_PASSWORD";
$secStr = New-Object -TypeName System.Security.SecureString;
$pw.ToCharArray() | ForEach-Object {$secStr.AppendChar($_)};
$Cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $u, $secStr;
$Session = New-CimSession -ComputerName 172.27.0.114 -Authentication Negotiate -Credential $Cred -OperationTimeoutSec 900
But in this container I get this error message:
New-CimSession : FAILED
At line:1 char:12
+ $Session = New-CimSession -ComputerName 172.27.0.114 -Authentication ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [New-CimSession], CimException
+ FullyQualifiedErrorId : Microsoft.Management.Infrastructure.CimException,Microsoft.Management.Infrastructure.CimCmdlets.NewCimSessionCommand
Further, if I try to configure WinRM in anyway, I get the following (from cmd):
C:\>winrm set winrm/config/client @{TrustedHosts="*"}
'winrm' is not recognized as an internal or external command,
operable program or batch file.
Also, if I look at services on the container, I don't see WinRM:
PS C:\> Get-Service
Status Name DisplayName
------ ---- -----------
Running cexecsvc Container Execution Agent
Running CryptSvc Cryptographic Services
Running DcomLaunch DCOM Server Process Launcher
Running Dhcp DHCP Client
Running DiagTrack Connected User Experiences and Teleme…
Running Dnscache DNS Client
Running EventLog Windows Event Log
Stopped KeyIso CNG Key Isolation
Stopped LanmanServer Server
Running LanmanWorkstation Workstation
Stopped lmhosts TCP/IP NetBIOS Helper
Stopped mpssvc Windows Defender Firewall
Stopped Netlogon Netlogon
Stopped NetSetupSvc Network Setup Service
Running nsi Network Store Interface Service
Stopped Power Power
Running ProfSvc User Profile Service
Running RpcEptMapper RPC Endpoint Mapper
Running RpcSs Remote Procedure Call (RPC)
Running SamSs Security Accounts Manager
Running Schedule Task Scheduler
Stopped seclogon Secondary Logon
Running SystemEventsBroker System Events Broker
Running TimeBrokerSvc Time Broker
Get-Service : Service 'TrustedInstaller (TrustedInstaller)' cannot be queried due to the following error:
At line:1 char:1
+ Get-Service
+ ~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (System.ServiceProcess.ServiceController:ServiceController) [Get-Service], ServiceCommandException
+ FullyQualifiedErrorId : CouldNotGetServiceInfo,Microsoft.PowerShell.Commands.GetServiceCommand
Stopped TrustedInstaller TrustedInstaller
Running UserManager User Manager
Stopped VaultSvc Credential Manager
Stopped WerSvc Windows Error Reporting Service
Stopped WinHttpAutoProxyS… WinHTTP Web Proxy Auto-Discovery Serv…
Stopped wisvc Windows Insider Service
Question
Is there a way to get WinRM installed and working on a Nano Server Docker container build 1809? If not, is there some workaround to connect to remote systems with PowerShell to invoke PowerShell cmdlets?
Perhaps there is some special Docker command I am missing, or some other Nano Server image available which has this missing feature?
Thanks very much in advance.