Azure DevOps is a popular platform for software development and deployment. The platform provides a big list of build-in tasks 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.
-
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 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 } }
-
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\$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 } }
-
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" } } }
-
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}
-
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 }
-
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 }
-
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
-
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")
-
Installing the .NET Core Runtime version
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 d:\dotnet-install.ps1 -Runtime dotnet -Version 2.2.8
-
Installing the ASP.NET Core Runtime version
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 d:\dotnet-install.ps1 -Runtime aspnetcore -Version 2.2.8
-
Executing the SQL script
$Query = "select * from [MyDatabase].[dbo].[MyTable]" Invoke-Sqlcmd -ConnectionString "$(ConnectionString)" -Query $Query | Out-File -FilePath "D:\temp\select_log.txt"
-
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 } }
-
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.