﻿#### 

Azure DevOps is a popular platform for software development and deployment. The platform provides a big list of [build-in tasks](https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/?view=azure-devops#deploy) for build and deployment, which is quite useful. Nevertheless, they are often not enough and you have to implement your own scripts. I prefer doing this by adding PowerShell scripts to the pipelines and here I would like to share my top PowerShell scripts that can be helpful for every developer.

**Disclaimer:** avoid using **IIS Web App Manage** tasks for *IIS* and *App Pool* managing.

**Problem:** it fails with an error if the current status is not the same as expected.

**Example:** the Stop App Pool task will fail if an application pool is already stopped.

1. Stopping a website if it exists or creating a new one and stopping it if it doesn’t exist

    ```
    
    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 bindingNew-Item IIS:\Sites\$siteName -bindings @{protocol="http";bindingInformation=":80:" + $siteName} -physicalPath $path$status = Get-WebsiteState -name $siteNameif ($status.Value -ne 'Stopped'){   Write-Output ('Stopping IIS Website: {0}' -f $siteName)	Stop-WebSite -Name $siteName}
    }
    ```
2. Stopping an App Pool if it exists or creating a new one and stopping it if it doesn’t exist

    ```
    
    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\$siteNameSet-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. Ensuring that the website and the 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. Setting the 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. Setting the SSL certificate and adding HTTPS binding to the 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. Setting an 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. Pinging the website to be sure whether 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 a .Net Core-related software with a specific version. Since you don`t have access to the server, you have to install it utilizing pipeline tasks. The easiest way to do it is to download the *dotnet-install.ps1* script and use it for the 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. Installing the .NET Core Runtime version

    ```
    
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 
    d:\dotnet-install.ps1 -Runtime dotnet -Version 2.2.8
    ```
10. Installing the ASP.NET Core Runtime version

    ```
    
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    d:\dotnet-install.ps1 -Runtime aspnetcore -Version 2.2.8
    ```
11. Executing the SQL script

    ```
    
    $Query = "select * from [MyDatabase].[dbo].[MyTable]"
    Invoke-Sqlcmd -ConnectionString "$(ConnectionString)" -Query $Query | Out-File -FilePath "D:\temp\select_log.txt"
    ```
12. Showing the logs by mask (for a 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. Zipping and uploading the directory

    ```
    
    $OutputDir = "$(System.DefaultWorkingDirectory)\output"
    
    Compress-Archive -Path $OutputDir -DestinationPath $OutputDir -Force
    Write-host "##vso[task.uploadfile]$OutputDir.zip"
    ```

I hope you find these scripts useful and expand your toolset for project deployment.

###### Author

[!\[sergey-200\](https://www.brimit.com/-/jssmedia/feature/blogs/authors/sergey-200.jpg?h=197&amp;iar=0&amp;w=200&amp;hash=0D636570F87F1C13C39E06EB19D9DF06)
Sergey Baranov
Sitecore MVP/ Senior Sitecore Developer](https://www.brimit.com/blog/author?authors=Sergey%20Baranov)

#### More on Sitecore

[!\[How Vercel Will Help You Save Effort When Deploying Sophisticated Sitecore Projects\](https://www.brimit.com/-/jssmedia/project/brimit/blog/2024/vercel_cover-image.png)
#Guides#How-toDXPE-commerce
##### How Vercel Will Help You Save Effort When Deploying Sophisticated Sitecore Projects
Optimize and accelerate the development and deployment of complex multisite Sitecore projects.
Alexei Vershalovich on July 17, 2024](https://www.brimit.com/blog/how-vercel-will-help-you-save-effort-when-deploying-sophisticated-sitecore-projects)

[!\[Training Up Tomorrow's Sitecore MVPs: a Mentoring Success Story\](https://www.brimit.com/-/jssmedia/project/brimit/blog/2023/sitecore-mentoring---cover-image.png)
#How-toDXP
##### Training Up Tomorrow's Sitecore MVPs: a Mentoring Success Story
How to participate in the Sitecore Mentor program and help younger colleagues jump-start a career in Sitecore development.
Sergey Baranov on October 2, 2023](https://www.brimit.com/blog/training-up-tomorrows-sitecore-mvps)

[!\[Going Headless. Part 2: When a Headless CMS Is Your Best Bet (if you have Sitecore)\](https://www.brimit.com/-/jssmedia/project/brimit/blog/2022/headless/adobestock_456986731.jpg)
#How-toDXPE-commerce
##### Going Headless. Part 2: When a Headless CMS Is Your Best Bet (if you have Sitecore)
Discover how a headless CMS can benefit organizations that use Sitecore.
Daniil Raschupkin, Palina Trokhautsava on September 15, 2022](https://www.brimit.com/blog/going-headless-part-2-when-a-headless-cms-is-your-best-bet-if-you-have-sitecore)

![](https://bat.bing.net/action/0?ti=187017043&amp;tm=gtm002&amp;Ver=2&amp;mid=85044885-56ef-4183-9d2f-fea2fdbbd4f9&amp;bo=2&amp;gtm_tag_source=1&amp;pi=0&amp;lg=en-US&amp;sw=800&amp;sh=600&amp;sc=24&amp;nwd=1&amp;tl=Azure%20DevOps%20deployment%3A%20Useful%20powershell%20scripts&amp;kw=azure,devops,deployment,powershell&amp;p=https%3A%2F%2Fwww.brimit.com%2Fblog%2Fazure-devops-deployment-useful-powershell-scripts&amp;r=&amp;lt=261&amp;evt=pageLoad&amp;sv=2&amp;asc=D&amp;cdb=AQAY&amp;rn=508191)