Documentation forSolarWinds Observability SaaS

Install the .NET Library on Docker Windows Container

Before you start

Do not run other APM libraries alongside the SolarWinds Observability .NET Library. Remove other APM libraries from your .NET application before using the SolarWinds Observability .NET Library.

Install the .NET Library

To create a Docker Windows Container with the SolarWinds Observability .NET Library, install the library with the command line.

  1. Create a file called conf.inf containing the following text, and place it in the same directory as your installer.

    [Setup]
    service_key=YourServiceKey
    apm_collector=YourSolarWindsApmCollectorEndpoint

    Modify the following text:

    • Replace YourServiceKey with the Service key you are using to identify your account and the service being instrumented. 

      The Service key should be in the form of YourApiToken:YourServiceName. Replace YourApiToken with the SolarWinds Observability API token (ingestion type) generated for this service, and replace YourServiceName with your chosen name for this service. See API Tokens.

      The service name is also called the entity's service ID in SolarWinds Observability SaaS. The service name can also be seen in the Overview tab in the service entity's Entity Explorer details view. Editing the display name of the service entity in SolarWinds Observability SaaS does not affect the Service key.

    • Replace YourSolarWindsApmCollectorEndpoint with the correct SolarWinds APM collector endpoint for your organization.

      The apm_collector setting is optional and ensures the correct endpoint URI is used when sending data to the SolarWinds APM collector. To determine the correct endpoint for your SolarWinds APM collector, see Data centers and endpoint URIs.

    The service key can also be passed to SolarWinds Observability .NET Library by defining the SW_APM_SERVICE_KEY environment variable when starting container.

  2. To simplify escaping commands, change the default escape character to a backtick (`). Add the following line to the beginning of the dockerfile.

    # escape=`
  3. Add the conf.inf and installer files to the dockerfile with the following commands.

    ADD ./SolarWindsAPM_DotNetAgent_Setup.exe c:\SolarWindsAPM_DotNetAgent_Setup.exe
    ADD ./conf.inf c:\conf.inf
  4. Set PowerShell as default shell with the following command.

    SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] `
  5. Install SolarWinds Observability .NET Library by entering the following command in the command prompt.

    SolarWindsAPM_DotNetAgent_Setup.exe [/SILENT | /VERYSILENT] /LOADINF="conf.inf" /COMPONENTS="IISOnly[,NonIISApplications,IISCoreApplication,NonIISCoreApplication]" [/CLOSEAPPLICATIONS | /NOCLOSEAPPLICATIONS]

    Square brackets indicate optional parameters.

    The pipe character separates multiple choices within an option.

    For /COMPONENTS see Install the .NET Library on Docker Windows Container for all available values. For example:

    /COMPONENTS="IISOnly,NonIISApplications"

    Launch and wait for installer process to finish:

    RUN $installerDir = $env:SystemDrive; `
        cd $installerDir ; `
        $processStartInfo = New-Object System.Diagnostics.ProcessStartInfo ; `
        $arguments = '/SILENT /CLOSEAPPLICATIONS /LOADINF="conf.inf" /COMPONENTS="IISOnly" /LOG="installer.log"' ; `
        $installer = $installerDir + '\SolarWindsAPM_DotNetAgent_Setup.exe' ; `
        $processStartInfo.FileName = $installer ; `
        $processStartInfo.Arguments = $arguments ; `
        $process = New-Object System.Diagnostics.Process ; `
        $process.StartInfo = $processStartInfo ; `
        $process.Start() | Out-Null ; `
        $process.WaitForExit() ; `
        $result = $process.ExitCode	; `
        echo "result: $result" ; `
        if ($result -ne 0) { `
            echo "Failed to install .NET Library" ; `
            exit $result; `
        } ; `
        echo ".NET Library installed." ;
                    
  6. If you are instrumenting applications running inside IIS, run the following commands to download and copy certificate to the C:\cert directory.

    RUN $certRoot = $env:SystemDrive + '\cert\' ; `
        mkdir $certRoot ; `
        Invoke-WebRequest https://curl.se/ca/cacert.pem -OutFile "$certRoot\cacert.pem" ;
  7. Run the following command to add the SW_APM_TRUSTEDPATH environment variable to instruct the SolarWinds Observability .NET Library to load certificate. If Windows is installed on a different system drive, then update C:\ to match the system drive returned by $env:SystemDrive.

    ENV SW_APM_TRUSTEDPATH='C:\cert\cacert.pem'
  8. If instrumenting an .NET Framework applications running inside IIS, add the COMPLUS_LoaderOptimization environment variable with the following command.

    ENV COMPLUS_LoaderOptimization=1
  9. When using the Docker Windows Container to create an Azure App Service, the environment variables declared inside the dockerfile must be set inside the Application Settings. To configure the .NET Library, navigate to the Application Settings in the configuration settings and add the SW_APM_TRUSTEDPATH and COMPLUS_LoaderOptimization application global environment variables.

  10. (Optional) List properties for HttpModule installed by SolarWinds Observability .NET Library with the following command.

    RUN Import-Module WebAdministration ; `
        $modules = (Get-WebConfiguration //modules -PSPath iis:).collection ; `
        $modules | foreach {if ($_.name -eq '"SolarWindsAPM"') { $_.attributes | select name, value}} ;

See the following example dockerfiles for web applications running inside IIS.

.NET Framework 4.8 web application
# escape=`

FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2019

SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]

# Set environment variables

ENV COMPLUS_LoaderOptimization=1 `
    SW_APM_TRUSTEDPATH='c:\cert\cacert.pem'
    
# Download and copy certificate under c:\cert directory

RUN $certRoot = $env:SystemDrive + '\cert\' ; `
    mkdir $certRoot ; `
    Invoke-WebRequest https://curl.se/ca/cacert.pem -OutFile "$certRoot\cacert.pem" ;

# Copy installer
     
ADD ./SolarWindsAPM_DotNetAgent_Setup.exe c:/SolarWindsAPM_DotNetAgent_Setup.exe
ADD ./conf.inf c:/conf.inf

# Install .NET Library

RUN $installerDir = $env:SystemDrive; `
    cd $installerDir ; `
    $processStartInfo = New-Object System.Diagnostics.ProcessStartInfo ; `
    $arguments = '/SILENT /CLOSEAPPLICATIONS /LOADINF="conf.inf" /COMPONENTS="IISOnly" /LOG="installer.log"' ; `
    $installer = $installerDir + '\SolarWindsAPM_DotNetAgent_Setup.exe' ; `
    $processStartInfo.FileName = $installer ; `
    $processStartInfo.Arguments = $arguments ; `
    $process = New-Object System.Diagnostics.Process ; `
    $process.StartInfo = $processStartInfo ; `
    $process.Start() | Out-Null ; `
    $process.WaitForExit() ; `
    $result = $process.ExitCode	; `
    echo "result: $result" ; `
    if ($result -ne 0) { `
        echo "Failed to install .NET Library" ; `
        exit $result; `
    } ; `
    echo ".NET Library installed" ; 

# List attributes for IIS HttpModule installed by .NET Library

RUN Import-Module WebAdministration ; `
    $modules = (Get-WebConfiguration //modules -PSPath iis:).collection ; `
    $modules | foreach {if ($_.name -eq '"SolarWindsAPM"') { $_.attributes | select name, value}} ;

# Copy .NET Framework 4.8 Web application

COPY webapp/ /inetpub/wwwroot/webapp/

# Set application pool as DefaultAppPool to allow passing of environment variables from ServiceMonit.exe to IIS worker process

RUN Import-Module WebAdministration ; `
    Get-WebSite -Name 'Default Web Site' | Remove-WebSite -Confirm:$false -Verbose ; `
    Remove-WebAppPool -Name "DefaultAppPool" -Confirm:$false -Verbose ; `
    $wwwRoot = $env:SystemDrive + '\inetpub\wwwroot' ; `
    $appName = 'webapp' ; `
    $appPoolName = 'DefaultAppPool' ; `
    $runtimeVersion = 'v4.0' ; `
    $identity = 'ApplicationPoolIdentity' ; `
    $pipelineMode = 'Integrated' ; `
    $bindings=@( @{protocol='http' ; bindingInformation='*:80:'} ) ; `
    New-Item -Path "IIS:\AppPools" -Name "$appPoolName" -Type AppPool ; `
    Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name managedRuntimeVersion -Value "$runtimeVersion" ; `
    Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name processModel -Value @{'identitytype' = $identity} ; `
    Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name managedPipelineMode -Value "$pipelineMode" ; `
    New-Website -Name $appName -ApplicationPool $appPoolName -PhysicalPath $wwwRoot\$appName ; `
    Set-ItemProperty -Path "IIS:\Sites\$appName" -Name bindings -Value $bindings ; `
    Start-IISSite -Name "$appName" ;

# Expose ports

EXPOSE 80

WORKDIR /inetpub/wwwroot
ASP.NET Core 6.0 application
# escape=`

FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2019

SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]

# Set environment variables

ENV SW_APM_TRUSTEDPATH='c:\cert\cacert.pem'

# Install ASP.NET Core Hosting Module for a ASP.NET Core 6.0 application. Update download link for a different ASP.NET Core version.

RUN Invoke-WebRequest https://download.visualstudio.microsoft.com/download/pr/d7124775-38c9-460f-a269-7bc131b3dfbf/7f60bcc6030e408cf11a935d5451ffa4/dotnet-hosting-6.0.20-win.exe -OutFile c:\dotnet-hosting-win.exe ; `
    $appRoot = $env:SystemDrive ; `
    cd $appRoot ; `
    $processStartInfo = New-Object System.Diagnostics.ProcessStartInfo ; `
    $arguments = '/install /quiet /norestart /log install.hosting.log' ; `
    $installer = $appRoot + '\dotnet-hosting-win.exe' ; `
    $processStartInfo.FileName = $installer ; `
    $processStartInfo.Arguments = $arguments ; `
    $process = New-Object System.Diagnostics.Process ; `
    $process.StartInfo = $processStartInfo ; `
    $process.Start() | Out-Null ; `
    $process.WaitForExit() ; `
    $result = $process.ExitCode	; `
    echo "result: $result" ; `
    if ($result -ne 0) { `
        echo "Failed to install ASP.NET Core Hosting Bundle." ; `
        exit $result; `
    } ; `
    echo "ASP.NET Core Hosting Bundle installed." ; `
    Remove-Item -Force C:\dotnet-hosting-win.exe; `
    Remove-Item -Force -Recurse $env:Temp\*

# Check that AspNetCoreModuleV2 IIS Module is installed

RUN Import-Module WebAdministration ; `
    $modules = (Get-WebConfiguration //modules -PSPath iis:).collection ; `
    $modules | foreach {if ($_.name -eq '"AspNetCoreModuleV2"') { $_.attributes | select name, value}} ;

# Install certificate

RUN $certRoot = $env:SystemDrive + '\cert\' ; `
    mkdir $certRoot ; `
    Invoke-WebRequest https://curl.se/ca/cacert.pem -OutFile "$certRoot\cacert.pem" ; 

# Copy installer and configuration file

COPY ./SolarWindsAPM_DotNetAgent_Setup.exe c:/SolarWindsAPM_DotNetAgent_Setup.exe
COPY ./conf.inf c:/conf.inf

# Install .NET Library

RUN $appRoot = $env:SystemDrive ; `
    cd $appRoot ; `
    $processStartInfo = New-Object System.Diagnostics.ProcessStartInfo ; `
    $arguments = '/SILENT /CLOSEAPPLICATIONS /LOADINF="conf.inf" /COMPONENTS="IISCoreApplications" /LOG="installer.log"' ; `
    $installer = $appRoot + '\SolarWindsAPM_DotNetAgent_Setup.exe' ; `
    $processStartInfo.FileName = $installer ; `
    $processStartInfo.Arguments = $arguments ; `
    $process = New-Object System.Diagnostics.Process ; `
    $process.StartInfo = $processStartInfo ; `
    $process.Start() | Out-Null ; `
    $process.WaitForExit() ; `
    $result = $process.ExitCode	; `
    echo "result: $result" ; `
    if ($result -ne 0) { `
        echo "Failed to install .NET Library" ; `
        exit $result; `
    } ; `
    echo ".NET Library installed." ; 

# List attributes for IIS HttpModule installed by .NET Library

RUN Import-Module WebAdministration ; `
    $modules = (Get-WebConfiguration //modules -PSPath iis:).collection ; `
    $modules | foreach {if ($_.name -eq '"SolarWindsAPM"') { $_.attributes | select name, value}} ;

# Copy ASP.NET Core 6.0 application

COPY webapp/ /inetpub/wwwroot/webapp/

# Set application pool as DefaultAppPool to allow passing of environment variables from ServiceMonit.exe to IIS worker process

RUN Import-Module WebAdministration ; `
    Get-WebSite -Name 'Default Web Site' | Remove-WebSite -Confirm:$false -Verbose ; `
    Remove-WebAppPool -Name "DefaultAppPool" -Confirm:$false -Verbose ; `
    $wwwRoot = $env:SystemDrive + '\inetpub\wwwroot' ; `
    $appName = 'webapp' ; `
    $appPoolName = 'DefaultAppPool' ; `
    $runtimeVersion = '' ; `
    $identity = 'ApplicationPoolIdentity' ; `
    $pipelineMode = 'Integrated' ; `
    $bindings=@( @{protocol='http' ; bindingInformation='*:80:'} ) ; `
    New-Item -Path "IIS:\AppPools" -Name "$appPoolName" -Type AppPool ; `
    Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name managedRuntimeVersion -Value "$runtimeVersion" ; `
    Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name processModel -Value @{'identitytype' = $identity} ; `
    Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name managedPipelineMode -Value "$pipelineMode" ; `
    New-Website -Name $appName -ApplicationPool $appPoolName -PhysicalPath $wwwRoot\$appName ; `
    Set-ItemProperty -Path "IIS:\Sites\$appName" -Name bindings -Value $bindings ; `
    Start-IISSite -Name "$appName" ;

# Expose ports

EXPOSE 80

WORKDIR /inetpub/wwwroot

Install the .NET Framework web application

Before you start

The web application must use the default application pool name DefaultAppPool. If any other name is used for the application pool, the process ServiceMonitor.exe does not pass the environment variables that are declared when the container is started. As a result, environment variables, such as SW_APM_SERVICE_KEY, passed in with the option -e when the docker container is started are not seen by the SolarWinds Observability .NET Library.

See the Microsoft IIS Service Monitor readme, as well as the Windows service installed in docker container not using Environment Variables closed issue for more information.

Set up the web application

  1. Copy the application binaries to a folder inside the C:\inetpub\wwwroot\webapp directory.

    COPY app/ /inetpub/wwwroot/webapp/
  2. Run the following command to delete the Default Web Site web site and the default application pool DefaultAppPool.

    RUN Import-Module WebAdministration ; `
        Get-WebSite -Name 'Default Web Site' | Remove-WebSite -Confirm:$false -Verbose ; `
        Remove-WebAppPool -Name "DefaultAppPool" -Confirm:$false -Verbose ;
  3. Run the following command to create a new application pool named DefaultAppPool to be used by the web application. The application is copied to the C:\inetpub\wwwroot\webapp directory.

    RUN Import-Module WebAdministration ; `
        $wwwRoot = $env:SystemDrive + '\inetpub\wwwroot' ; `
        $appName = 'webapp' ; `
        $appPoolName = 'DefaultAppPool' ; `
        $runtimeVersion = 'v4.0' ; `
        $identity = 'ApplicationPoolIdentity' ; `
        $pipelineMode = 'Integrated' ; `
        $bindings=@( @{protocol='http' ; bindingInformation='*:80:'} ) ; `
        New-Item -Path "IIS:\AppPools" -Name "$appPoolName" -Type AppPool ; `
        Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name managedRuntimeVersion -Value "$runtimeVersion" ; `
        Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name processModel -Value @{'identitytype' = $identity} ; `
        Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name managedPipelineMode -Value "$pipelineMode" ; `
        New-Website -Name $appName -ApplicationPool $appPoolName -PhysicalPath $wwwRoot\$appName ; `
        Set-ItemProperty -Path "IIS:\Sites\$appName" -Name bindings -Value $bindings ; `
        Start-IISSite -Name "$appName" ;

Install the ASP.NET Core web application

Before you start

The web application must use the default application pool name DefaultAppPool. If any other name is used for the application pool, the process ServiceMonitor.exe does not pass the environment variables that are declared when the container is started. As a result, environment variables, such as SW_APM_SERVICE_KEY, passed in with the option -e when the docker container is started are not seen by the SolarWinds Observability .NET Library.

See the Microsoft IIS Service Monitor readme, as well as the Windows service installed in docker container not using Environment Variables closed issue for more information.

Set up the web application

  1. Run the following commands to install the ASP.NET Core Hosting Module for an ASP.NET Core 6.0 application.

    If you are using a different ASP.NET Core version, replace the download link in the first line of the command with the correct download link for your ASP.NET Core version.

    RUN Invoke-WebRequest https://download.visualstudio.microsoft.com/download/pr/d7124775-38c9-460f-a269-7bc131b3dfbf/7f60bcc6030e408cf11a935d5451ffa4/dotnet-hosting-6.0.20-win.exe -OutFile c:\dotnet-hosting-win.exe ; `
        $appRoot = $env:SystemDrive ; `
        cd $appRoot ; `
        $processStartInfo = New-Object System.Diagnostics.ProcessStartInfo ; `
        $arguments = '/install /quiet /norestart /log install.hosting.log' ; `
        $installer = $appRoot + '\dotnet-hosting-win.exe' ; `
        $processStartInfo.FileName = $installer ; `
        $processStartInfo.Arguments = $arguments ; `
        $process = New-Object System.Diagnostics.Process ; `
        $process.StartInfo = $processStartInfo ; `
        $process.Start() | Out-Null ; `
        $process.WaitForExit() ; `
        $result = $process.ExitCode	; `
        echo "result: $result" ; `
        if ($result -ne 0) { `
            echo "Failed to install ASP.NET Core Hosting Bundle." ; `
            exit $result; `
        } ; `
        echo "ASP.NET Core Hosting Bundle installed." ; `
        Remove-Item -Force C:\dotnet-hosting-win.exe; `
        Remove-Item -Force -Recurse $env:Temp\*
    
    # Check that AspNetCoreModuleV2 IIS Module is installed
    
    RUN Import-Module WebAdministration ; `
        $modules = (Get-WebConfiguration //modules -PSPath iis:).collection ; `
        $modules | foreach {if ($_.name -eq '"AspNetCoreModuleV2"') { $_.attributes | select name, value}} ;
  2. Copy the application binaries to a folder inside the C:\inetpub\wwwroot\webapp directory.

    COPY webapp/ /inetpub/wwwroot/webapp/
  3. Run the following command to delete the Default Web Site web site and the default application pool DefaultAppPool.

    RUN Import-Module WebAdministration ; `
        Get-WebSite -Name 'Default Web Site' | Remove-WebSite -Confirm:$false -Verbose ; `
        Remove-WebAppPool -Name "DefaultAppPool" -Confirm:$false -Verbose ;
  4. Run the following command to create a new application pool named DefaultAppPool to be used by the web application. The application is copied to the C:\inetpub\wwwroot\webapp directory.

    The runtime for the application pool should be set to No Managed Code.

    RUN Import-Module WebAdministration ; `
        $wwwRoot = $env:SystemDrive + '\inetpub\wwwroot' ; `
        $appName = 'webapp' ; `
        $appPoolName = 'DefaultAppPool' ; `
        $runtimeVersion = '' ; `
        $identity = 'ApplicationPoolIdentity' ; `
        $pipelineMode = 'Integrated' ; `
        $bindings=@( @{protocol='http' ; bindingInformation='*:80:'} ) ; `
        New-Item -Path "IIS:\AppPools" -Name "$appPoolName" -Type AppPool ; `
        Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name managedRuntimeVersion -Value "$runtimeVersion" ; `
        Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name processModel -Value @{'identitytype' = $identity} ; `
        Set-ItemProperty -Path "IIS:\AppPools\$appPoolName" -Name managedPipelineMode -Value "$pipelineMode" ; `
        New-Website -Name $appName -ApplicationPool $appPoolName -PhysicalPath $wwwRoot\$appName ; `
        Set-ItemProperty -Path "IIS:\Sites\$appName" -Name bindings -Value $bindings ; `
        Start-IISSite -Name "$appName" ;

Troubleshooting

Set debug log level inside configuration file

To aid in troubleshooting issues, set the debug log level for SolarWinds Observability SaaS inside the image.

RUN $installDir = $env:SW_APM_HOME_DOTNET ; `
    $configFile = $installDir + '\solarwinds_apm.config' ; `
    $xml = [xml](Get-Content $configFile) ; `
    $newNode = $xml.CreateElement('"add"') ; `
    $xmlAtt = $xml.CreateAttribute('"key"') ; `
    $xmlAtt.value = '"LogLevel"' ; `
    $newNode.Attributes.Append($xmlAtt) ; `
    $xmlAtt = $xml.CreateAttribute('"value"') ; `
    $xmlAtt.value = '"Debug"' ; `
    $newNode.Attributes.Append($xmlAtt); `
    $xml.configuration.appSettings.AppendChild($newNode) ; `
    $xml.Save($configFile); 

The scripts are not supported under any SolarWinds support program or service. The scripts are provided AS IS without warranty of any kind. SolarWinds further disclaims all warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The risk arising out of the use or performance of the scripts and documentation stays with you. In no event shall SolarWinds or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the scripts or documentation.