Skip to content

Integrating .NET into Deployment

dennisburton edited this page Nov 13, 2014 · 6 revisions

Part 4: Integrating .NET into Deployment

Building a .NET Deployment Script

  1. Ensure current working directory is the repository root

  2. Create a scaffolding script for the WebAPI project

azure site deploymentscript --aspWAP .\src\api\API.csproj -s .\src\TodoList.sln -o .\apiscript

Select no at the prompt to overwrite the .deployment file

Add .NET Environment Variables into Deployment

  1. Open apiscript\deploy.cmd.

  2. Within the :: Setup section, copy the items after the definition of KuduSync to the clipboard (lines 50-62)

IF NOT DEFINED DEPLOYMENT_TEMP (
  SET DEPLOYMENT_TEMP=%temp%\___deployTemp%random%
  SET CLEAN_LOCAL_DEPLOYMENT_TEMP=true
)

IF DEFINED CLEAN_LOCAL_DEPLOYMENT_TEMP (
  IF EXIST "%DEPLOYMENT_TEMP%" rd /s /q "%DEPLOYMENT_TEMP%"
  mkdir "%DEPLOYMENT_TEMP%"
)

IF NOT DEFINED MSBUILD_PATH (
  SET MSBUILD_PATH=%WINDIR%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe
)
  1. Paste them in the primary deploy.cmd file--the one within the repository root--within the :: Setup section, after the definition of KUDU_SYNC_CMD and before goto Deployment.
  :: Locally just running "kuduSync" would also work
  SET KUDU_SYNC_CMD=%appdata%\npm\kuduSync.cmd
)

IF NOT DEFINED DEPLOYMENT_TEMP (
  SET DEPLOYMENT_TEMP=%temp%\___deployTemp%random%
  SET CLEAN_LOCAL_DEPLOYMENT_TEMP=true
)

IF DEFINED CLEAN_LOCAL_DEPLOYMENT_TEMP (
  IF EXIST "%DEPLOYMENT_TEMP%" rd /s /q "%DEPLOYMENT_TEMP%"
  mkdir "%DEPLOYMENT_TEMP%"
)

IF NOT DEFINED MSBUILD_PATH (
  SET MSBUILD_PATH=%WINDIR%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe
)

goto Deployment

Add .NET Commands into Deployment

  1. Note the Deployment commands in the :: Deployment section of apiscript\deploy.cmd.

    1. Restore NuGet packages will pull referenced packages from NuGet before the build.

    2. Build to the temporary path compiles the API Project and stages all deployable files to a TEMP folder.

    3. KuduSync synchronizes compiled output to the target wwwroot folder.

  2. Copy all 3 components to the clipboard (lines 68-89).

  3. Paste these sections into the primary deploy.cmd file after :Deployment but before echo Handling node.js deployment.

    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    :: Deployment
    :: ----------
    
    echo Handling .NET Web Application deployment.
    
    :: 1. Restore NuGet packages
    IF /I "src\TodoList.sln" NEQ "" (
      call :ExecuteCmd "%NUGET_EXE%" restore "%DEPLOYMENT_SOURCE%\src\TodoList.sln"
      IF !ERRORLEVEL! NEQ 0 goto error
    )
    
    :: 2. Build to the temporary path
    IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" (
      call :ExecuteCmd "%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\src\api\API.csproj" /nologo /verbosity:m /t:Build /t:pipelinePreDeployCopyAllFilesToOneFolder /p:_PackageTempDir="%DEPLOYMENT_TEMP%";AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release /p:SolutionDir="%DEPLOYMENT_SOURCE%\src\\" %SCM_BUILD_ARGS%
    ) ELSE (
      call :ExecuteCmd "%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\src\api\API.csproj" /nologo /verbosity:m /t:Build /p:AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release /p:SolutionDir="%DEPLOYMENT_SOURCE%\src\\" %SCM_BUILD_ARGS%
    )
    
    IF !ERRORLEVEL! NEQ 0 goto error
    
    :: 3. KuduSync
    IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" (
      call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_TEMP%" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd"
      IF !ERRORLEVEL! NEQ 0 goto error
    )
    
    echo Handling node.js deployment.
    

    This introduces a bug into our KuduSync script that is tricky to track down. Both KuduSync commands are using the same manifest. Right now, the MSBuild occurs first, building into TEMP. Then KuduSync copies from TEMP to the final destination, wwwroot. After gulp runs, KuduSync is executed again to synchronize gulp output to the final destination, deleting the files placed into wwwroot from the MSBuild process.
    We will fix this now by changing the order and some of the the destinations.

  4. Move the KuduSync command for the MSBuild step after the KuduSync command for the gulp build

popd

echo 3. KuduSync
IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" (
call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_SOURCE%\src\web\dist" -t "%DEPLOYMENT_TARGET%" -n "%DEPLOYMENT_SOURCE%" -p "%u%" -i ".git;.hg;.deployment;deploy.cmd"
  IF !ERRORLEVEL! NEQ 0 goto error
)

:: 3. KuduSync
IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" (
  call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_TEMP%" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd"
  IF !ERRORLEVEL! NEQ 0 goto error
)
  1. Update the commented numbers to echo and re-number for clean output and debugging

Original code:

:: 1. Restore NuGet packages
:: 2. Build to the temporary path
echo 1. Select node version
echo 2. Install npm packages
echo Execute Gulp
echo 3. KuduSync
:: 3. KuduSync

Revised code:

echo 1. Restore NuGet packages
echo 2. Build to the temporary path
echo 3. Select node version
echo 4. Install npm packages
echo 5. Execute Gulp
echo 6. KuduSync from Gulp
echo 7. KuduSync to wwwroot
  1. Change the destination for the gulp KuduSync step from %DEPLOYMENT_TARGET% to %DEPLOYMENT_TEMP%.
echo 6. KuduSync from Gulp
IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" (
  call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_SOURCE%\src\web\dist" -t "%DEPLOYMENT_TEMP%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd"
  IF !ERRORLEVEL! NEQ 0 goto error
)
  1. Change the manifest values to %DEPLOYMENT_SOURCE%\src\web\generated\manifest to both the next manifest (-n) and previous manifest (-p) on the gulp build KuduSync.
echo 6. KuduSync from Gulp
IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" (
  call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_SOURCE%\src\web\dist" -t "%DEPLOYMENT_TEMP%" -n "%DEPLOYMENT_SOURCE%\src\web\generated\manifest" -p "%DEPLOYMENT_SOURCE%\src\web\generated\manifest" -i ".git;.hg;.deployment;deploy.cmd"
  IF !ERRORLEVEL! NEQ 0 goto error
)
  1. Make sure NuGet is available for local build.

This is an awful hack. The NUGET_EXE environment variable is not set up when running locally. We need to find the NuGet executable and set the environment variable NUGET_EXE to point at it. Mine happens to be installed by Chocolatey, yours may be elsewhere. Just ensure that the version is NuGet v2.8 or greater.

Within the :: Setup section, after the definition of MSBUILD_PATH and before goto Deployment, add a line that ensures NUGET_EXE is defined and available.

IF NOT DEFINED MSBUILD_PATH (
  SET MSBUILD_PATH=%WINDIR%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe
)

IF NOT DEFINED NUGET_EXE (
  SET NUGET_EXE=C:\ProgramData\Chocolatey\lib\NuGet.CommandLine.2.8.0\tools\NuGet.exe
)

goto Deployment

Test Deployment Locally

  1. Return working directory to repository root.

  2. Run the deployment script.

deploy.cmd

Azure Deployment

  1. Create a SQL Database with a name of todosample on Azure and obtain its connection string.

Note that this should be in the same region as your WebSite

  1. In Package Manager Console, run update-database, specifying the Azure connection string.
update-database -ConnectionString "<string>" -ConnectionProviderName "System.Data.SqlClient"
  1. Visit the CONFIGURE tab of the Azure Website in the management portal.

  2. Add a connection string entry for todosdb and set its value to the connection string used above.

What is awesome here is that this allows for your web.config to remain safe. It will only ever point at a local database. You do not have to expose your production secrets anywhere but on the portal (or configuration script if you prefer). This setting will override the value in the web.config at runtime.

  1. Commit your changes and push to Azure

  2. Visit your Website and see your persisted changes now.