Using Chef Habitat to Migrate Legacy Windows Applications


In this bog post we will discuss Using Chef Habitat to Migrate Legacy Windows Applications to a Modern Secure Platform. With support ending for Windows Server 2008 and Microsoft SQL Server 2008, legacy Windows applications will need to be migrated to newer secure and supportable modern platforms. For instance, Windows Server 2016 and 2019 along with Microsoft SQL Server 2017 and later. In this blog post, the third in our series on Chef Habitat, we will migrate a legacy ASP.NET 2.0 application from Windows Server 2008 and Microsoft SQL Server 2005 to a Windows Server 2016 physical or virtual machine and a Docker container with Windows Server 2016 Core. Windows Server 2016 Core is a striped down version of Windows Server without a GUI and is managed as you would Linux, from the command line. Since there is no GUI, the attack surface is reduced and the OS image is significantly smaller. Only what needs to be loaded for the application will be installed. Chef has a Github repository with a legacy ASP.NET 2.0 Application from the Codeplex Archive that we will be installing. This will show how Legacy ASP.NET 2.0 applications can be migrated to modern and secure platforms.


See the previous blog post under Prerequisites and Workstation Setup for installing the required tools and configuring Chef Habitat.

VMs need to be running Windows Server 2016 with a minimum of 4GB of RAM. Containers need 2GB of RAM for each container.

Note: This application currently only runs on a Windows Server running on a physical/virtual hardware, or in a container but not directly on Windows 10. For Docker on Windows, you must be running in Windows container mode. AWS or Azure images will need to have Docker preinstalled, and at least 50GB of disk space.

Common Steps for Windows Server and Containers

Now the code from the Github repository needs to be downloaded. This needs to be on the root of the C: drive. The MS SQL Server installation can fail if the path is to long. Entering into a local studio at c:\users\administrators\sqlwebadmin will result in a much longer install path than entering from c:\sqlwebadmin. Clone the repository and cd into the top level directory:

cd c:\
git clone
cd sqlwebadmin

A local default origin should have been setup from the Prerequisites and Workstation Setup section in the previous blog post.

The INSTALL_HOOK now needs to be enable. See this blog post for more information.


This plan takes advantage of several dependencies that use this feature to run an install hook when the dependency is installed for things like enabling windows features and registering a COM component.

Installing on a Windows Server 2016 (Physical or Virtual)

Enter a local Habitat Studio and load core/sqlserver2005:

hab studio enter
hab svc load core/sqlserver2005

This will take several minutes to load since it is downloading and installing the .Net 2.0 runtime and installing SQL Server 2005, while its loading, build this plan:


Now we need to wait for SQL Server’s post-run hook to complete. View the Supervisor output with Get-SupervisorLog and wait for the message:

sqlserver2005.default hook[post-run]:(HK): 1> 2> 3> 4> 5> 6> Application user setup complete

Now load <your_origin>/sqlwebadmin:

hab svc load <your_origin>/sqlwebadmin --bind database:sqlserver2005.default

In the Supervisor log wait for:

sqlwebadmin.default(O): sqlwebadmin is running

The website should now be accessible. Browse to http://localhost:8099/databases.aspx.

Exporting to a Windows Server 2016 Docker Container

Export the core/sqlserver2005 package to a docker image:

$env:HAB_SQLSERVER2005="{\`"svc_account\`":\`"NT AUTHORITY\\SYSTEM\`"}"
hab pkg export docker --memory 2gb core/sqlserver2005

The first line above will make sure that the SQL Server install sets the svc_account to the SYSTEM account instead of the default NETWORK SERVICE account which is advisable in a container environment.

Build our sqlwebadmin package (make sure you are still in c:\sqlwebadmin):

hab pkg build .

Export our sqlwebadmin hart to a docker image:

hab pkg export docker --memory 2gb <path to HART file>

Now lets bring these two containers together into a Habitat supervisor ring:

$sql = docker run -d --env HAB_LICENSE=accept-no-persist --memory 2gb core/sqlserver2005
$ip = docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $sql
docker run -it --env HAB_LICENSE=accept-no-persist <your_origin>/sqlwebadmin --bind database:sqlserver2005.default --peer $ip

Alternatively you can use Docker Compose along with the provided docker-compose.yml to bring up the containers. Update the docker-compose.yml file with your origin and the environment variables below for each container:

version: '2.2'
    image: core/sqlserver2005
      - HAB_LICENSE=accept
    image: <your_origin>/sqlwebadmin
      - HAB_LICENSE=accept
      - 8099:8099
      - sqlserver2005
      - sqlserver2005
    command: --peer sqlserver2005 --bind database:sqlserver2005.default

      name: nat
docker-compose up

Grab the IP address of the sqlwebadmin container:

docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aql)

Browsing to http://<CONTAINER_IP&gt;:8099/databases.aspx should bring up the application.

Perficient can help!

In this blog post we discussed the steps of Using Chef Habitat to Migrate Legacy Windows Applications to a Modern Secure Platform using Chef Habitat. With Windows and SQL Server end-of-support happening beginning this year, now is the time to begin migrating those legacy applications with Habitat. This approach eliminates your dependencies on these legacy operating systems and helps you avoid costly support contracts.  We can also help you modernize your application development processes at the core, using an OS independent approach that makes your business more innovative and resilient for the future.  Let us know if we can help.

Getting Started with Chef Habitat on Windows


This is the second post in our series on Chef Habitat. For an introduction to Habitat, please refer to our initial post. In this write up, we will be looking closely at Habitat in a Windows context. There are a few differences between Habitat on Windows versus Linux or Mac which we will point out. Additionally, we will take you through steps to package your own Windows applications in Habitat. Habitat on Windows uses PowerShell instead of Linux shell scripting to build packages and perform package installation. Dependent packages must run on Windows or be cross-platform, such as .NET Core or Java. PowerShell Core is used for the Habitat Studio on Windows, providing a clean room for working with packages. You can also run Habitat Studio in a Windows Server 2016 container for additional isolation. Along with modern Windows applications, Habitat supports build, packaging, and deployment of legacy Windows applications. See this post from Chef for additional information and this one for legacy Windows applications. Chef has created packages for PowerShell Core, Visual Studio Build Tools, 7-Zip, WIX, .NET Core, and Visual C++ redistributable binaries that can be used as dependencies in your Habitat Plan to create custom application packages. Once a HART package exists, you can deploy it directly to physical or virtual servers, or export the package for target run times such as Docker, Kubernetes, or Cloud Foundry. HART packages can also be uploaded to a public or private Builder for archival and future deployments. Our sample application, Contoso University, is written in ASP.NET and based on Microsoft Entity Framework Core. Contoso University is a database-driven application for managing students, courses, and instructor information at a fictional university. If you want to skip the tutorial and see the completed code right away, I pushed it to this repository.


Many of these prerequisites apply to Linux workstation setup. User accounts are required for GitHub, Habitat Builder, and Docker Hub.
  1. Google Chrome – For browsing the sample application. Google Chrome is the most compatible browser for our application.
  2. Git – For source code management and cloning the source repository.
  3. GitHub – A GitHub account is used for authentication to Habitat Builder.
  4. Habitat Builder – A Habitat Builder account is required for building and publishing Habitat packages.
  5. Docker and Docker Hub – We will use Docker to run our application after building the Habitat package.

Workstation Setup

Chocolatey is an open-source, community-managed package manager (similar to Homebrew on Mac). Chocolatey is used by a number of companies including Chef. Chocolatey packages are vetted against VirusTotal however a more thorough vetting process should be adopted if using these packages in production environments. We use Chocolatey here to demonstrate how package managers work and how workstation setup on Windows can be streamlined.

Install Choclatey, Habitat, Git, and Google Chrome

Install Choclatey with PowerShell:
Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString(''))
Install Chef Habitat:
choco install habitat -y
Install Google Chrome:
choco install googlechrome -y
Install Git and refresh the system path:
choco install git -y

Configure Habitat

Start configuration of the Habitat command-line interface (CLI) using hab setup. First, point the Habitat CLI to a Habitat Builder instance. This can be an on-premise Builder or the publicly-hosted Builder. Choosing Yes will prompt you for the on-premise Builder endpoint (illustrated below however the remaining steps assume use of the public Builder):
Connect to an on-premise bldr instance? No
Enter the Origin name created on the Habitat Public Builder site:
Set up a default Origin? Yes
Default origin name: manny-rodriguez
An Origin key pair allows for secure uploads to the Builder Depot. Create one now, if needed:
Create a Public Signing Key for the Builder Depot? Yes
Add a Habitat Personal Access Token to your CLI configuration for uploading to Builder and checking job status:
Set up a default Habitat personal access token? Yes
Habitat personal access token: <TOKEN>
Setup now prompts you about a Control (CTL) Gateway. This will be covered in a separate blog post. Enter No to proceed:
Setup a default Habitat CTLGateway Secret? No
Add a binlink to the system path for package binaries to be easily found:
Add binlink to directory Path? Yes
Choose whether to enable or disable usage analytics:
Enable analytics? No

Package and Deploy a Windows ASP.NET Application

You’re now ready to start working with Habitat! Habitat requires you to declare all application dependencies in a Habitat Plan. On Windows, this file is typically named plan.ps1. See here for additional information. Let’s start with downloading the code, expanding the archive, and navigating to the appropriate directory:
cd c:
Invoke-Webrequest -uri -OutFile
cd contosouniversity
Now, we start authoring our Habitat Plan. The hab plan init command is useful for getting started here:
hab plan init --windows
The resulting directory structure is shown below:
tree habitat /F
| default.toml
| plan.ps1
├── config
└── hooks

Package Variables and Dependencies

$pkg_name and $pkg_origin will be automatically updated by Habitat based on contents of the local repository. $pkg_mainataner and $pkg_license should be updated manually with the appropriate details. These variables are passed to functions and script files that are used as templates for package installation and configuration:
$pkg_maintainer="Manny Rodriguez <>"
Package dependencies should be declared at this point. Use the $pkg_deps variable for deployment/runtime dependencies. Here, we specify core/dsc as a package dependency, which is a core package representing PowerShell Desired State Configuration (DSC). This allows any configuration not in the run hook (described further down) to be implemented. We require PowerShell DSC to configure SQL Server 2017 and the ASP.NET application:
$pkg_build_deps is for build-time dependencies.
# core/nuget is required for fetching dependent .NET packages:
We use $pkg_binds to specify the database connection details:
$pkg_binds={"database"="username password port"}

Build Logic

For our application, we must override the standard build logic to make sure our ASP.NET package is built correctly. Specifically, we override the Invoke-Build and Invoke-Install functions. A difference here between Linux Bash syntax and PowerShell is that Bash functions are defined using do instead of function:
function Invoke-Build {
    Copy-Item $PLAN_CONTEXT/../* $HAB_CACHE_SRC_PATH/$pkg_dirname -recurse -force
    nuget restore "$HAB_CACHE_SRC_PATH/$pkg_dirname/C#/$pkg_name/packages.config" -PackagesDirectory "$HAB_CACHE_SRC_PATH/$pkg_dirname/C#/packages" -Source ""
    nuget install MSBuild.Microsoft.VisualStudio.Web.targets -Version -OutputDirectory $HAB_CACHE_SRC_PATH/$pkg_dirname/
    $env:VSToolsPath = "$HAB_CACHE_SRC_PATH/$pkg_dirname/MSBuild.Microsoft.VisualStudio.Web.targets."
    ."$env:SystemRoot\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe" "$HAB_CACHE_SRC_PATH/$pkg_dirname/C#/$pkg_name/${pkg_name}.csproj" /t:Build /p:VisualStudioVersion=14.0
    if($LASTEXITCODE -ne 0) {
        Write-Error "dotnet build failed!"

function Invoke-Install {
    ."$env:SystemRoot\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe" "$HAB_CACHE_SRC_PATH/$pkg_dirname/C#/$pkg_name/${pkg_name}.csproj" /t:WebPublish /p:WebPublishMethod=FileSystem /p:publishUrl=$pkg_prefix/www

Application Configuration

The configuration for our ASP.NET application is defined in a default.toml file. The configuration values are passed to the appropriate files when the application is built. Below we see that the application listening port is specified, as well as the IIS application pool, application name, and site name. PowerShell DSC uses these values to properly configure IIS. If any of these configuration items should change, make updates in the default.toml file then push the changes out to the Habitat Supervisor using hab config apply to apply the updates:
port = 8099
app_pool = "hab_pool"
app_name = "hab_app"
site_name = "hab_site"
hab config apply 1 /tmp/newconfig.toml

More on PowerShell DSC

PowerShell DSC is Microsoft’s preferred method of configuration management for Windows. This works by using PowerShell for low-level tasks and scripting, along with DSC to provide idempotent configurations that can be applied and executed only if there has been a change on the server that needs correction. Microsoft provides DSC resources, such as xWebAdministration, for quickly developing configurations that need to be applied to one or multiple servers, along with instructions on how to create custom resources. This fits in nicely with Habitat, allowing you to define a desired state for your applications. Plan variables (from plan.ps1) are used to update the templated PowerShell script with the correct values:
Configuration NewWebsite
    Import-DscResource -Module xWebAdministration
    Node 'localhost' {
        WindowsFeature ASP {
            Ensure = "Present"
            Name   = "Web-Asp-Net45"
        xWebAppPool { { cfg.app_pool } }
            Name   = "{{cfg.app_pool}}"
            Ensure = "Present"
            State  = "Started"
        xWebsite { { cfg.site_name } }
            Ensure          = "Present"
            Name            = "{{cfg.site_name}}"
            State           = "Started"
            PhysicalPath    = Resolve-Path "{{pkg.svc_path}}"
            ApplicationPool = "{{cfg.app_pool}}"
            BindingInfo     = @(
                    Protocol = "http"
                    Port     = { { cfg.port } }
        xWebApplication { { cfg.app_name } }
            Name       = "{{cfg.app_name}}"
            Website    = "{{cfg.site_name}}"
            WebAppPool =  "{{cfg.app_pool}}"
            PhysicalPath = Resolve-Path "{{pkg.svc_var_path}}"
            Ensure     = "Present"

Defining Database Connection Logic

In order to connect to the database, the steps below need to be executed in order to create the configuration to connect to the SQL Server. In the next section, Life cycle Event Handlers (Hooks), the completed code is shown. The PowerShell code below will parse the csproj file for the web.config and create a new on that can be updated with the appropriate connection string:
Copy-Item '.\C#\ContosoUniversity\Web.config' .\habitat\config
Remove-Item '.\C#\ContosoUniversity\Web*.config'
[xml]$xml = Get-Content '.\C#\ContosoUniversity\ContosoUniversity.csproj'
$nodes = $xml.Project.ItemGroup.Content | Where-Object { $_.Include -like "Web.*" }
$nodes | ForEach-Object { $_.ParentNode.RemoveChild($_) }
$f = Resolve-Path '.\C#\ContosoUniversity\ContosoUniversity.csproj'
    <add name="SchoolContext" connectionString="Data Source={{bind.database.first.sys.ip}},{{bind.database.first.cfg.port}};Initial Catalog=ContosoUniversity2;User ID={{bind.database.first.cfg.username}};Password={{bind.database.first.cfg.password}};" providerName="System.Data.SqlClient" />
The templatized web.config will need to be updated during the init hook with the code below: Init Hook Update
Set-Location {{pkg.svc_path}}var
New-Item -Name Web.config -ItemType SymbolicLink -target "{{pkg.svc_config_path}}/Web.config" -Force | Out-Null
One last step is needed. The run hook needs to be able to update the permissions of the web.config file. Add the code below to the run hook: Run Hook Update
Import-Module "{{pkgPathFor "core/dsc-core"}}/Modules/DscCore"
Start-DscCore (Join-Path {{pkg.svc_config_path}} website.ps1)
NewWebsite $pool = "{{cfg.app_pool}}"
$access = New-Object System.Security.AccessControl.FileSystemAccessRule "IIS APPPOOL$pool", "ReadAndExecute", "Allow"
$acl = Get-Acl "{{pkg.svc_config_path}}/Web.config"
$acl | Set-Acl "{{pkg.svc_config_path}}/Web.config"

Life cycle Event Handlers (Hooks)

On Windows, PowerShell Core is used in the Habitat Plan to implement event-driven hooks which occur throughout the life cycle of applications/services. In our example, we will focus on the init and run hooks (these are most common). The init hook executes when the application package is initially installed and can be used to ensure certain files are available or configuration items are in place: Init Hook
Set-Location {{pkg.svc_path}}
if (Test-Path var) {
    Remove-Item var -Recurse -Force
New-Item -Name var -ItemType Junction -target "{{pkg.path}}/www" | Out-Null
Set-Location {{pkg.svc_path}}\var
New-Item -Name Web.config -ItemType SymbolicLink -target "{{pkg.svc_config_path}}/Web.config" -Force | Out-Null
The run hook executes after the init hook, either when the application package starts or is updated or when the package configuration changes. The run hook in our case is used to prepare the server for application installation and also to start the service itself. Again in our case, PowerShell DSC resources are made available. They are downloaded from the PowerShell Gallery, a public repository hosted by Microsoft, though they can also be downloaded from elsewhere. Permissions are also set for the IIS configuration in our run hook. Any arbitrary PowerShell code can be used here to configure the application: Run Hook
# The Powershell Progress stream can sometimes interfere
# with the Supervisor output. Its non critical so turn it off

# We need to install the xWebAdministration DSC resource.
# Habitat runs its hooks inside of Powershell Core but DSC
# configurations are applied in a hosted WMI process by
# Windows Powershell. In order for Windows Powershell to locate
# the installed resource, it must be installed using Windows
# Powershell instead of Powershell Core. We can use Invoke-Command
# and point to localhost to "remote" from Powershell Core to
# Windows Powershell.
Invoke-Command -ComputerName localhost -EnableNetworkAccess {
    Write-Host "Checking for nuget package provider..."
    if(!(Get-PackageProvider -Name nuget -ErrorAction SilentlyContinue -ListAvailable)) {
        Write-Host "Installing Nuget provider..."
        Install-PackageProvider -Name NuGet -Force | Out-Null
    Write-Host "Checking for xWebAdministration PS module..."
    if(!(Get-Module xWebAdministration -ListAvailable)) {
        Write-Host "Installing xWebAdministration PS Module..."
        Install-Module xWebAdministration -Force | Out-Null

# Leverage the Powershell Module in the dsc-core package
# that makes applying DSC configurations in Powershell
# Core simple.
Import-Module "{{pkgPathFor "core/dsc-core"}}/Modules/DscCore"
Start-DscCore (Join-Path {{pkg.svc_config_path}} website.ps1) NewWebsite

# The svc_config_path lacks an ACL for the USERS group
# so we need to ensure the app pool user can access those files
$pool = "{{cfg.app_pool}}"
$access = New-Object System.Security.AccessControl.FileSystemAccessRule `
"IIS APPPOOL\$pool",`
$acl = Get-Acl "{{pkg.svc_config_path}}/Web.config"
$acl | Set-Acl "{{pkg.svc_config_path}}/Web.config"

# The run hook must run indefinitely or else the Supervisor
# will think the service has terminated and will loop
# trying to restart it. The above DSC apply starts our
# application in IIS. We will continuously poll our app
# and cleanly shut down only if the application stops
# responding or if the Habitat service is stopped or
# unloaded.
try {
    Write-Host "{{}} is running"
    $running = $true
    while($running) {
        Start-Sleep -Seconds 1
        $resp = Invoke-WebRequest "http://localhost:{{cfg.port}}/{{cfg.app_name}}" -Method Head
        if($resp.StatusCode -ne 200) { $running = $false }
catch {
    Write-Host "{{}} HEAD check failed"
finally {
    # Add any cleanup here which will run after supervisor stops the service
    Write-Host "{{}} is stoping..."
    ."$env:SystemRoot\System32\inetsrv\appcmd.exe" stop apppool "{{cfg.app_pool}}"
    ."$env:SystemRoot\System32\inetsrv\appcmd.exe" stop site "{{cfg.site_name}}"
    Write-Host "{{}} has stopped"

Building the Package

The Habitat Studio is a cleanroom for building and testing your Habitat packages. On Windows, the Studio exposes the Windows system, core Habitat services, and the application source directories. The Studio will download required, missing packages or update any pre-installed packages upon starting, so may take a few minutes longer to start the first time. When using Docker to run Habitat Studio, the underlying Windows containers will be pulled which may also take time. Executing the build command within the Studio will gather package dependencies and source code (may be installation binaries for COTS applications), and assemble a Habitat Artifact (HART) package for testing and distribution:
hab studio enter -W
$env:HAB_DOCKER_OPTS="--memory 2gb -p 80:8099"
hab studio enter -D

Testing the Habitat Package Locally

To test the package locally within the Habitat Studio, run the commands below. This will install and configure SQL Server 2017, IIS, ASP.NET, and our sample application. After loading the core/sqlserver package, we check the Habitat Supervisor log to ensure it is fully running before loading other dependent packages:
hab svc load core/sqlserver
sqlserver.default hook[post-run]:(HK): 1> 2> 3> 4> 5> 6> Application user setup complete
hab svc load manny-rodriguez/contosouniversity --bind database:sqlserver.default
Open a browser to http://<local IP>/hab_app after seeing the following output in the Supervisor log:
contosouniversity.default(O): contosouniversity is running
Congratulations! You’re almost there. Finally, the Habitat package should be uploaded to the Builder Depot using the command below. You should point to the current build file to ensure you publish the latest changes:
hab pkg upload .resultsmanny-rodriguez-contosouniversity-0.2.0-20190314110601-x86_64-windows.hart

Deploying to a Server

When your applications are packaged with Chef Habitat, the only installation requirement on your target servers is Chef Habitat. Chocolatey can be used here again or any other deployment method. Habitat will also need to be configured as outlined in Workstation Setup. Start the Habitat Supervisor using hab sup run and execute the same commands used when testing locally to load your application. After the Supervisor is started, a new PowerShell prompt may need to be opened. A Windows service can also be used to run the Habitat Supervisor unattended, which we’ll cover in a subsequent post:
hab sup run
hab svc load core/sqlserver
sqlserver.default hook[post-run]:(HK): 1> 2> 3> 4> 5> 6> Application user setup complete
hab svc load manny-rodriguez/contosouniversity --bind database:sqlserver.default
Once again, open a browser to http://<server hostname or IP>/hab_app once the Supervisor log indicates your service is running.

Exporting Packages for Docker

Aside from deploying HART files directly to traditional server environments, Habitat packages can be exported to Docker containers or other run-time formats using hab pkg export. We illustrate this below using two packages: the core package for SQL Server 2017 and the one we just built for our sample application.
hab pkg export docker core/sqlserver
hab pkg export docker .resultsmanny-rodriguez-contosouniversity-0.2.0-20190314110601-x86_64-windows.hart
In order for our application container to communicate with the SQL Server container, we need to note the IP address of the SQL Server container and feed this to the docker run command for our application. Following is some PowerShell code to capture the SQL Server container IP in a variable:
$sql = docker run -d --memory 2GB core/sqlserver
$ip = docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $sql
docker run -it -p 80:8099 myorigin/contosouniversity --peer $ip --bind database:sqlserver.default

Wrapping Up

In this blog post we took a Modern ASP.NET Application and packaged and deployed it with Chef Habitat. This included the prerequisites of installation of the required software, creating the needed accounts and setting up Chef Habitat. A Habitat Plan was created and modified for packaging and deployment of the ASP.NET Application. We learned about the Lifecycle Event Handlers (hooks) and how to use them to build the package and run the application. A local environment was used to build, package, test and upload to the Builder Depot. Next, a Docker Container was exported for SQL Server 2017 and our ASP.NET application. The Docker Containers were started, applications and Windows Features were installed and configured. Testing resulted in the ASP.NET Application running. The key takeaways are that Applications can be quickly built, packaged, deployed and managed using Chef Habitat. This changes the way Applications are currently managed through their Life cycle, resulting in less time spent during the development cycle, along with quicker deployments saving time and money. In the next Blog Post, we will discuss packaging a Legacy ASP.NET application that uses a no longer supported version of SQL Server.

Perficient can help!

With Windows and SQL Server end-of-support happening beginning this year, now is the time to begin migrating those legacy applications with Habitat. This approach eliminates your dependencies on these legacy operating systems and helps you avoid costly support contracts.  We can also help you modernize your application development processes at the core, using an OS independent approach that makes your business more innovative and resilient for the future.  Let us know if we can help.