How to debug and test custom Sitecore OrderCloud middleware on your local environment

Sergey Baranov on September 13, 2020
Azure DevOps

Many companies and their developers use Azure DevOps platform for project deployment. Azure DevOps provides a big list of build-in tasks that can be useful, but very often it is not enough and you have to implement your own scripts. In this article I share my TOP of useful powershell scripts that can be helpful for everyone.

Disclaimer: avoid of using IIS Web App Manage taks for IIS and App Pool managing. Problem: it fails with error if current status is not the same as expected. Example: "Stop App Pool" task will fail if application pool is already stopped.

  1. Stop Website (if exists) or Create new and Stop (if not exists)

    
    Import-Module WebAdministration
    $siteName = 'website.com';
    $path = "c:\inetpub\wwwroot\website.com"
    
    $exist = (Get-Website -name $siteName) -ne $null
    
    if($exist){
    	$status = Get-WebsiteState -name $siteName
    
    	if ($status.Value -ne 'Stopped'){
    	   Write-Output ('Stopping IIS Website: {0}' -f $siteName)
    		Stop-WebSite -Name $siteName
    	}
    }
    else{
            # create new IIS website and 80 port binding
    	New-Item IIS:\Sites\$siteName -bindings @{protocol="http";bindingInformation=":80:" + $siteName} -physicalPath $path
    	$status = Get-WebsiteState -name $siteName
    	if ($status.Value -ne 'Stopped'){
    	   Write-Output ('Stopping IIS Website: {0}' -f $siteName)
    		Stop-WebSite -Name $siteName
    	}
    }
    
    
  2. Stop App Pool (if exists) or Create new and Stop (if not exists)

    
    Import-Module WebAdministration
    $siteName = 'website.com';
    
    if(Test-Path IIS:\AppPools\$siteName){
    	$status = Get-WebAppPoolState -name $siteName
    
    	if ($status.Value -ne 'Stopped'){
    	   Write-Output ('Stopping Application Pool: {0}' -f $siteName)
    		Stop-WebAppPool -Name $siteName
    	}
    }
    else{
    	New-Item IIS:\AppPools\$siteName
    	Set-ItemProperty IIS:\Sites\$siteName -name applicationPool -value $siteName
    	$status = Get-WebAppPoolState -name $siteName
    
    	if ($status.Value -ne 'Stopped'){
    	   Write-Output ('Stopping Application Pool: {0}' -f $siteName)
    		Stop-WebAppPool -Name $siteName
    	}
    }
    
    
  3. Ensure that Website and App Pool are both stopped

    
    Import-Module WebAdministration
    $siteName = 'website.com';
    
    $exist = (Get-Website -name $siteName) -ne $null
    if($exist){
    	if(Test-Path IIS:\AppPools\$siteName){
    		$success = $false;
    		# timer in 60 sec 
    		$sec = 60;
    
    		do{
    			$iisState = (Get-WebsiteState -name $siteName).Value
    			$iisstopped = $iisState -eq "Stopped"
    			$poolState  = (Get-WebAppPoolState -name $siteName).Value
    			$poolstopped = $poolState -eq "Stopped"
    
    			Write-Output "Website state: $iisState"
    			Write-Output "AppPoolstate: $poolState "
    
    			$success = $iisstopped -and $poolstopped
    
    			if(!$success){
    			 Start-Sleep -s 1
    			 $sec = $sec - 1;
    			}
    		}
    		while (!$success -and $sec -gt 0)
    
    		if(!$success){
    		  throw "Can not stop IIS or App pool"
    		}
    	}
    }
    
    
  4. Set App Pool Identity user

    
    Import-Module WebAdministration
    $siteName = 'website.com';
    $appUser = 'Apppool_User';
    $appPassword = 'Apppool_User_Password';
    
    Set-ItemProperty IIS:\AppPools\$siteName -name processModel -value @{userName=$appUser;password=$appPassword;identitytype=3}
    
    
    
  5. Set SSL certificate and add HTTPS binding to Website

    
    Import-Module WebAdministration
    $siteName = 'website.com';
    $fqdn = 'website.com';
    
    # get certificate by Subject
    $newCert =  dir Cert:\localmachine\My | where-Object {$_.subject -like "*website.com*"};
    # get certificate by thumbprint
    #$newCert =  dir Cert:\localmachine\My | where-Object {$_.Thumbprint -eq "1d486045ed3af7513b62ae43135421268bcde3dd"};
    
    
    $webbindings = Get-WebBinding -Name $siteName
    $webbindings
    
    
    $hasSsl = $webbindings | Where-Object { $_.protocol -like "*https*" }
    
    if($hasSsl)
    {
        Write-Output "An SSL certificate is already assigned."
    }
    else
    {
        Write-Output  "Applying TLS/SSL Certificate"
        New-WebBinding -Name $siteName -Port 443 -Protocol https -HostHeader $fqdn; #could add -IPAddress here if needed (and for the get below)
    	
    	$httpsBinding = Get-WebBinding -Name $siteName -Port 443 -Protocol "https" -HostHeader $fqdn;
    	
    	Write-Output $httpsBinding
    	
        $httpsBinding.AddSslCertificate($newCert.Thumbprint, "my")
    
        Write-Output  "`r`n`r`nNew web bindings"
        $webbindings = Get-WebBinding -Name $siteName
        $webbindings
    }
    
    
  6. Set environment variable

    
    Import-Module WebAdministration
    $siteName = 'website.com';
    $fqdn = 'website.com';
    
    $envVariables = "/system.applicationHost/applicationPools/add[@name='$(SITE_NAME)']/environmentVariables"
    $configSection = $envVariables + "/add[@name='ASPNETCORE_ENVIRONMENT']"
    $subsections=get-webconfiguration $configSection -PSPath iis:
    $subsections | foreach { $_.attributes | select name,value }
    
    if($subsections -eq $null){
    	$cmd='c:\windows\system32\inetsrv\appcmd.exe'
    &$cmd set config -section:system.applicationHost/applicationPools /+"[name='$(SITE_NAME)'].environmentVariables.[name='ASPNETCORE_ENVIRONMENT',value='$(ENVIRONMENT)']" /commit:apphost
    }
    
    
  7. Ping Website to be sure if it starts

    
    $KeepAliveUrl = "https://website.come/keepAlive"
    $InitTimeoutSec = 300
    $ReInitTimeoutSec = 60
    $ThirdTimeoutSec = 5
    
    Write-Host "KeepAliveUrl: " + $KeepAliveUrl;
    
    ## Enable TLS 1.2 to allow HTTPS communication
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    
    ## The first request after the deployment may take a long time 
    Write-Host "Request page to send a wake-up call and wait $InitTimeoutSec seconds to complete initialization"
    Invoke-WebRequest -Uri $KeepAliveUrl -TimeoutSec $InitTimeoutSec -UseBasicParsing
    
    ## Second request may still take a while as some initialization processes could
    ## have been started after first request (such as package installation via fridaycore), so smaller timeout here to make sure it is alright 
    Write-Host "Requestto send another wake-up call and wait $ReInitTimeoutSec seconds to complete"
    Invoke-WebRequest -Uri $KeepAliveUrl -TimeoutSec $ReInitTimeoutSec -UseBasicParsing
    
    ## Third and any further requests must be responded within 5s
    Invoke-WebRequest -Uri $KeepAliveUrl -TimeoutSec $ThirdTimeoutSec -UseBasicParsing
    
    
  8. Sometimes you need to install .Net Core related software with specific version, but you don`t have access to server and have to install in by pipeline tasks. The easiest way to do it is download dotnet-install.ps1 and use it for installations.

    
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    
    $WebClient = New-Object System.Net.WebClient
    $WebClient.DownloadFile("https://dotnet.microsoft.com/download/dotnet-core/scripts/v1/dotnet-install.ps1","D:\dotnet-install.ps1")
    
    
  9. Install .NET Core Runtime version

    
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 
    d:\dotnet-install.ps1 -Runtime dotnet -Version 2.2.8
    
    
  10. Install ASP.NET Core Runtime version

    
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    d:\dotnet-install.ps1 -Runtime aspnetcore -Version 2.2.8
    
    
  11. Execute SQL script

    
    $Query = "select * from [MyDatabase].[dbo].[MyTable]"
    Invoke-Sqlcmd -ConnectionString "$(ConnectionString)" -Query $Query | Out-File -FilePath "D:\temp\select_log.txt"
    
    
  12. Show logs by mask (for specific date)

    
    $logpath = "c:\inetpub\wwwroot\website.com\logs"
    $today = Get-Date -Format "yyyyMMdd"
    $userDate = $(Date)
    
    if(![string]::IsNullOrEmpty($userDate)){
     $today = $userDate
    }
    
    if (Test-Path -Path $logpath ){
    	Get-ChildItem -Path $logpath
    
    	Get-ChildItem $logpath -Filter *$today*.txt | 
    	Foreach-Object {
    		Get-Content $_.FullName
    	}
    }
    
    
  13. Zip and Upload directory

    
    $OutputDir = "$(System.DefaultWorkingDirectory)\output"
    
    Compress-Archive -Path $OutputDir -DestinationPath $OutputDir -Force
    Write-host "##vso[task.uploadfile]$OutputDir.zip"