├── CloudConfigs ├── AzureSubscriptionData.config ├── RoleProperties.txt ├── SQLAzureServerInfo.config ├── SearchConfig.config ├── ServiceConfiguration.Cloud.cscfg ├── ServiceConfiguration.Local.cscfg ├── ServiceDefinition.csdef ├── StartupTasks │ ├── ConfigureIISAppPool.cmd │ ├── ConfigureIISAppPool.ps1 │ ├── ManageConfigACL.cmd │ └── ManageConfigACL.ps1 ├── Subscription1.publishsettings └── diagnostics.wadcfgx ├── README.md └── Scripts ├── CertificatesManagement.ps1 ├── CleanAzureData.ps1 ├── Common.ps1 ├── Configuration └── config.json ├── ConfigureRedisCache.ps1 ├── CreateAzurePackage.ps1 ├── CreateSitefinityAzureResourceGroup.ps1 ├── DatabaseAzure.ps1 ├── DeploySitefinityToAzureAppService.ps1 ├── DeploySitefinityToAzureCloudService.ps1 ├── ManageAzureRedisCache.ps1 ├── ManageAzureResourceGroup.ps1 ├── ManageAzureServices.ps1 ├── ManageAzureStorage.ps1 ├── Modules.ps1 ├── PublishCloudService.ps1 ├── Templates ├── Default.json └── Default.params.json ├── UpdateAzureCloudConfigs.ps1 └── UpdateSitefinityConfigs.ps1 /CloudConfigs/AzureSubscriptionData.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | true 6 | https://management.core.windows.net 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /CloudConfigs/RoleProperties.txt: -------------------------------------------------------------------------------- 1 | TargetFrameWorkVersion=v4.0 -------------------------------------------------------------------------------- /CloudConfigs/SQLAzureServerInfo.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | SQLAzure 4 | 5 | 6 | 7 | 8 | 9 | false 10 | -------------------------------------------------------------------------------- /CloudConfigs/SearchConfig.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CloudConfigs/ServiceConfiguration.Cloud.cscfg: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CloudConfigs/ServiceConfiguration.Local.cscfg: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CloudConfigs/ServiceDefinition.csdef: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /CloudConfigs/StartupTasks/ConfigureIISAppPool.cmd: -------------------------------------------------------------------------------- 1 | PowerShell -ExecutionPolicy Unrestricted -File .\ConfigureIISAppPool.ps1 -------------------------------------------------------------------------------- /CloudConfigs/StartupTasks/ConfigureIISAppPool.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | 3 | .SYNOPSIS 4 | This scripts sets the Start Mode of the application pool and the Idle Timeout. 5 | 6 | #> 7 | 8 | param($startMode='AlwaysRunning', 9 | $idleTimeout='0', 10 | $logFileName="ConfigureIISAppPool") 11 | 12 | $Logfile = "$PSScriptRoot\$logFileName.log" 13 | 14 | try{ 15 | Import-Module WebAdministration 16 | }catch [Exception]{ 17 | Import-Module WebAdministration 18 | } 19 | 20 | function LogWrite 21 | { 22 | Param ([string]$logstring) 23 | 24 | Add-content $Logfile -value $logstring 25 | } 26 | 27 | try{ 28 | LogWrite "Start Updating Application Pool Defaults Settings ..." 29 | 30 | LogWrite "Setting Start Mode to `"$startMode`"..." 31 | Set-WebConfigurationProperty /system.applicationHost/applicationPools/applicationPoolDefaults[1] -name startMode -value $startMode 32 | 33 | LogWrite "Setting Idle Timeout to `"$idleTimeout`"..." 34 | Set-WebConfigurationProperty /system.applicationHost/applicationPools/applicationPoolDefaults[1]/processModel[1] -name idleTimeout -value $idleTimeout 35 | } 36 | catch [Exception]{ 37 | $("$file.name: " + $_.Exception.Message) | out-file $Logfile -Append 38 | Add-Content $Logfile $error 39 | } 40 | finally{ 41 | LogWrite "Finished Updating Application Pool Defaults Settings" 42 | } 43 | -------------------------------------------------------------------------------- /CloudConfigs/StartupTasks/ManageConfigACL.cmd: -------------------------------------------------------------------------------- 1 | PowerShell -ExecutionPolicy Unrestricted -File .\ManageConfigACL.ps1 -------------------------------------------------------------------------------- /CloudConfigs/StartupTasks/ManageConfigACL.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | 3 | .SYNOPSIS 4 | This scripts gives permissions for the NETWORK SERVICE user to modify Siefinity files. 5 | 6 | #> 7 | 8 | param($defaultDrive = "E:\", 9 | $logFileName = "ManageConfigACL") 10 | 11 | $Logfile = "$PSScriptRoot\$logFileName.log" 12 | 13 | function LogWrite 14 | { 15 | Param ([string]$logstring) 16 | 17 | Add-content $Logfile -value $logstring 18 | } 19 | 20 | try{ 21 | LogWrite "Start managing configuration ACL..." 22 | 23 | $drive = $defaultDrive 24 | $rootFolder = $PSScriptRoot 25 | $tokens = $rootFolder.Split(":") 26 | 27 | if($tokens.length -gt 0){ 28 | $drive = "{0}:\" -f $tokens[0] 29 | } 30 | 31 | $configurationPath = Join-Path $drive "approot\App_Data\Sitefinity\Configuration\*" 32 | 33 | LogWrite "PSScriptRoot is '$PSScriptRoot'" 34 | LogWrite "drive is '$drive'" 35 | LogWrite "configurationPath is '$configurationPath'" 36 | 37 | LogWrite "Setting configuration ACL for '$configurationPath'" 38 | 39 | icacls $configurationPath '/grant' '"NT AUTHORITY\NetworkService":(w)' 40 | } 41 | catch [Exception]{ 42 | $("$file.name: " + $_.Exception.Message) | out-file $Logfile -Append 43 | LogWrite $error 44 | } 45 | finally{ 46 | LogWrite "Finished managing configuration ACL" 47 | } 48 | -------------------------------------------------------------------------------- /CloudConfigs/Subscription1.publishsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /CloudConfigs/diagnostics.wadcfgx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sitefinity Azure PowerShell Deployment Scripts 2 | 3 | The repository contains scripts for continuous deployment of Sitefinity sites to Azure. You can use the scripts to integrate your Sitefinity to scalable continues integration and automatic deployments. Scripts allow you to integrate uploads from source controls and repository tools like TFS, Git, and GitHub. 4 | You can use scripts to publish Sitefinity directly from your local computer to Azure Web Apps (Azure Websites) and Azure Cloud Service. 5 | 6 | # Features 7 | 8 | - Azure Cloud Service Deployment -Highly available, scalable n-tier cloud apps with more control of the OS 9 | - Azure Websites Deployment -Scalable Web Apps 10 | 11 | Scripts automatically prepare your Sitefinity instance for deployment by modifying needed configurations. 12 | 13 | Scripts support configuration for 14 | - Database instance - create SQL server or connect to existing one. If database doesn't exists new database is created and Sitefinity data is then imported to Azure Database. 15 | - Redis Cache - create a new Redis Cache or use existing one. Note that it is better to use different Redis key prefixes for different instances in case you use one Redis Cache. 16 | - Remote Desktop - option to connect to Cloud Service through remote desktop on the machine. This is not supported for Azure Websites. 17 | - Extended Azure Logs - provides information about your Azure Role and thousands of metrics related to Azure environment 18 | - NLB instances - allows to use Sitefinity in NLB scenario - your Sitefinity license must support NLB 19 | - Azure Search - using Azure Search instead of built-in Lucene provider. Azure Search provides the engine for full-text search text analysis, advanced search features, search data storage, and a query command syntax 20 | - Blob storage providers - use blob storage for your binary data instead of file system or database. Blob storage service allows you to store Sitefinity large amounts of unstructured data, such as text or binary data( images or videos), that can be accessed from anywhere in the world via HTTP or HTTPS. You can use Blob storage to expose data publicly to the world, or to store application data privately 21 | - Use of publish settings file (Management certificate authentication) 22 | 23 | ## Requirements 24 | * Powershell 3.0+ 25 | * Microsoft Azure Powershell - https://github.com/Azure/azure-powershell/releases 26 | * Microsoft Azure SDK 2.9.6 - https://www.microsoft.com/en-us/download/details.aspx?id=54289 27 | * Microsoft Azure Authoring Tools 28 | * Microsoft Azure Libraries for NET 29 | * Data Tier Application (DAC) Framework - https://www.microsoft.com/en-us/download/confirmation.aspx?id=53013 30 | * Web Deploy v.3.5 - https://www.microsoft.com/en-us/download/details.aspx?id=39277 31 | 32 | ## Azure Cloud Service Deployment 33 | 34 | #### Update the **AzureSubscriptionData.config** file under **~\CloudConfigs** folder 35 | - Set your **SubscriptionId** 36 | - Set your **SubscriptionName** 37 | - Set your **DeploymentLabel** 38 | 39 | #### Update the **Subscription1.publishsettings** file under **~\CloudConfigs** folder 40 | - Set your Subscription **Id** 41 | - Set your Subscription **Name** 42 | - Set your **ManagementCertificate** 43 | 44 | #### Update the needed properties in the **config.json** file under **~\Scripts\Configuration** folder 45 | - Set your azure **serverName** 46 | - Set your azure **server** 47 | - Set your azure **user** 48 | - Set your azure **password** 49 | - Set your azure **subscription** 50 | - Set the **certificate** properties which is used for **Remote Desktop Access** and for **SSL endpoint** 51 | 52 | ### Using lower version of Azure SDK for .NET 53 | If a lower version of Azure SDK for .NET than the specified in the requirements is used - make sure to update the **schemaVersion** attribute in: 54 | - CloudConfigs/ServiceConfiguration.Cloud.cscfg 55 | - CloudConfigs/ServiceConfiguration.Local.cscfg 56 | - CloudConfigs/ServiceDefinition.csdef 57 | 58 | #### Run the script for Deploying Sitefinity to Azure Cloud Service 59 | ```powershell 60 | .\DeploySitefinityToAzure.ps1 -websiteRootDirectory "C:\temp\SitefinityWebApp" -databaseName "SfDB1" -sqlServer ".\SQLSERVER" -serviceName "myservicename" -storageAccountName "mystorageaccname" -enableRemoteDesktopAccess "true" -enableSsl "false" 61 | ``` 62 | #### Run the script for cleaning the Azure storage, service and database 63 | ```powershell 64 | .\CleanAzureData.ps1 -azureDatabaseName "SfDB1" -cloudServiceName "myservicename" -storageAccountName "mystorageaccname" 65 | ``` 66 | ## Azure App Services Deployment 67 | 68 | #### Setup for Azure App Services deployment 69 | 1. Set Subscription Information 70 | 1. Run ```Get-AzurePublishSettingsFile``` powershell command or simply navigate to https://manage.windowsazure.com/publishsettings/index?client=powershell to download the publish settings file for your subscription. 71 | 1. Replace the content of **.\CloudConfigs\Subscription1.publishsettings** with the content from the publishsettings you have download in the previous step. 72 | 1. Open .\CloudConfigs\AzureSubscriptionData.config and set the following values: 73 | 1. **CertificateData** - located in the Subscription1.publishsettings file in the ManagementCertificate node 74 | 1. **SubscriptionId** - located in the Subscription1.publishsettings file in the Id node. 75 | 1. **SubscriptionName** - located in the Subscription1.publishsettings file in the Name node. 76 | 1. **DeploymentLabel** - label by your choice. 77 | 1. Set SQL Server information in **.\Scripts\Configurations\config.json** under azure.sql 78 | 1. **location** - this value is to differentiate multiple SQL Server instances in different location. Afterwards the script will know which SQL Server to choose by passing a location parameter. E.g. West Europe 79 | 1. **serverName** - Azure SQL Server name in Azure (e.g. sitefinitysql) 80 | 1. **server** - Full connection string of the Azure SQL Server (e.g. sitefinitysql.database.windows.net) 81 | 1. **user** - Azure SQL user 82 | 1. **password** - Azure SQL password 83 | 1. Open a new powershell session and run the script by specyfing the following parameters: 84 | 1. **websiteRootDirectory** - path to the SitefinityWebApp 85 | 1. **sqlServer** - the local instance of SQL Server (e.g. ".\SQLSERVER") 86 | 1. **databaseName** - the database name of the SitefinityWebApp you will deploy 87 | 1. **websiteName** - the app service name (website name) that will be created in azure 88 | 1. **[Optional]redisCacheConnectionString** - set this if you want to use redis cache. The correct format of the connection string is the following: ```primaryAccessKey@redisCacheName.redis.cache.windows.net?ssl=true``` 89 | 1. **[Optional]websiteLocation** - Set where the location of the app service will be. This should match the SQL location property in the config.json you had applied in step. Default value is West Europe. 90 | 1. **[Optional]buildConfiguration** - Set the build configuration in which your project will be built - Debug/Release/ReleasePro... Default value is Release 91 | 1. **[Optional]launchWebsite** - if set, after the deployment finishes, powershell will make a request to the deployed website. Default value is $true 92 | 93 | 94 | **NOTE:** The scripts use default template file located in **.\Scripts\Templates\** You can use your own template file, but be careful with some of the properties, because there are some policies. For example there are policies for the sqlServerName, sqlPassword and search index name. 95 | 96 | 97 | #### Run the script for Deploying Sitefinity to Azure App Services 98 | ```powershell 99 | .\DeploySitefinityToAzureAppService.ps1 -websiteRootDirectory "C:\Tests\Sitefinity_10_0_HF3\Projects\azureAppServiceDemo" -databaseName "azureAppServiceDemoDb" -sqlServer ".\SQLSERVER" -websiteName "azureappsvcsfdemo" -redisCacheConnectionString "l6b65PIJza3zamYsNto8/cvtwtvvs1G4ffPBL3V6ybo=@sfdemoredis.redis.cache.windows.net?ssl=true" -websiteLocation "West Europe" -deployDatabase $true -buildConfiguration "Release" -launchWebsite $true 100 | ``` 101 | -------------------------------------------------------------------------------- /Scripts/CertificatesManagement.ps1: -------------------------------------------------------------------------------- 1 | function AddCertificateToService($serviceName, $certificate, $password) 2 | { 3 | Add-AzureCertificate -serviceName $serviceName -certToDeploy $certificate –password $password 4 | } 5 | 6 | function ImportCertificate($certPath, $thumbPrint) 7 | { 8 | $file = ( Get-ChildItem -Path $certPath ) 9 | 10 | if ($file -eq $null) { 11 | throw “Management certificate could not be found under $certPath” 12 | } 13 | 14 | $cert = Get-Item Cert:\CurrentUser\My\$thumbPrint 15 | if ($cert -eq $null) { 16 | Write-Host “Management certificate was not found start importing to 'cert:\CurrentUser\My'” 17 | $file | Import-Certificate -CertStoreLocation cert:\CurrentUser\My 18 | } 19 | return $cert 20 | } -------------------------------------------------------------------------------- /Scripts/CleanAzureData.ps1: -------------------------------------------------------------------------------- 1 | param($azureDatabaseName, 2 | $cloudServiceName, 3 | $storageAccountName, 4 | $accountLocation = "West Europe") 5 | 6 | . "$PSScriptRoot\Modules.ps1" 7 | 8 | 9 | $sqlConfig = $config.azure.sql | Where-Object { $_.location -eq $accountLocation } 10 | 11 | 12 | if($azureDatabaseName) 13 | { 14 | DeleteAzureDatabase $sqlConfig.serverName $azureDatabaseName $sqlConfig.user $sqlConfig.password 15 | } 16 | else 17 | { 18 | Write-Host "Database name is empty. Database cleanup will be skipped." -ForegroundColor Red 19 | } 20 | 21 | if($cloudServiceName) 22 | { 23 | RemoveCloudService $cloudServiceName 24 | } 25 | else 26 | { 27 | Write-Host "Cloud Service name is empty. Cloud Service cleanup will be skipped." -ForegroundColor Red 28 | } 29 | 30 | if($storageAccountName) 31 | { 32 | RemoveStorageAccount $storageAccountName 33 | } 34 | else 35 | { 36 | Write-Host "Storage Account name is empty. Storage Account cleanup will be skipped." -ForegroundColor Red 37 | } -------------------------------------------------------------------------------- /Scripts/Common.ps1: -------------------------------------------------------------------------------- 1 | $MsBuildExe = "C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe" 2 | 3 | function Ensure-File([string]$path) { 4 | if ([System.IO.File]::Exists($path) -eq $false) { 5 | throw (New-Object 'System.IO.FileNotFoundException' -ArgumentList("${path} does not exist.")) 6 | } 7 | } 8 | 9 | function LogMessage($message) 10 | { 11 | $currentTime = Get-Date -Format "HH:mm:ss" 12 | Write-Host "[[$currentTime]]::LOG:: $message" 13 | } 14 | 15 | function Get-Settings([string]$settingsPath) { 16 | Ensure-File -path $settingsPath 17 | 18 | $json = Get-Content $settingsPath -Raw 19 | $instance = $json | ConvertFrom-Json 20 | 21 | return $instance 22 | } 23 | 24 | function Get-AzureSdkPath { 25 | param($azureSdkPath) 26 | if(!$azureSdkPath) 27 | { 28 | if(!(Test-Path "$env:ProgramFiles\Microsoft SDKs\Azure\.NET SDK")) 29 | { 30 | $azureSdkPath = (dir "$env:ProgramFiles\Microsoft SDKs\Windows Azure\.NET SDK" -ErrorAction SilentlyContinue | sort Name -desc | select -first 1 ).FullName 31 | } 32 | else 33 | { 34 | #Path is changed for Azure .NET SDk 2.4 and above. 35 | $azureSdkPath = (dir "$env:ProgramFiles\Microsoft SDKs\Azure\.NET SDK" -ErrorAction SilentlyContinue | sort Name -desc | select -first 1 ).FullName 36 | } 37 | 38 | } 39 | 40 | if(!$azureSdkPath -or !(Test-Path $azureSdkPath)) 41 | { 42 | throw "Azure SDK not found. Please specify the path to the Azure SDK in the AzureSdkPath parameter or modify the scrtips to get cspack.exe" 43 | } 44 | 45 | Write-Host "SDK path has been set to $azureSdkPath."; 46 | return $azureSdkPath 47 | } 48 | 49 | function Get-SqlPackageExePath 50 | { 51 | $MSSQLx64Directory = "$env:ProgramFiles\Microsoft SQL Server" 52 | $MSSQLx86Directory = "${env:ProgramFiles(x86)}\Microsoft SQL Server" 53 | if(Test-Path $MSSQLx64Directory) 54 | { 55 | $sqlPackageExe = Get-ChildItem $MSSQLx64Directory -Include SqlPackage.exe -Recurse -ErrorAction SilentlyContinue | Select-Object -Last 1 56 | if($sqlPackageExe -ne $null) 57 | { 58 | return $sqlPackageExe 59 | } 60 | } 61 | if(Test-Path $MSSQLx86Directory) 62 | { 63 | $sqlPackageExe = Get-ChildItem $MSSQLx86Directory -Include SqlPackage.exe -Recurse -ErrorAction SilentlyContinue | Select-Object -Last 1 64 | if($sqlPackageExe -ne $null) 65 | { 66 | return $sqlPackageExe 67 | } 68 | } 69 | throw "SqlPackage.exe was not found. Please ensure you have 'SqlPackage.exe' installed on your machine." 70 | } 71 | 72 | function BuildSln($sln, $target, $configuration, $paramsAsString) 73 | { 74 | LogMessage "Start building '$sln'" 75 | & $MsBuildExe $sln /t:$target /p:Configuration=$configuration /p:$paramsAsString 76 | } -------------------------------------------------------------------------------- /Scripts/Configuration/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "azure": { 3 | "sql": [ 4 | { 5 | "location": "", 6 | "serverName": "yourServerName", 7 | "server": "yourServerName.database.windows.net", 8 | "user": "yourUsername", 9 | "password": "yourPassword" 10 | } 11 | ], 12 | "subscription": "", 13 | "accountsLocation": "West Europe", 14 | "roleName": "SitefinityWebApp", 15 | "environment": "Production", 16 | "timeStampFormat": "g", 17 | "resourceGroupName": "", 18 | "azureAccount": "myschoolaccount@mydomain.onmicrosoft.com", 19 | "azureAccountPassword": "", 20 | "alwaysDeleteExistingDeployments": "1", 21 | "enableDeploymentUpgrade": "1", 22 | "sslEndpointName": "SslEndpoint", 23 | "storageAccountKey": "" 24 | }, 25 | "files": { 26 | "serviceDefinition": "../CloudConfigs/ServiceDefinition.csdef", 27 | "roleProperties": "../CloudConfigs/RoleProperties.txt", 28 | "cloudConfig": "../CloudConfigs/ServiceConfiguration.Cloud.cscfg", 29 | "subscriptionPublishSettings": "../CloudConfigs/Subscription1.publishsettings", 30 | "azureSubscriptionDataConfig": "../CloudConfigs/AzureSubscriptionData.config", 31 | "azureSqlServerInforFilePath": "../CloudConfigs/SQLAzureServerInfo.config", 32 | "diagnosticsConfig": "../CloudConfigs/diagnostics.wadcfgx", 33 | "searchConfig": "../CloudConfigs/SearchConfig.config" 34 | }, 35 | "remoteAccessSettings": [ 36 | { 37 | "name": "Microsoft.WindowsAzure.Plugins.RemoteAccess.Enabled", 38 | "value": "true" 39 | }, 40 | { 41 | "name": "Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountUsername", 42 | "value": "sitefinity" 43 | }, 44 | { 45 | "name": "Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountEncryptedPassword", 46 | "value": "yourAccountEncryptedPassword" 47 | }, 48 | { 49 | "name": "Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountExpiration", 50 | "value": "2018-01-22T23:59:59.0000000+02:00" 51 | }, 52 | { 53 | "name": "Microsoft.WindowsAzure.Plugins.RemoteForwarder.Enabled", 54 | "value": "true" 55 | } 56 | ], 57 | "certificate": 58 | { 59 | "path":"path to your certificate", 60 | "password":"your remote access password", 61 | "name": "Microsoft.WindowsAzure.Plugins.RemoteAccess.PasswordEncryption", 62 | "thumbprint": "your certificate thumbprint", 63 | "thumbprintAlgorithm": "sha1" 64 | } 65 | } -------------------------------------------------------------------------------- /Scripts/ConfigureRedisCache.ps1: -------------------------------------------------------------------------------- 1 | #Configures Redis cache in Sitefinity 2 | param( 3 | [Parameter(Mandatory=$True)] 4 | [String] 5 | $systemConfig, 6 | [Parameter(Mandatory=$True)] 7 | [String] 8 | $redisCacheConnectionString 9 | ) 10 | 11 | Write-Warning "Configuring redis cache..." 12 | 13 | $doc = New-Object System.Xml.XmlDocument 14 | $doc.Load($systemConfig) 15 | 16 | $loadBalancingConfigNode = $doc.SelectSingleNode("//systemConfig/loadBalancingConfig") 17 | if($loadBalancingConfigNode -eq $null) 18 | { 19 | $loadBalancingConfigNode = $doc.CreateElement("loadBalancingConfig") 20 | $systemConfigNode = $doc.SelectSingleNode("//systemConfig") 21 | $systemConfigNode.AppendChild($loadBalancingConfigNode) 22 | } 23 | 24 | $redisSettingsNode = $doc.SelectSingleNode("//systemConfig/loadBalancingConfig/redisSettings") 25 | if($redisSettingsNode -eq $null) 26 | { 27 | $redisSettingsNode = $doc.CreateElement("redisSettings") 28 | $loadBalancingConfigNode.AppendChild($redisSettingsNode) 29 | } 30 | 31 | $redisSettingsNode.SetAttribute("ConnectionString", $redisCacheConnectionString) 32 | 33 | $doc.Save($systemConfig) 34 | 35 | Write-Warning "Redis cache has been configured." -------------------------------------------------------------------------------- /Scripts/CreateAzurePackage.ps1: -------------------------------------------------------------------------------- 1 | . "$PSScriptRoot\Common.ps1" 2 | 3 | #Creates Azure package file that will be uploaded to Cloud Service 4 | function CreatePackage($serviceDefinitionPath, $outDir, $projectLocation, $roleName, $rolePropertiesFilePath) 5 | { 6 | $out = [string]::Format("/out:{0}", $outDir) 7 | $role = [string]::Format("/role:{0};{1}", $roleName, $projectLocation) 8 | $sites = [string]::Format("/sites:{0};{1};{2}", $roleName, "Web", $projectLocation) 9 | $roleProperties = [string]::Format("/rolePropertiesFile:{0};{1}", $roleName, $rolePropertiesFilePath) 10 | 11 | LogMessage ("[Start] creating azure cloud package with the following properites: definition path {0}, out directory {1}, role {2}, sites {3}, role properties {4}" -f $serviceDefinitionPath, $out, $role, $sites, $roleProperties) 12 | 13 | $cspackExe = Get-ChildItem -Path (Get-AzureSdkPath) -Include "cspack.exe" -Recurse | select -first 1 14 | & $cspackExe $serviceDefinitionPath $out $role $sites $roleProperties 15 | LogMessage ("[Completed] creating azure cloud package") 16 | } -------------------------------------------------------------------------------- /Scripts/CreateSitefinityAzureResourceGroup.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | $WebsiteRootDirectory = "", 3 | $DatabaseName = "", 4 | $SqlServer = "", 5 | $ResourceGroupName = "", 6 | $AzureAccount = "", 7 | $AzureAccountPassword = "", 8 | $ResourceGroupLocation = "West Europe", 9 | $TemplateFile = "$PSScriptRoot\Templates\Default.json", 10 | $TemplateParameterFile = "$PSScriptRoot\Templates\Default.params.json", 11 | $BuildConfiguration = "Release" 12 | ) 13 | 14 | . "$PSScriptRoot\Modules.ps1" 15 | 16 | $templateParams = Get-Settings $TemplateParameterFile 17 | 18 | $sitefinityProject = Join-Path $websiteRootDirectory "SitefinityWebApp.csproj" 19 | $bacpacDatabaseFile = "$PSScriptRoot\temp\$DatabaseName.bacpac" 20 | $sqlConnectionUser = $templateParams.parameters.sqlServerAdminLogin.value 21 | $sqlConnectionServer = "$($templateParams.parameters.sqlServerName.value).database.windows.net" 22 | $sqlConnectionUsername = "$sqlConnectionUser@$sqlConnectionServer" 23 | 24 | $systemConfigPath = Join-Path $websiteRootDirectory "App_Data\Sitefinity\Configuration\SystemConfig.config" 25 | $outputPath = Join-Path $websiteRootDirectory "pkg" 26 | $buildParameters = "OutputPath=$outputPath;IgnoreDeployManagedRuntimeVersion=true;FilesToIncludeForPublish=AllFilesInProjectFolder" 27 | 28 | # Create new azure resource group 29 | NewAzureResourceGroup -ResourceGroupName $ResourceGroupName ` 30 | -ResourceGroupLocation $ResourceGroupLocation ` 31 | -AzureAccount $AzureAccount ` 32 | -AzureAccountPassword $AzureAccountPassword ` 33 | -TemplateFile $TemplateFile ` 34 | -TemplateParameterFile $TemplateParameterFile 35 | 36 | # Configure powershell with publishsettings for your subscription 37 | Import-AzurePublishSettingsFile "$PSScriptRoot\$($config.files.subscriptionPublishSettings)" 38 | Set-AzureSubscription -SubscriptionName $config.azure.subscription 39 | Select-AzureSubscription -SubscriptionName $config.azure.subscription 40 | 41 | CreateDatabasePackage $sqlServer $DatabaseName $bacpacDatabaseFile 42 | DeployDatabasePackage $bacpacDatabaseFile $templateParams.parameters.sqlDatabaseName.value $sqlConnectionServer $templateParams.parameters.sqlServerAdminLogin.value $templateParams.parameters.sqlServerAdminLoginPassword.value 43 | 44 | # Update Sitefinity web.config and DataConfig.config with database settings. 45 | UpdateSitefinityWebConfig $websiteRootDirectory 46 | UpdateSitefinityDataConfig $websiteRootDirectory $sqlConnectionServer $sqlConnectionUsername $templateParams.parameters.sqlServerAdminLoginPassword.value $templateParams.parameters.sqlDatabaseName.value 47 | 48 | # Configure Redis Cache 49 | $redisCacheName = $templateParams.parameters.redisCacheName.value 50 | $redisPrimaryKey = GetAzureRedisCacheKey -ResourceGroupName $ResourceGroupName -CacheName $redisCacheName 51 | $redisCacheConnectionString = "$redisPrimaryKey@$redisCacheName.redis.cache.windows.net?ssl=true" 52 | LogMessage "RedisCache connection string: '$redisCacheConnectionString'" 53 | . "$PSScriptRoot\ConfigureRedisCache.ps1" $systemConfigPath $redisCacheConnectionString 54 | . "$PSScriptRoot\ConfigureTestNlbHandlers.ps1" $systemConfigPath 55 | 56 | # Configure Azure Search Service - currently no exposed API 57 | #$azureServiceAdminKey TODO 58 | #$azureSearchServiceName = $templateParams.parameters.azureSearchName.value 59 | #ConfigureAzureSearchService $config.files.searchConfig $azureServiceAdminKey $azureSearchServiceName 60 | #Copy-Item $config.files.searchConfig "$websiteRootDirectory\App_Data\Sitefinity\Configuration" -Force 61 | 62 | # Build deployment package 63 | BuildSln $sitefinityProject "Package" $BuildConfiguration $buildParameters 64 | 65 | $sfPackageLocationPath = Get-ChildItem $outputPath -Recurse -Include "SitefinityWebApp.zip" 66 | LogMessage "Publishing deployment package '$sfPackageLocationPath'..." 67 | Publish-AzureWebsiteProject -Name $templateParams.parameters.siteName.value -Package $sfPackageLocationPath -------------------------------------------------------------------------------- /Scripts/DatabaseAzure.ps1: -------------------------------------------------------------------------------- 1 | . "$PSScriptRoot\Common.ps1" 2 | $sqlpackageExe = Get-SqlPackageExePath 3 | 4 | # e.g. $bacpacDatabaseFile="C:\temp\DatabaseName.bacpac" 5 | function CreateDatabasePackage($sqlServer, $databaseName, $bacpacDatabaseFile) 6 | { 7 | LogMessage "Creating database package..." 8 | & $sqlpackageExe /a:Export /ssn:$sqlServer /sdn:$databaseName /tf:$bacpacDatabaseFile 9 | LogMessage "Database package has been created." 10 | } 11 | 12 | function DeployDatabasePackage($bacpacDatabaseFile, $databaseName, $azureServer, $user, $password) 13 | { 14 | LogMessage "Importing database package..." 15 | try 16 | { 17 | & $sqlpackageExe /a:Import /sf:$bacpacDatabaseFile /tsn:$azureServer /tdn:$databaseName /tu:$user /tp:$password /p:DatabaseEdition="Basic" 18 | } catch { 19 | if(!($_.Exception.Message -ne $null -and $_.Exception.Message.Contains("compatibility issues with SQL Azure"))) 20 | { 21 | throw $_.Exception 22 | } 23 | } 24 | LogMessage "Database package has been imported" 25 | } 26 | 27 | # The serverName must the be just the name of the azure server e.g. "servername" and not the full server address "vdxlfno62c.database.windows.net" 28 | function DeleteAzureDatabase($serverName, $databaseName, $user, $password) 29 | { 30 | $db_password = ConvertTo-SecureString -String $password -AsPlainText -Force 31 | $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $user, $db_password 32 | $context = New-AzureSqlDatabaseServerContext -ServerName $serverName -Credential $credential 33 | LogMessage "Deleting $databaseName from Azure $serverName server..." 34 | Remove-AzureSqlDatabase -Context $context -DatabaseName $databaseName -Force 35 | LogMessage "$databaseName database deleted from Azure $serverName server." 36 | } 37 | 38 | function EnsureDBDeleted($sqlServer, $dbName) 39 | { 40 | [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | out-null 41 | $Server = New-Object Microsoft.SqlServer.Management.Smo.Server($sqlServer) 42 | $DBObject = $Server.Databases[$dbName] 43 | if ($DBObject) 44 | { 45 | LogMessage "Deleting '$dbName' database from '$sqlServer' SQL Server." 46 | $Server.KillAllProcesses($dbName) 47 | $Server.KillDatabase($dbName) 48 | } 49 | } 50 | 51 | function ImportDatabaseFromAzure($azureServer, $databaseName, $user, $password, $sqlServer) 52 | { 53 | LogMessage "Exporting '$databaseName' database from '$azureServer' azure server..." 54 | $bacpacDatabaseFile = "$PSScriptRoot\$databaseName.bacpac" 55 | & $sqlpackageExe /a:Export /ssn:$azureServer /sdn:$databaseName /su:$user /sp:$password /tf:$bacpacDatabaseFile 56 | 57 | EnsureDBDeleted $sqlServer $databaseName 58 | 59 | LogMessage "Importing '$databaseName' database from '$azureServer' azure server..." 60 | & $sqlpackageExe /a:Import /sf:$bacpacDatabaseFile /tdn:$databaseName /tsn:$sqlServer 61 | 62 | Remove-Item $bacpacDatabaseFile 63 | } 64 | 65 | function BackupDatabase($sqlServer, $databaseName, $bakupFolder) 66 | { 67 | New-Item -ItemType Directory -Path $bakupFolder -Force 68 | $dbBakFullPath = $bakupFolder+"\" + $databaseName +".bak" 69 | SQLCMD.EXE -S $sqlServer -E -q "exit(BACKUP DATABASE [$databaseName] TO DISK='$dbBakFullPath')" 70 | } -------------------------------------------------------------------------------- /Scripts/DeploySitefinityToAzureAppService.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | 3 | .SYNOPSIS 4 | Script to deploy web app to Azure App Services. 5 | 6 | .DESCRIPTION 7 | This Powershell Script applies the required modifications and deploys the database and the sitefinity web app 8 | to Azure App Services. The deployment will set the minimal Pricing Tiers for the Database(Basic) and for the App Service(Free). 9 | 10 | .EXAMPLE 11 | .\DeploySitefinityToAzureAppService.ps1 -$websiteRootDirectory "pathToSitefinityWebApp" -databaseName "localDbName" -sqlServer "localSqlServerInstance" -websiteName "yourWebsiteName" -$redisCacheConnectionString "primaryKey@redisCacheName.redis.cache.windows.net?ssl=true" 12 | 13 | #> 14 | param([Parameter(Mandatory=$True)]$websiteRootDirectory, 15 | [Parameter(Mandatory=$True)]$databaseName, 16 | [Parameter(Mandatory=$True)]$sqlServer, 17 | [Parameter(Mandatory=$True)]$websiteName, 18 | $redisCacheConnectionString, 19 | $websiteLocation = "West Europe", 20 | $deployDatabase = $true, 21 | $buildConfiguration = "Release", 22 | $launchWebsite = $true) 23 | 24 | . "$PSScriptRoot\Modules.ps1" 25 | 26 | $sitefinityProject = Join-Path $websiteRootDirectory "SitefinityWebApp.csproj" 27 | $tempDir = "$PSScriptRoot\temp" 28 | 29 | if(!(Test-Path $tempDir)) 30 | { 31 | New-Item $tempDir -ItemType Directory -Force 32 | } 33 | 34 | $bacpacDatabaseFile = "$tempDir\$databaseName.bacpac" 35 | 36 | $sqlConfig = $config.azure.sql | Where-Object { $_.location -eq $websiteLocation } 37 | 38 | Write-Host "Sql server is set to : $($sqlConfig.server)" 39 | $sqlConnectionUser = $sqlConfig.user 40 | $sqlConnectionServer = $sqlConfig.server 41 | $sqlConnectionUsername = "$sqlConnectionUser@$sqlConnectionServer" 42 | 43 | $systemConfigPath = Join-Path $websiteRootDirectory "App_Data\Sitefinity\Configuration\SystemConfig.config" 44 | $outputPath = Join-Path $websiteRootDirectory "pkg" 45 | $buildParameters = "OutputPath=$outputPath;IgnoreDeployManagedRuntimeVersion=true;FilesToIncludeForPublish=AllFilesInProjectFolder" 46 | 47 | # Configure powershell with publishsettings for your subscription 48 | Import-AzurePublishSettingsFile "$PSScriptRoot\$($config.files.subscriptionPublishSettings)" 49 | Set-AzureSubscription -SubscriptionName $config.azure.subscription 50 | Select-AzureSubscription -SubscriptionName $config.azure.subscription 51 | $subscription = Get-AzureSubscription $config.azure.subscription 52 | LogMessage "Azure Cloud Service deploy script started." 53 | LogMessage "Preparing deployment of $deploymentLabel for $($subscription.subscriptionname) with Subscription ID $($subscription.subscriptionid)." 54 | 55 | LogMessage 'Add-AzureWebsite: Start' 56 | $website = Get-AzureWebsite -Name $websiteName -ErrorAction SilentlyContinue 57 | 58 | if ($website) 59 | { 60 | LogMessage ('Add-AzureWebsite: An existing web site ' + 61 | $website.Name + ' was found') 62 | } 63 | else 64 | { 65 | if (Test-AzureName -Website -Name $websiteName) 66 | { 67 | LogMessage ('Website {0} already exists' -f $websiteName) 68 | } 69 | else 70 | { 71 | $website = New-AzureWebsite -Name $websiteName -Location $websiteLocation -Verbose 72 | } 73 | } 74 | 75 | $website | Out-String | Write-Host 76 | LogMessage 'Add-AzureWebsite: End' 77 | 78 | # Deploying Sitefinity database 79 | # First the database is packed from local isntance and then imported to the destination Azure server 80 | if($deployDatabase) 81 | { 82 | CreateDatabasePackage $sqlServer $databaseName $bacpacDatabaseFile 83 | DeployDatabasePackage $bacpacDatabaseFile $databaseName $sqlConfig.server $sqlConnectionUsername $sqlConfig.password 84 | } 85 | else 86 | { 87 | Write-Host "Deploy Database parameter set to: $deployDatabase. Skipping database deployment..." 88 | } 89 | 90 | # Update Sitefinity web.config and DataConfig.config with database settings. 91 | UpdateSitefinityWebConfig $websiteRootDirectory 92 | UpdateSitefinityDataConfig $websiteRootDirectory $sqlConfig.server $sqlConnectionUsername $sqlConfig.password $databaseName 93 | 94 | if([string]::IsNullOrEmpty($redisCacheConnectionString)) 95 | { 96 | LogMessage "Redis connection string not specified. Skipping redis configuration" 97 | } 98 | else 99 | { 100 | # Configure Redis Cache 101 | . "$PSScriptRoot\ConfigureRedisCache.ps1" $systemConfigPath $redisCacheConnectionString 102 | } 103 | 104 | # Build deployment package 105 | BuildSln $sitefinityProject "Package" $buildConfiguration $buildParameters 106 | 107 | $sfPackageLocationPath = Get-ChildItem $outputPath -Recurse -Include "SitefinityWebApp.zip" 108 | LogMessage "Publishing deployment package '$sfPackageLocationPath'..." 109 | Publish-AzureWebsiteProject -Name $websiteName -Package $sfPackageLocationPath 110 | 111 | LogMessage "Azure websites deployment has completed." 112 | if ($launchWebsite -eq "true") 113 | { 114 | LogMessage "Opening '$websiteName' site..." 115 | Show-AzureWebsite -Name $websiteName 116 | } 117 | -------------------------------------------------------------------------------- /Scripts/DeploySitefinityToAzureCloudService.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | 3 | .SYNOPSIS 4 | Script to deploy web app to Azure Cloud Services. 5 | 6 | .DESCRIPTION 7 | This Powershell Script applies the required modifications and deploys the database and the sitefinity web app 8 | to Azure Cloud Services. 9 | 10 | .EXAMPLE 11 | .\DeploySitefinityToAzureCloudService.ps1 -$websiteRootDirectory "C:\SitefinityWebApp_10_0_6400_0\testdeployment" -databaseName "testdeploymentdb" -sqlServer ".\SQLSERVER" -serviceName "testdeployment" -storageAccountName "testdeployment" 12 | 13 | #> 14 | param([Parameter(Mandatory=$True)]$websiteRootDirectory, 15 | [Parameter(Mandatory=$True)]$databaseName, 16 | [Parameter(Mandatory=$True)]$sqlServer, 17 | [Parameter(Mandatory=$True)]$serviceName, 18 | [Parameter(Mandatory=$True)]$storageAccountName, 19 | $vmSize = "Medium", 20 | $enableRemoteDesktopAccess = "true", 21 | $enableDiagnostics = "true", 22 | $enableSsl = "false", 23 | $enableRedisCache = "false", 24 | $deployDatabase = "true", 25 | $instanceCount = '1', 26 | $accountLocation = "West Europe" 27 | ) 28 | 29 | . "$PSScriptRoot\Modules.ps1" 30 | 31 | $serviceName = $serviceName.ToLower() 32 | $storageAccountName = $storageAccountName.ToLower() 33 | New-Item "$PSScriptRoot\temp" -ItemType "Directory" -Force 34 | $bacpacDatabaseFile = "$PSScriptRoot\temp\$databaseName.bacpac" 35 | $azurePackage = "$PSScriptRoot\temp\cloud_package.cspkg" 36 | $azureStartupTaskScripts = "$PSScriptRoot\..\CloudConfigs\StartupTasks" 37 | $startupTasksTargetDirectory = Join-Path $websiteRootDirectory "\bin\" 38 | $deploymentLabel = "ContinuousDeploy to $servicename" 39 | $slot = $config.azure.environment 40 | $serviceUrl = "$serviceName.cloudapp.net" 41 | $azureTempFilesDir = Join-Path $env:APPDATA "Windows Azure Powershell" 42 | 43 | $sqlConfig = $config.azure.sql | Where-Object { $_.location -eq $accountLocation } 44 | 45 | Write-Host "Sql server is set to : $($sqlConfig.server)" 46 | $sqlConnectionUser = $sqlConfig.user 47 | $sqlConnectionServer = $sqlConfig.server 48 | $sqlConnectionUsername = "$sqlConnectionUser@$sqlConnectionServer" 49 | 50 | try 51 | { 52 | $serviceDefinitionPath = Resolve-Path "$PSScriptRoot\$($config.files.serviceDefinition)" 53 | $cloudConfigPath = Resolve-Path "$PSScriptRoot\$($config.files.cloudConfig)" 54 | $subscriptionPublishSettingsPath = Resolve-Path "$PSScriptRoot\$($config.files.subscriptionPublishSettings)" 55 | $rolePropertiesPath = Resolve-Path "$PSScriptRoot\$($config.files.roleProperties)" 56 | $diagnosticsConfigPath = Resolve-Path "$PSScriptRoot\$($config.files.diagnosticsConfig)" 57 | 58 | Write-Host "Cleaning up Azure Subscriptions" 59 | Get-AzureSubscription | % { Remove-AzureSubscription $_.SubscriptionName -Force } 60 | 61 | Write-Host "Cleaning up temp Azure Powershell Files from $azureTempFilesDir" 62 | Get-ChildItem $azureTempFilesDir | % { Remove-Item $_.FullName } 63 | 64 | #configure powershell with publishsettings for your subscription 65 | Import-AzurePublishSettingsFile $subscriptionPublishSettingsPath 66 | 67 | $acc = CreateStorageAccount $storageAccountName $accountLocation 68 | Set-AzureSubscription -CurrentStorageAccountName $storageAccountName -SubscriptionName $config.azure.subscription 69 | Select-AzureSubscription -SubscriptionName $config.azure.subscription 70 | $subscription = Get-AzureSubscription $config.azure.subscription 71 | 72 | LogMessage "Azure Cloud Service deploy script started." 73 | LogMessage "Preparing deployment of $deploymentLabel for $($subscription.subscriptionname) with Subscription ID $($subscription.subscriptionid)." 74 | 75 | if($deployDatabase -eq "true") 76 | { 77 | CreateDatabasePackage $sqlServer $databaseName $bacpacDatabaseFile 78 | DeployDatabasePackage $bacpacDatabaseFile $databaseName $sqlConfig.server $sqlConnectionUsername $sqlConfig.password 79 | UpdateSQLAzureServerInfoData $databaseName $databaseName 80 | } 81 | else 82 | { 83 | Write-Host "Deploy Database parameter set to: $deployDatabase. Skipping database deployment..." 84 | } 85 | 86 | #Copies all startup task scripts to the website binaries directory. 87 | robocopy $azureStartupTaskScripts $startupTasksTargetDirectory 88 | 89 | UpdateInstancesCount $instanceCount 90 | 91 | if([int]$instanceCount -gt 1) 92 | { 93 | UpdateSitefinityWebConfig $websiteRootDirectory 94 | } 95 | if($enableDiagnostics -eq "true") 96 | { 97 | EnableAzureTraceListener $websiteRootDirectory 98 | } 99 | 100 | UpdateSitefinityDataConfig $websiteRootDirectory $sqlConfig.server $sqlConnectionUsername $sqlConfig.password $databaseName 101 | 102 | if($enableRedisCache -eq "true") 103 | { 104 | #The AzureResourceManager module requires Add-AzureAccount. A Publish Settings file is not sufficient. 105 | $secpassword = ConvertTo-SecureString $config.azure.azureAccountPassword -AsPlainText -Force 106 | $credentials = New-Object System.Management.Automation.PSCredential ($config.azure.azureAccount, $secpassword) 107 | Add-AzureAccount -Credential $credentials 108 | $redisCacheName = "$($serviceName)redis" 109 | $redisPrimaryKey = NewAzureRedisCache -CacheName $redisCacheName -ResourceGroupName $config.azure.resourceGroupName -Location $config.azure.accountsLocation 110 | $redisCacheConnectionString = "$redisPrimaryKey@$redisCacheName.redis.cache.windows.net?ssl=true" 111 | $systemConfigPath = "$websiteRootDirectory\App_Data\Sitefinity\Configuration\SystemConfig.config" 112 | LogMessage "RedisCache connection string: '$redisCacheConnectionString'" 113 | . "$PSScriptRoot\..\..\CommonScripts\PowerShell\Common\SitefinitySetup\ConfigureRedisCache.ps1" $systemConfigPath $redisCacheConnectionString 114 | } 115 | 116 | $serv = CreateCloudService $serviceName $accountLocation 117 | 118 | UpdateAzureSubscriptionData $acc.AccountName $serv.ServiceName $acc.AccountName $accountLocation 119 | UpdateServiceConfigurationCloudData $acc.AccountName $acc.AccessKey 120 | if($enableSsl -eq "true" -or $enableRemoteDesktopAccess -eq "true") 121 | { 122 | $certificatePath = Resolve-Path "$PSScriptRoot\$($config.certificate.path)" 123 | AddCertificateToService $serviceName $certificatePath $config.certificate.password 124 | AddCertificatesNode 125 | } else { 126 | DeleteCertificatesNode 127 | } 128 | ConfigureSsl -EnableSsl $enableSsl 129 | ConfigureRemoteDesktop -EnableRemoteDesktopAccess $enableRemoteDesktopAccess 130 | UpdateVMSize -ServiceDefinitionPath "$serviceDefinitionPath" -VMSize $vmSize 131 | 132 | CreatePackage $serviceDefinitionPath $azurePackage $websiteRootDirectory $config.azure.roleName $rolePropertiesPath 133 | Publish $serv.ServiceName $acc.AccountName $azurePackage $cloudConfigPath $config.azure.environment $deploymentLabel $config.azure.timeStampFormat $config.azure.alwaysDeleteExistingDeployments $config.azure.enableDeploymentUpgrade $config.azure.subscription $subscriptionPublishSettingsPath 134 | 135 | if($enableDiagnostics -eq "true") 136 | { 137 | LogMessage "Setting AzureServiceDiagnosticsExtension..." 138 | $storageAccountKey = (Get-AzureStorageKey -StorageAccountName $storageAccountName).Primary 139 | $storageContext = New-AzureStorageContext -StorageAccountName $storageAccountName -StorageAccountKey $storageAccountKey 140 | Set-AzureServiceDiagnosticsExtension -ServiceName $serviceName -DiagnosticsConfigurationPath $diagnosticsConfigPath -StorageContext $storageContext -Role $config.azure.roleName 141 | } 142 | } 143 | finally 144 | { 145 | LogMessage "Azure deployment has completed." 146 | } 147 | -------------------------------------------------------------------------------- /Scripts/ManageAzureRedisCache.ps1: -------------------------------------------------------------------------------- 1 | # Creates new redis cache in an already existing resource group 2 | # The AzureResourceManager module requires Add-AzureAccount 3 | function NewAzureRedisCache 4 | { 5 | Param( 6 | [Parameter(Mandatory=$true)] 7 | [String] 8 | $CacheName 9 | , 10 | [Parameter(Mandatory=$true)] 11 | [String] 12 | $ResourceGroupName 13 | , 14 | [Parameter(Mandatory=$true)] 15 | [String] 16 | $Location 17 | , 18 | [ValidateSet("Basic","Standard")] 19 | [String] 20 | $Sku="Basic" 21 | ) 22 | 23 | Switch-AzureMode AzureResourceManager 24 | Write-Host "Creating '$cacheName' azure redis cache..." 25 | $redisCache = New-AzureRedisCache -Location $location -Name $cacheName -ResourceGroupName $resourceGroupName -Size 250MB -Sku $Sku 26 | 27 | # Wait until the Cache is provisioned. 28 | for ($i = 0; $i -le 60; $i++) 29 | { 30 | Start-Sleep -s 30 31 | $cacheGet = Get-AzureRedisCache -ResourceGroupName $resourceGroupName -Name $cacheName 32 | Write-Host "'$cacheName' redis cache current state is '$($cacheGet[0].ProvisioningState)'..." 33 | if ([string]::Compare("succeeded", $cacheGet[0].ProvisioningState, $True) -eq 0) 34 | { 35 | break 36 | } 37 | If($i -eq 60) 38 | { 39 | exit 40 | } 41 | } 42 | 43 | $cacheKeys = Get-AzureRedisCacheKey -ResourceGroupName $resourceGroupName -Name $cacheName 44 | Switch-AzureMode AzureServiceManagement 45 | return $($cacheKeys.PrimaryKey) 46 | } 47 | 48 | function GetAzureRedisCacheKey 49 | { 50 | Param( 51 | [Parameter(Mandatory=$true)] 52 | [String] 53 | $CacheName 54 | , 55 | [Parameter(Mandatory=$true)] 56 | [String] 57 | $ResourceGroupName 58 | ) 59 | Switch-AzureMode AzureResourceManager 60 | 61 | $cacheKeys = Get-AzureRedisCacheKey -ResourceGroupName $ResourceGroupName -Name $CacheName 62 | 63 | Switch-AzureMode AzureServiceManagement 64 | return $($cacheKeys.PrimaryKey) 65 | } -------------------------------------------------------------------------------- /Scripts/ManageAzureResourceGroup.ps1: -------------------------------------------------------------------------------- 1 | # Creates new azure resource group with website template 2 | # The AzureResourceManager module requires Add-AzureAccount 3 | function NewAzureResourceGroup 4 | { 5 | Param( 6 | [Parameter(Mandatory=$true)] 7 | $ResourceGroupName, 8 | [Parameter(Mandatory=$true)] 9 | $AzureAccount, 10 | [Parameter(Mandatory=$true)] 11 | $AzureAccountPassword, 12 | $ResourceGroupLocation = "West Europe", 13 | $TemplateFile = "$PSScriptRoot\Templates\Default.json", 14 | $TemplateParameterFile = "$PSScriptRoot\Templates\Default.params.json" 15 | ) 16 | 17 | Switch-AzureMode AzureResourceManager 18 | 19 | #NOTE: 20 | #The AzureResourceManager module requires Add-AzureAccount. A Publish Settings file is not sufficient. 21 | $secpassword = ConvertTo-SecureString $AzureAccountPassword -AsPlainText -Force 22 | $credentials = New-Object System.Management.Automation.PSCredential ($AzureAccount, $secpassword) 23 | Add-AzureAccount -Credential $credentials 24 | 25 | #Creating new AzureResourceGroup 26 | Write-Host "Creating '$ResourceGroupName' Azure Resource group..." 27 | 28 | 29 | New-AzureResourceGroup -Name $ResourceGroupName ` 30 | -Location $ResourceGroupLocation ` 31 | -TemplateFile $TemplateFile ` 32 | -TemplateParameterFile $TemplateParameterFile ` 33 | -Force -Verbose 34 | 35 | Write-Host "'$ResourceGroupName' Azure Resource group has been successfully created." 36 | 37 | Switch-AzureMode AzureServiceManagement 38 | } -------------------------------------------------------------------------------- /Scripts/ManageAzureServices.ps1: -------------------------------------------------------------------------------- 1 | #Creates Cloud Service 2 | function CreateCloudService($Name, $Location) 3 | { 4 | $service = Get-AzureService | where { $_.ServiceName -eq $Name } 5 | if(!$service) 6 | { 7 | LogMessage ("[Start] creating cloud service {0} in location {1}" -f $Name, $Location) 8 | New-AzureService -ServiceName $Name -Location $Location 9 | LogMessage ("[Finish] creating cloud service {0} in location {1}" -f $Name, $Location) 10 | } 11 | else 12 | { 13 | LogMessage ("Cloud service '{0}' exists. New cloud service hasn't been created" -f $Name) 14 | } 15 | 16 | Return @{ServiceName = $Name} 17 | } 18 | 19 | #Removes Cloud Service 20 | function RemoveCloudService($Name) 21 | { 22 | LogMessage ("[Start] removing cloud service {0}" -f $Name) 23 | $service = Get-AzureService | where { $_.ServiceName -eq $Name } 24 | if($service) 25 | { 26 | LogMessage ("[InProgress] cloud service found {0}" -f $Name) 27 | $stat = Remove-AzureService $service.ServiceName -Force 28 | if($stat.OperationStatus -eq "Succeeded") 29 | { 30 | LogMessage ("[Finish] removing cloud service {0}" -f $Name) 31 | } 32 | else 33 | { 34 | LogMessage ("Removing cloud service '{0}' failed" -f $Name) 35 | } 36 | } 37 | else 38 | { 39 | LogMessage ("Cloud service '{0}' not found" -f $Name) 40 | } 41 | } -------------------------------------------------------------------------------- /Scripts/ManageAzureStorage.ps1: -------------------------------------------------------------------------------- 1 | #Creates Storage Account 2 | function CreateStorageAccount($Name, $Location) 3 | { 4 | $sAccount = Get-AzureStorageAccount | where { $_.StorageAccountName -eq $Name } 5 | if(!$sAccount) 6 | { 7 | # Create a new storage account 8 | LogMessage ("[Start] creating storage account {0} in location {1}" -f $Name, $Location) 9 | New-AzureStorageAccount -StorageAccountName $Name -Location $Location -Verbose 10 | LogMessage ("[Finish] creating storage account {0} in location {1}" -f $Name, $Location) 11 | } 12 | else 13 | { 14 | LogMessage ("Storage account '{0}' exists. New azure storage account hasn't been created" -f $Name) 15 | } 16 | # Get the access key of the storage account 17 | $key = Get-AzureStorageKey -StorageAccountName $Name 18 | # Generate the connection string of the storage account 19 | $connectionString = "BlobEndpoint=http://{0}.blob.core.windows.net/;QueueEndpoint=http://{0}.queue.core.windows.net/;TableEndpoint=http://{0}.table.core.windows.net/;AccountName={0};AccountKey={1}" -f $Name, $key.Primary 20 | 21 | #this returns the storage key. example $storageData.AccessKey 22 | Return @{AccountName = $Name; AccessKey = $key.Primary; ConnectionString = $connectionString} 23 | } 24 | 25 | #Removes Storage account 26 | function RemoveStorageAccount($Name) 27 | { 28 | LogMessage ("[Start] removing storage account {0}" -f $Name) 29 | $sAccount = Get-AzureStorageAccount | where { $_.StorageAccountName -eq $Name } 30 | if($sAccount) 31 | { 32 | LogMessage ("[InProgress] storage account found {0}" -f $Name) 33 | $stat = Remove-AzureStorageAccount -StorageAccountName $Name 34 | if($stat.OperationStatus -eq "Succeeded") 35 | { 36 | LogMessage ("[Finish] removing storage account {0}" -f $Name) 37 | } 38 | else 39 | { 40 | LogMessage ("Removing storage account '{0}' failed" -f $Name) 41 | } 42 | } 43 | else 44 | { 45 | LogMessage ("Storage account '{0}' not found" -f $Name) 46 | } 47 | } -------------------------------------------------------------------------------- /Scripts/Modules.ps1: -------------------------------------------------------------------------------- 1 | #$ErrorActionPreference = "Stop" 2 | 3 | . "$PSScriptRoot\Common.ps1" 4 | . "$PSScriptRoot\CreateAzurePackage.ps1" 5 | . "$PSScriptRoot\DatabaseAzure.ps1" 6 | . "$PSScriptRoot\ManageAzureServices.ps1" 7 | . "$PSScriptRoot\ManageAzureStorage.ps1" 8 | . "$PSScriptRoot\ManageAzureResourceGroup.ps1" 9 | . "$PSScriptRoot\ManageAzureRedisCache.ps1" 10 | . "$PSScriptRoot\PublishCloudService.ps1" 11 | . "$PSScriptRoot\UpdateAzureCloudConfigs.ps1" 12 | . "$PSScriptRoot\UpdateSitefinityConfigs.ps1" 13 | . "$PSScriptRoot\CertificatesManagement.ps1" 14 | 15 | #configure powershell with Azure 1.7 modules 16 | Import-Module "C:\Program Files (x86)\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Azure.psd1" 17 | $config = Get-Settings "$PSScriptRoot\Configuration\config.json" -------------------------------------------------------------------------------- /Scripts/PublishCloudService.ps1: -------------------------------------------------------------------------------- 1 | function Publish($serviceName, $storageAccountName, $packageLocation, $cloudConfigLocation, $environment, $deploymentLabel, $timeStampFormat, $alwaysDeleteExistingDeployments, $enableDeploymentUpgrade, $selectedsubscription) 2 | { 3 | LogMessage ("[Publish] is called with the folling parameters 'Serivce name: {0}', 'Storage account name: {1}', 'Package location: {2}','Cloud config location: {3}', 'Environment: {4}' " -f $serviceName, $storageAccountName, $packageLocation, $cloudConfigLocation, $environment) 4 | 5 | $deployment = Get-AzureDeployment -ServiceName $serviceName -Slot $slot -ErrorVariable a -ErrorAction silentlycontinue 6 | if ($a[0] -ne $null) 7 | { 8 | LogMessage "No deployment is detected. Creating a new deployment. " 9 | } 10 | #check for existing deployment and then either upgrade, delete + deploy, or cancel according to $alwaysDeleteExistingDeployments and $enableDeploymentUpgrade boolean variables 11 | if ($deployment.Name -ne $null) 12 | { 13 | switch ($alwaysDeleteExistingDeployments) 14 | { 15 | 1 16 | { 17 | switch ($enableDeploymentUpgrade) 18 | { 19 | 1 #Update deployment inplace (usually faster, cheaper, won't destroy VIP) 20 | { 21 | LogMessage "Deployment exists in $servicename. Upgrading deployment..." 22 | UpgradeDeployment 23 | } 24 | 0 #Delete then create new deployment 25 | { 26 | LogMessage "Deployment exists in $servicename. Deleting deployment..." 27 | DeleteDeployment 28 | CreateNewDeployment 29 | 30 | } 31 | } # switch ($enableDeploymentUpgrade) 32 | } 33 | 0 34 | { 35 | LogMessage "ERROR: Deployment exists in $servicename. Script execution cancelled." 36 | exit 37 | } 38 | } #switch ($alwaysDeleteExistingDeployments) 39 | } else { 40 | CreateNewDeployment 41 | } 42 | 43 | $deployment = Get-AzureDeployment -slot $slot -serviceName $servicename 44 | $deploymentUrl = $deployment.Url 45 | 46 | LogMessage "Created Cloud Service with URL $deploymentUrl." 47 | LogMessage "Azure Cloud Service deploy script finished." 48 | } 49 | 50 | function CreateNewDeployment() 51 | { 52 | write-progress -id 3 -activity "Creating New Deployment" -Status "In progress" 53 | LogMessage "Creating New Deployment: In progress" 54 | 55 | $opstat = New-AzureDeployment -Slot $slot -Package $packageLocation -Configuration $cloudConfigLocation -label $deploymentLabel -ServiceName $serviceName 56 | 57 | $completeDeployment = Get-AzureDeployment -ServiceName $serviceName -Slot $slot 58 | $completeDeploymentID = $completeDeployment.deploymentid 59 | 60 | write-progress -id 3 -activity "Creating New Deployment" -completed -Status "Complete" 61 | LogMessage "Creating New Deployment: Complete, Deployment ID: $completeDeploymentID" 62 | 63 | StartInstances 64 | } 65 | 66 | function UpgradeDeployment() 67 | { 68 | write-progress -id 3 -activity "Upgrading Deployment" -Status "In progress" 69 | LogMessage "Upgrading Deployment: In progress" 70 | 71 | LogMessage "Slot: '$slot', Label: '$deploymentLabel', ServiceName: '$serviceName'" 72 | # perform Update-Deployment 73 | $setdeployment = Set-AzureDeployment -Upgrade -Slot $slot -Package $packageLocation -Configuration $cloudConfigLocation -label $deploymentLabel -ServiceName $serviceName -Force 74 | 75 | $completeDeployment = Get-AzureDeployment -ServiceName $serviceName -Slot $slot 76 | $completeDeploymentID = $completeDeployment.deploymentid 77 | 78 | write-progress -id 3 -activity "Upgrading Deployment" -completed -Status "Complete" 79 | LogMessage "Upgrading Deployment: Complete, Deployment ID: $completeDeploymentID" 80 | } 81 | 82 | function DeleteDeployment() 83 | { 84 | 85 | write-progress -id 2 -activity "Deleting Deployment" -Status "In progress" 86 | LogMessage "Deleting Deployment: In progress" 87 | 88 | #WARNING - always deletes with force 89 | $removeDeployment = Remove-AzureDeployment -Slot $slot -ServiceName $serviceName -Force 90 | 91 | write-progress -id 2 -activity "Deleting Deployment: Complete" -completed -Status $removeDeployment 92 | LogMessage "Deleting Deployment: Complete" 93 | } 94 | 95 | function StartInstances() 96 | { 97 | write-progress -id 4 -activity "Starting Instances" -status "In progress" 98 | LogMessage "Starting Instances: In progress" 99 | 100 | $deployment = Get-AzureDeployment -ServiceName $serviceName -Slot $slot 101 | $runstatus = $deployment.Status 102 | 103 | if ($runstatus -ne 'Running') 104 | { 105 | $run = Set-AzureDeployment -Slot $slot -ServiceName $serviceName -Status Running 106 | } 107 | $deployment = Get-AzureDeployment -ServiceName $serviceName -Slot $slot 108 | $oldStatusStr = @("") * $deployment.RoleInstanceList.Count 109 | 110 | while (-not(AllInstancesRunning($deployment.RoleInstanceList))) 111 | { 112 | $i = 1 113 | foreach ($roleInstance in $deployment.RoleInstanceList) 114 | { 115 | $instanceName = $roleInstance.InstanceName 116 | $instanceStatus = $roleInstance.InstanceStatus 117 | 118 | if ($oldStatusStr[$i - 1] -ne $roleInstance.InstanceStatus) 119 | { 120 | $oldStatusStr[$i - 1] = $roleInstance.InstanceStatus 121 | LogMessage "Starting Instance '$instanceName': $instanceStatus" 122 | } 123 | 124 | write-progress -id (4 + $i) -activity "Starting Instance '$instanceName'" -status "$instanceStatus" 125 | $i = $i + 1 126 | } 127 | 128 | sleep -Seconds 1 129 | 130 | $deployment = Get-AzureDeployment -ServiceName $serviceName -Slot $slot 131 | } 132 | 133 | $i = 1 134 | foreach ($roleInstance in $deployment.RoleInstanceList) 135 | { 136 | $instanceName = $roleInstance.InstanceName 137 | $instanceStatus = $roleInstance.InstanceStatus 138 | 139 | if ($oldStatusStr[$i - 1] -ne $roleInstance.InstanceStatus) 140 | { 141 | $oldStatusStr[$i - 1] = $roleInstance.InstanceStatus 142 | LogMessage "Starting Instance '$instanceName': $instanceStatus" 143 | } 144 | 145 | $i = $i + 1 146 | } 147 | 148 | $deployment = Get-AzureDeployment -ServiceName $serviceName -Slot $slot 149 | $opstat = $deployment.Status 150 | 151 | write-progress -id 4 -activity "Starting Instances" -completed -status $opstat 152 | LogMessage "Starting Instances: $opstat" 153 | } 154 | 155 | function AllInstancesRunning($roleInstanceList) 156 | { 157 | foreach ($roleInstance in $roleInstanceList) 158 | { 159 | if ($roleInstance.InstanceStatus -ne "ReadyRole") 160 | { 161 | return $false 162 | } 163 | } 164 | 165 | return $true 166 | } -------------------------------------------------------------------------------- /Scripts/Templates/Default.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "siteName": { 6 | "type": "string" 7 | }, 8 | "hostingPlanName": { 9 | "type": "string" 10 | }, 11 | "siteLocation": { 12 | "type": "string" 13 | }, 14 | "sku": { 15 | "type": "string", 16 | "allowedValues": [ 17 | "Free", 18 | "Shared", 19 | "Basic", 20 | "Standard", 21 | "Premium" 22 | ], 23 | "defaultValue": "Free" 24 | }, 25 | "instancesCount": { 26 | "type": "int", 27 | "defaultValue": 1 28 | }, 29 | "workerSize": { 30 | "type": "string", 31 | "allowedValues": [ 32 | "0", 33 | "1", 34 | "2" 35 | ], 36 | "defaultValue": "0" 37 | }, 38 | "redisCacheName": { 39 | "type": "string" 40 | }, 41 | "redisCacheLocation": { 42 | "type": "string" 43 | }, 44 | "redisCacheSKUName": { 45 | "type": "string", 46 | "allowedValues": [ 47 | "Basic", 48 | "Standard" 49 | ], 50 | "defaultValue": "Standard" 51 | }, 52 | "redisCacheSKUFamily": { 53 | "type": "string", 54 | "allowedValues": [ 55 | "C" 56 | ], 57 | "defaultValue": "C" 58 | }, 59 | "redisCacheSKUCapacity": { 60 | "type": "int", 61 | "allowedValues": [ 62 | 0, 63 | 1, 64 | 2, 65 | 3, 66 | 4, 67 | 5, 68 | 6 69 | ], 70 | "defaultValue": 0 71 | }, 72 | "redisCacheVersion": { 73 | "type": "string", 74 | "allowedValues": [ 75 | "2.8" 76 | ], 77 | "defaultValue": "2.8" 78 | }, 79 | "sqlServerName": { 80 | "type": "string" 81 | }, 82 | "sqlServerLocation": { 83 | "type": "string" 84 | }, 85 | "sqlServerAdminLogin": { 86 | "type": "string" 87 | }, 88 | "sqlServerAdminLoginPassword": { 89 | "type": "securestring" 90 | }, 91 | "sqlDatabaseName": { 92 | "type": "string" 93 | }, 94 | "sqlDatabaseCollation": { 95 | "type": "string", 96 | "defaultValue": "SQL_Latin1_General_CP1_CI_AS" 97 | }, 98 | "sqlDatabaseEdition": { 99 | "type": "string", 100 | "defaultValue": "Web", 101 | "allowedValues": [ 102 | "Basic", 103 | "Business", 104 | "Premium", 105 | "Standard", 106 | "Web" 107 | ] 108 | }, 109 | "azureSearchName": { 110 | "type": "string" 111 | }, 112 | "azureSearchLocation": { 113 | "type": "string" 114 | }, 115 | "azureSearchSku": { 116 | "type": "string", 117 | "allowedValues": [ 118 | "free", 119 | "standard", 120 | "standard2" 121 | ], 122 | "defaultValue": "standard" 123 | }, 124 | "azureSearchReplicaCount": { 125 | "type": "int", 126 | "allowedValues": [ 127 | 1, 128 | 2, 129 | 3, 130 | 4, 131 | 5, 132 | 6 133 | ], 134 | "defaultValue": 1 135 | }, 136 | "azureSearchPartitionCount": { 137 | "type": "int", 138 | "allowedValues": [ 139 | 1, 140 | 2, 141 | 3, 142 | 4, 143 | 6, 144 | 12 145 | ], 146 | "defaultValue": 1 147 | } 148 | }, 149 | "resources": [ 150 | { 151 | "apiVersion": "2014-06-01", 152 | "name": "[parameters('hostingPlanName')]", 153 | "type": "Microsoft.Web/serverfarms", 154 | "location": "[parameters('siteLocation')]", 155 | "tags": { 156 | "displayName": "HostingPlan" 157 | }, 158 | "properties": { 159 | "name": "[parameters('hostingPlanName')]", 160 | "sku": "[parameters('sku')]", 161 | "workerSize": "[parameters('workerSize')]", 162 | "numberOfWorkers": "[parameters('instancesCount')]" 163 | } 164 | }, 165 | { 166 | "apiVersion": "2014-06-01", 167 | "name": "[parameters('siteName')]", 168 | "type": "Microsoft.Web/sites", 169 | "location": "[parameters('siteLocation')]", 170 | "tags": { 171 | "[concat('hidden-related:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource", 172 | "displayName": "Website" 173 | 174 | }, 175 | "dependsOn": [ 176 | "[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]" 177 | ], 178 | "properties": { 179 | "name": "[parameters('siteName')]", 180 | "serverFarm": "[parameters('hostingPlanName')]" 181 | } 182 | }, 183 | { 184 | "apiVersion": "2014-04-01", 185 | "name": "[concat(parameters('hostingPlanName'), '-', resourceGroup().name)]", 186 | "type": "Microsoft.Insights/autoscalesettings", 187 | "location": "[parameters('siteLocation')]", 188 | "tags": { 189 | "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource", 190 | "displayName": "AutoScaleSettings" 191 | }, 192 | "dependsOn": [ 193 | "[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]" 194 | ], 195 | "properties": { 196 | "profiles": [ 197 | { 198 | "name": "Default", 199 | "capacity": { 200 | "minimum": 1, 201 | "maximum": 2, 202 | "default": 1 203 | }, 204 | "rules": [ 205 | { 206 | "metricTrigger": { 207 | "metricName": "CpuPercentage", 208 | "metricResourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]", 209 | "timeGrain": "PT1M", 210 | "statistic": "Average", 211 | "timeWindow": "PT10M", 212 | "timeAggregation": "Average", 213 | "operator": "GreaterThan", 214 | "threshold": 80.0 215 | }, 216 | "scaleAction": { 217 | "direction": "Increase", 218 | "type": "ChangeCount", 219 | "value": 1, 220 | "cooldown": "PT10M" 221 | } 222 | }, 223 | { 224 | "metricTrigger": { 225 | "metricName": "CpuPercentage", 226 | "metricResourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]", 227 | "timeGrain": "PT1M", 228 | "statistic": "Average", 229 | "timeWindow": "PT1H", 230 | "timeAggregation": "Average", 231 | "operator": "LessThan", 232 | "threshold": 60.0 233 | }, 234 | "scaleAction": { 235 | "direction": "Decrease", 236 | "type": "ChangeCount", 237 | "value": 1, 238 | "cooldown": "PT1H" 239 | } 240 | } 241 | ] 242 | } 243 | ], 244 | "enabled": false, 245 | "name": "[concat(parameters('hostingPlanName'), '-', resourceGroup().name)]", 246 | "targetResourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]" 247 | } 248 | }, 249 | { 250 | "apiVersion": "2014-04-01", 251 | "name": "[concat('ServerErrors ', parameters('siteName'))]", 252 | "type": "Microsoft.Insights/alertrules", 253 | "location": "[parameters('siteLocation')]", 254 | "dependsOn": [ 255 | "[concat('Microsoft.Web/sites/', parameters('siteName'))]" 256 | ], 257 | "tags": { 258 | "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', parameters('siteName'))]": "Resource", 259 | "displayName": "ServerErrorsAlertRule" 260 | }, 261 | "properties": { 262 | "name": "[concat('ServerErrors ', parameters('siteName'))]", 263 | "description": "[concat(parameters('siteName'), ' has some server errors, status code 5xx.')]", 264 | "isEnabled": false, 265 | "condition": { 266 | "odata.type": "Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition", 267 | "dataSource": { 268 | "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource", 269 | "resourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/sites/', parameters('siteName'))]", 270 | "metricName": "Http5xx" 271 | }, 272 | "operator": "GreaterThan", 273 | "threshold": 0.0, 274 | "windowSize": "PT5M" 275 | }, 276 | "action": { 277 | "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction", 278 | "sendToServiceOwners": true, 279 | "customEmails": [] 280 | } 281 | } 282 | }, 283 | { 284 | "apiVersion": "2014-04-01", 285 | "name": "[concat('ForbiddenRequests ', parameters('siteName'))]", 286 | "type": "Microsoft.Insights/alertrules", 287 | "location": "[parameters('siteLocation')]", 288 | "dependsOn": [ 289 | "[concat('Microsoft.Web/sites/', parameters('siteName'))]" 290 | ], 291 | "tags": { 292 | "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', parameters('siteName'))]": "Resource", 293 | "displayName": "ForbiddenRequestsAlertRule" 294 | }, 295 | "properties": { 296 | "name": "[concat('ForbiddenRequests ', parameters('siteName'))]", 297 | "description": "[concat(parameters('siteName'), ' has some requests that are forbidden, status code 403.')]", 298 | "isEnabled": false, 299 | "condition": { 300 | "odata.type": "Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition", 301 | "dataSource": { 302 | "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource", 303 | "resourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/sites/', parameters('siteName'))]", 304 | "metricName": "Http403" 305 | }, 306 | "operator": "GreaterThan", 307 | "threshold": 0, 308 | "windowSize": "PT5M" 309 | }, 310 | "action": { 311 | "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction", 312 | "sendToServiceOwners": true, 313 | "customEmails": [] 314 | } 315 | } 316 | }, 317 | { 318 | "apiVersion": "2014-04-01", 319 | "name": "[concat('CPUHigh ', parameters('hostingPlanName'))]", 320 | "type": "Microsoft.Insights/alertrules", 321 | "location": "[parameters('siteLocation')]", 322 | "dependsOn": [ 323 | "[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]" 324 | ], 325 | "tags": { 326 | "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource", 327 | "displayName": "CPUHighAlertRule" 328 | }, 329 | "properties": { 330 | "name": "[concat('CPUHigh ', parameters('hostingPlanName'))]", 331 | "description": "[concat('The average CPU is high across all the instances of ', parameters('hostingPlanName'))]", 332 | "isEnabled": false, 333 | "condition": { 334 | "odata.type": "Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition", 335 | "dataSource": { 336 | "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource", 337 | "resourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]", 338 | "metricName": "CpuPercentage" 339 | }, 340 | "operator": "GreaterThan", 341 | "threshold": 90, 342 | "windowSize": "PT15M" 343 | }, 344 | "action": { 345 | "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction", 346 | "sendToServiceOwners": true, 347 | "customEmails": [] 348 | } 349 | } 350 | }, 351 | { 352 | "apiVersion": "2014-04-01", 353 | "name": "[concat('LongHttpQueue ', parameters('hostingPlanName'))]", 354 | "type": "Microsoft.Insights/alertrules", 355 | "location": "[parameters('siteLocation')]", 356 | "dependsOn": [ 357 | "[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]" 358 | ], 359 | "tags": { 360 | "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource", 361 | "displayName": "LongHttpQueueAlertRule" 362 | }, 363 | "properties": { 364 | "name": "[concat('LongHttpQueue ', parameters('hostingPlanName'))]", 365 | "description": "[concat('The HTTP queue for the instances of ', parameters('hostingPlanName'), ' has a large number of pending requests.')]", 366 | "isEnabled": false, 367 | "condition": { 368 | "odata.type": "Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition", 369 | "dataSource": { 370 | "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource", 371 | "resourceUri": "[concat(resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]", 372 | "metricName": "HttpQueueLength" 373 | }, 374 | "operator": "GreaterThan", 375 | "threshold": 100.0, 376 | "windowSize": "PT5M" 377 | }, 378 | "action": { 379 | "odata.type": "Microsoft.Azure.Management.Insights.Models.RuleEmailAction", 380 | "sendToServiceOwners": true, 381 | "customEmails": [] 382 | } 383 | } 384 | }, 385 | { 386 | "apiVersion": "2014-04-01", 387 | "name": "[parameters('siteName')]", 388 | "type": "Microsoft.Insights/components", 389 | "location": "[parameters('siteLocation')]", 390 | "dependsOn": [ 391 | "[concat('Microsoft.Web/sites/', parameters('siteName'))]" 392 | ], 393 | "tags": { 394 | "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', parameters('siteName'))]": "Resource", 395 | "displayName": "AppInsightsComponent" 396 | }, 397 | "properties": { 398 | "applicationId": "[parameters('siteName')]" 399 | } 400 | }, 401 | { 402 | "name": "[parameters('redisCacheName')]", 403 | "type": "Microsoft.Cache/Redis", 404 | "location": "[parameters('redisCacheLocation')]", 405 | "apiVersion": "2014-04-01-preview", 406 | "dependsOn": [ ], 407 | "tags": { 408 | "displayName": "RedisCache" 409 | }, 410 | "properties": { 411 | "sku": { 412 | "name": "[parameters('redisCacheSKUName')]", 413 | "family": "[parameters('redisCacheSKUFamily')]", 414 | "capacity": "[parameters('redisCacheSKUCapacity')]" 415 | }, 416 | "redisVersion": "[parameters('redisCacheVersion')]" 417 | } 418 | }, 419 | { 420 | "name": "[parameters('sqlServerName')]", 421 | "type": "Microsoft.Sql/servers", 422 | "location": "[parameters('sqlServerLocation')]", 423 | "apiVersion": "2014-04-01-preview", 424 | "dependsOn": [ ], 425 | "tags": { 426 | "displayName": "sqlServer" 427 | }, 428 | "properties": { 429 | "administratorLogin": "[parameters('sqlServerAdminLogin')]", 430 | "administratorLoginPassword": "[parameters('sqlServerAdminLoginPassword')]" 431 | }, 432 | "resources": [ 433 | { 434 | "name": "AllowAllWindowsAzureIps", 435 | "type": "firewallrules", 436 | "location": "[parameters('sqlServerLocation')]", 437 | "apiVersion": "2014-04-01-preview", 438 | "dependsOn": [ 439 | "[concat('Microsoft.Sql/servers/', parameters('sqlServerName'))]" 440 | ], 441 | "properties": { 442 | "startIpAddress": "0.0.0.0", 443 | "endIpAddress": "255.255.255.255" 444 | } 445 | }, 446 | { 447 | "name": "[parameters('sqlDatabaseName')]", 448 | "type": "databases", 449 | "location": "[parameters('sqlServerLocation')]", 450 | "apiVersion": "2014-04-01-preview", 451 | "dependsOn": [ 452 | "[parameters('sqlServerName')]" 453 | ], 454 | "tags": { 455 | "displayName": "sqlDatabase" 456 | }, 457 | "properties": { 458 | "collation": "[parameters('sqlDatabaseCollation')]", 459 | "edition": "[parameters('sqlDatabaseEdition')]", 460 | "maxSizeBytes": "1073741824" 461 | } 462 | } 463 | ] 464 | }, 465 | { 466 | "apiVersion": "2015-02-28", 467 | "name": "[parameters('azureSearchName')]", 468 | "type": "Microsoft.Search/searchServices", 469 | "location": "[parameters('azureSearchLocation')]", 470 | "properties": { 471 | "sku": { 472 | "name": "[parameters('azureSearchSku')]" 473 | }, 474 | "replicaCount": "[parameters('azureSearchReplicaCount')]", 475 | "partitionCount": "[parameters('azureSearchPartitionCount')]" 476 | } 477 | } 478 | ] 479 | } -------------------------------------------------------------------------------- /Scripts/Templates/Default.params.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "siteName": { 6 | "value": "" 7 | }, 8 | "hostingPlanName": { 9 | "value": "" 10 | }, 11 | "siteLocation": { 12 | "value": "West Europe" 13 | }, 14 | "sku": { 15 | "value": "Basic" 16 | }, 17 | "workerSize": { 18 | "value": "0" 19 | }, 20 | "instancesCount": { 21 | "value": 2 22 | }, 23 | "sqlServerName": { 24 | "value": "" 25 | }, 26 | "sqlDatabaseName": { 27 | "value": "" 28 | }, 29 | "sqlServerLocation": { 30 | "value": "West Europe" 31 | }, 32 | "sqlServerAdminLogin": { 33 | "value": "" 34 | }, 35 | "sqlServerAdminLoginPassword": { 36 | "value": "" 37 | }, 38 | "redisCacheName": { 39 | "value": "" 40 | }, 41 | "redisCacheLocation": { 42 | "value": "West Europe" 43 | }, 44 | "redisCacheSKUName": { 45 | "value": "Standard" 46 | }, 47 | "redisCacheSKUCapacity": { 48 | "value": 0 49 | }, 50 | "azureSearchName": { 51 | "value": "" 52 | }, 53 | "azureSearchLocation": { 54 | "value": "West Europe" 55 | }, 56 | "azureSearchSku": { 57 | "value": "standard" 58 | }, 59 | "azureSearchReplicaCount": { 60 | "value": 1 61 | }, 62 | "azureSearchPartitionCount": { 63 | "value": 1 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Scripts/UpdateAzureCloudConfigs.ps1: -------------------------------------------------------------------------------- 1 | function UpdateAzureSubscriptionData($currentStorageAccount, $cloudServiceName, $storageServiceName, $location) 2 | { 3 | $azureSubscriptionDataConfig = Resolve-Path "$PSScriptRoot\$($config.files.azureSubscriptionDataConfig)" 4 | Write-Host "Azure subscription data config is: $azureSubscriptionDataConfig" 5 | 6 | Set-ItemProperty $azureSubscriptionDataConfig -name IsReadOnly -value $false 7 | [Xml]$sdXml = Get-Content $azureSubscriptionDataConfig 8 | 9 | #update Current storage account 10 | $sdXml.AzureSubscriptionData.CurrentStorageAccount = $currentStorageAccount 11 | 12 | #update could service data 13 | Foreach ($cloudService in $sdXml.AzureSubscriptionData.CloudService) 14 | { 15 | $cloudService.Name = $cloudServiceName 16 | $cloudService.Location = $location 17 | } 18 | 19 | #update storage service data 20 | Foreach ($storageService in $sdXml.AzureSubscriptionData.StorageService) 21 | { 22 | $storageService.Name = $storageServiceName 23 | } 24 | 25 | $sdXml.Save($azureSubscriptionDataConfig) 26 | } 27 | 28 | function UpdateServiceConfigurationCloudData($AccountName, $AccountKey) 29 | { 30 | $cloudConfig = Resolve-Path "$PSScriptRoot\$($config.files.cloudConfig)" 31 | Write-Host "Cloud config path is: $cloudConfig" 32 | 33 | Set-ItemProperty $cloudConfig -name IsReadOnly -value $false 34 | [Xml]$cscfgXml = Get-Content $cloudConfig 35 | $connectionStringValue = [string]::Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}", $AccountName, $AccountKey) 36 | 37 | $settingNode = $cscfgXml.ServiceConfiguration.Role.ConfigurationSettings.Setting | where {$_.name -like 'Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString'} 38 | $settingNode.Value = $connectionStringValue 39 | 40 | $cscfgXml.Save($cloudConfig) 41 | } 42 | 43 | function UpdateInstancesCount($count) 44 | { 45 | $cloudConfig = Resolve-Path "$PSScriptRoot\$($config.files.cloudConfig)" 46 | Write-Host "Cloud config path is: $cloudConfig" 47 | 48 | Set-ItemProperty $cloudConfig -name IsReadOnly -value $false 49 | [Xml]$cscfgXml = Get-Content $cloudConfig 50 | $instancesNode = $cscfgXml.ServiceConfiguration.Role.Instances; 51 | $instancesNode.count = $count 52 | $cscfgXml.Save($cloudConfig) 53 | } 54 | 55 | # Example: UpdateVMSize -ServiceDefinitionPath "C:\Temp\Tools\AzureDeployment\CloudConfigs\ServiceDefinition.csdef" -VMSize "big" 56 | function UpdateVMSize 57 | { 58 | Param( 59 | [string]$ServiceDefinitionPath, 60 | [string]$VMSize 61 | ) 62 | 63 | Set-ItemProperty $ServiceDefinitionPath -name IsReadOnly -value $false 64 | if ($ServiceDefinitionPath -eq $null) 65 | { 66 | Throw "Path to xml file is not provided." 67 | } 68 | 69 | if ($VMSize -eq $null) 70 | { 71 | Throw "VMSize is not provided as a parameter." 72 | } 73 | 74 | [Xml]$XMLfile = Get-Content $ServiceDefinitionPath 75 | $XMLFile.ServiceDefinition.WebRole.vmsize = $VMSize 76 | $XMLFile.save($ServiceDefinitionPath) 77 | } 78 | 79 | # Example: UpdateVMModules -ServiceDefinitionPath "C:\Temp\Tools\AzureDeployment\CloudConfigs\ServiceDefinition.csdef" -Modules @("RemoteAccess", "RemoteForwarder") 80 | function UpdateVMModules 81 | { 82 | Param( 83 | [string]$ServiceDefinitionPath, 84 | [array]$Modules 85 | ) 86 | 87 | if ($ServiceDefinitionPath -eq $null) 88 | { 89 | Throw "Path to xml file is not provided." 90 | } 91 | 92 | if ($Modules -eq $null) 93 | { 94 | Throw "No modules were provided." 95 | } 96 | 97 | Set-ItemProperty $ServiceDefinitionPath -name IsReadOnly -value $false 98 | [Xml]$XMLfile = Get-Content $ServiceDefinitionPath 99 | $xdns = $XMLfile.DocumentElement.NamespaceURI 100 | Foreach ($module in $Modules) 101 | { 102 | if(($XMLFile.ServiceDefinition.WebRole.Imports.Import | where {$_.moduleName -like $module}) -eq $null){ 103 | $newImport = $XMLfile.CreateElement("Import", $xdns) 104 | $XMLFile.ServiceDefinition.WebRole.Imports.AppendChild($newImport) 105 | $newImport.SetAttribute("moduleName", $module) 106 | } 107 | } 108 | 109 | $XMLFile.save($ServiceDefinitionPath) 110 | } 111 | 112 | function DeleteVMModules 113 | { 114 | Param( 115 | [Parameter(Mandatory=$true)] 116 | [string]$ServiceDefinitionPath, 117 | [Parameter(Mandatory=$true)] 118 | [array]$Modules 119 | ) 120 | 121 | Set-ItemProperty $ServiceDefinitionPath -name IsReadOnly -value $false 122 | [Xml]$XMLfile = Get-Content $ServiceDefinitionPath 123 | $nsmgr = New-Object Xml.XmlNamespaceManager $XMLfile.NameTable 124 | $nsmgr.AddNamespace("ns", $XMLfile.DocumentElement.NamespaceURI) 125 | foreach ($module in $Modules) 126 | { 127 | $moduleNode = $XMLfile.SelectSingleNode("//ns:ServiceDefinition/ns:WebRole/ns:Imports/ns:Import[@moduleName='$module']", $nsmgr) 128 | if($moduleNode -ne $null) { 129 | $moduleNode.ParentNode.RemoveChild($moduleNode) 130 | } 131 | } 132 | 133 | $XMLFile.save($ServiceDefinitionPath) 134 | } 135 | 136 | function UpdateSQLAzureServerInfoData($rootDatabase, $targetDatabase) 137 | { 138 | $azureSqlServerInforFilePath = Resolve-Path "$PSScriptRoot\$($config.files.azureSqlServerInforFilePath)" 139 | Write-Host "Sql Server Info file Path is: $azureSqlServerInforFilePath" 140 | 141 | Set-ItemProperty $azureSqlServerInforFilePath -name IsReadOnly -value $false 142 | [Xml]$sdInfoXml = Get-Content $azureSqlServerInforFilePath 143 | 144 | $sdInfoXml.ServerInfo.ServerInstance = $sqlConfig.server 145 | $sdInfoXml.ServerInfo.Login = $sqlConfig.user 146 | $sdInfoXml.ServerInfo.Password = $sqlConfig.password 147 | $sdInfoXml.ServerInfo.RootDatabase = $rootDatabase 148 | $sdInfoXml.ServerInfo.TargetDatabase = $targetDatabase 149 | 150 | $sdInfoXml.Save($azureSqlServerInforFilePath) 151 | } 152 | 153 | function GenerateRemoteDesktopRequiredSettingsNodes($serviceConfigurationPath) 154 | { 155 | Set-ItemProperty $serviceConfigurationPath -name IsReadOnly -value $false 156 | [Xml]$cscfgXml = Get-Content $serviceConfigurationPath 157 | 158 | foreach($setting in $config.remoteAccessSettings) 159 | { 160 | $settingNode = $cscfgXml.ServiceConfiguration.Role.ConfigurationSettings.Setting | where {$_.name -like $setting.name} 161 | if($settingNode -ne $null){ 162 | $settingNode.Value = $setting.value; 163 | }else{ 164 | $configSettings = $cscfgXml.ServiceConfiguration.Role.ConfigurationSettings; 165 | $xdNS = $cscfgXml.DocumentElement.NamespaceURI 166 | $elem = $cscfgXml.CreateElement('Setting', $xdNS) 167 | $elem.SetAttribute('name',$setting.name) 168 | $elem.SetAttribute('value',$setting.value) 169 | $configSettings.AppendChild($elem); 170 | } 171 | } 172 | 173 | $cscfgXml.Save($serviceConfigurationPath) 174 | } 175 | 176 | function RemoveRemoteDesktopRequiredSettingsNodes($serviceConfigurationPath) 177 | { 178 | Set-ItemProperty $serviceConfigurationPath -name IsReadOnly -value $false 179 | [Xml]$cscfgXml = Get-Content $serviceConfigurationPath 180 | $nsmgr = New-Object Xml.XmlNamespaceManager $cscfgXml.NameTable 181 | $nsmgr.AddNamespace("ns", $cscfgXml.DocumentElement.NamespaceURI) 182 | 183 | foreach($setting in $config.remoteAccessSettings) 184 | { 185 | $settingNode = $cscfgXml.SelectSingleNode("//ns:ServiceConfiguration/ns:Role/ns:ConfigurationSettings/ns:Setting[@name='$($setting.name)']", $nsmgr) 186 | if($settingNode -ne $null) { 187 | $settingNode.ParentNode.RemoveChild($settingNode) 188 | } 189 | } 190 | 191 | $cscfgXml.Save($serviceConfigurationPath) 192 | } 193 | 194 | function AddCertificatesNode 195 | { 196 | $cloudConfig = Resolve-Path "$PSScriptRoot\$($config.files.cloudConfig)" 197 | Write-Host "Cloud config path is: $cloudConfig" 198 | 199 | Set-ItemProperty $cloudConfig -name IsReadOnly -value $false 200 | [Xml]$cscfgXml = Get-Content $cloudConfig 201 | 202 | $roleElement = $cscfgXml.ServiceConfiguration.Role 203 | $xdNS = $cscfgXml.DocumentElement.NamespaceURI 204 | $certificateElement = $cscfgXml.ServiceConfiguration.Role.Certificates 205 | if($certificateElement -eq $null){ 206 | $certificateElement = $cscfgXml.CreateElement('Certificates', $xdNS) 207 | $roleElement.AppendChild($certificateElement); 208 | } 209 | $elem = $cscfgXml.ServiceConfiguration.Role.Certificates.Certificate | where {$_.name -like $config.certificate.name} 210 | if($elem -eq $null){ 211 | $elem = $cscfgXml.CreateElement('Certificate', $xdNS) 212 | $certificateElement.AppendChild($elem); 213 | } 214 | $elem.SetAttribute('name', $config.certificate.name) 215 | $elem.SetAttribute('thumbprint', $config.certificate.thumbprint) 216 | $elem.SetAttribute('thumbprintAlgorithm', $config.certificate.thumbprintAlgorithm) 217 | 218 | $cscfgXml.Save($cloudConfig) 219 | } 220 | 221 | function DeleteCertificatesNode 222 | { 223 | $cloudConfig = Resolve-Path "$PSScriptRoot\$($config.files.cloudConfig)" 224 | Write-Host "Cloud config path is: $cloudConfig" 225 | 226 | Set-ItemProperty $cloudConfig -name IsReadOnly -value $false 227 | [Xml]$cscfgXml = Get-Content $cloudConfig 228 | $nsmgr = New-Object Xml.XmlNamespaceManager $cscfgXml.NameTable 229 | $nsmgr.AddNamespace("ns", $cscfgXml.DocumentElement.NamespaceURI) 230 | $certificatesNode = $cscfgXml.SelectSingleNode("//ns:ServiceConfiguration/ns:Role/ns:Certificates", $nsmgr) 231 | if($certificatesNode -ne $null) { 232 | $certificatesNode.ParentNode.RemoveChild($certificatesNode) 233 | } 234 | $cscfgXml.Save($cloudConfig) 235 | } 236 | 237 | function ConfigureRemoteDesktop 238 | { 239 | Param( 240 | [Parameter(Mandatory=$true)] 241 | [string]$EnableRemoteDesktopAccess 242 | ) 243 | 244 | $cloudConfig = Resolve-Path "$PSScriptRoot\$($config.files.cloudConfig)" 245 | $serviceDefinition = Resolve-Path "$PSScriptRoot\$($config.files.serviceDefinition)" 246 | Write-Host "Cloud config path is: $cloudConfig" 247 | Write-Host "Service Definition Path is: $serviceDefinition" 248 | 249 | Set-ItemProperty $cloudConfig -name IsReadOnly -value $false 250 | [Xml]$cscfgXml = Get-Content $cloudConfig 251 | if($EnableRemoteDesktopAccess -eq "true") 252 | { 253 | UpdateVMModules -ServiceDefinitionPath $serviceDefinition -Modules @("RemoteAccess", "RemoteForwarder") 254 | GenerateRemoteDesktopRequiredSettingsNodes $cloudConfig 255 | } else { 256 | DeleteVMModules -ServiceDefinitionPath $serviceDefinition -Modules @("RemoteAccess", "RemoteForwarder") 257 | RemoveRemoteDesktopRequiredSettingsNodes $cloudConfig 258 | } 259 | } 260 | 261 | function ConfigureSsl 262 | { 263 | Param( 264 | [Parameter(Mandatory=$true)] 265 | [string]$EnableSsl 266 | ) 267 | $serviceDefinition = Resolve-Path "$PSScriptRoot\$($config.files.serviceDefinition)" 268 | Write-Host "Service Definition Path is: $serviceDefinition" 269 | 270 | Set-ItemProperty $serviceDefinition -name IsReadOnly -value $false 271 | [Xml]$XMLfile = Get-Content $serviceDefinition 272 | $xdns = $XMLfile.DocumentElement.NamespaceURI 273 | 274 | if($EnableSsl -eq "true") { 275 | if(($XMLFile.ServiceDefinition.WebRole.Sites.Site.Bindings.Binding | where {$_.name -like $config.azure.sslEndpointName}) -eq $null){ 276 | $sslBinding = $XMLfile.CreateElement("Binding", $xdns) 277 | $XMLFile.ServiceDefinition.WebRole.Sites.Site.Bindings.AppendChild($sslBinding) 278 | $sslBinding.SetAttribute("name", $config.azure.sslEndpointName) 279 | $sslBinding.SetAttribute("endpointName", $config.azure.sslEndpointName) 280 | } 281 | if(($XMLFile.ServiceDefinition.WebRole.Endpoints.InputEndpoint | where {$_.name -like $config.azure.sslEndpointName}) -eq $null){ 282 | $sslEndpoint = $XMLfile.CreateElement("InputEndpoint", $xdns) 283 | $XMLFile.ServiceDefinition.WebRole.Endpoints.AppendChild($sslEndpoint) 284 | $sslEndpoint.SetAttribute("name", $config.azure.sslEndpointName) 285 | $sslEndpoint.SetAttribute("protocol", "https") 286 | $sslEndpoint.SetAttribute("port", "443") 287 | $sslEndpoint.SetAttribute("certificate", $config.certificate.name) 288 | } 289 | } else { 290 | $nsmgr = New-Object Xml.XmlNamespaceManager $XMLfile.NameTable 291 | $nsmgr.AddNamespace("ns", $xdns) 292 | $bindingNode = $XMLFile.SelectSingleNode("//ns:ServiceDefinition/ns:WebRole/ns:Sites/ns:Site/ns:Bindings/ns:Binding[@name='$SslEndpointName']", $nsmgr) 293 | if($bindingNode -ne $null) { 294 | $bindingNode.ParentNode.RemoveChild($bindingNode) 295 | } 296 | $endpointNode = $XMLFile.SelectSingleNode("//ns:ServiceDefinition/ns:WebRole/ns:Endpoints/ns:InputEndpoint[@name='$SslEndpointName']", $nsmgr) 297 | if($endpointNode -ne $null) { 298 | $endpointNode.ParentNode.RemoveChild($endpointNode) 299 | } 300 | } 301 | 302 | $XMLFile.save($serviceDefinition) 303 | } -------------------------------------------------------------------------------- /Scripts/UpdateSitefinityConfigs.ps1: -------------------------------------------------------------------------------- 1 | function UpdateSitefinityWebConfig($websiteRootDirectory) 2 | { 3 | $webConfig = Join-Path $websiteRootDirectory "\web.config" 4 | Set-ItemProperty $webConfig -name IsReadOnly -value $false 5 | $doc = New-Object System.Xml.XmlDocument 6 | $doc.Load($webConfig) 7 | 8 | if($doc.SelectSingleNode("//configuration/configSections/sectionGroup[@name='telerik']") -eq $null) 9 | { 10 | $configSectionsNode = $doc.SelectSingleNode("//configuration/configSections") 11 | $sectionGroupNode = $doc.CreateElement("sectionGroup") 12 | $sectionGroupNode.SetAttribute("name","telerik") 13 | $configSectionsNode.AppendChild($sectionGroupNode) 14 | $sectionNode = $doc.CreateElement("section") 15 | $sectionNode.SetAttribute("name","sitefinity") 16 | $sectionNode.SetAttribute("type","Telerik.Sitefinity.Configuration.SectionHandler, Telerik.Sitefinity") 17 | $sectionNode.SetAttribute("requirePermission","false") 18 | $sectionGroupNode.AppendChild($sectionNode) 19 | } 20 | 21 | if($doc.SelectSingleNode("//configuration/telerik") -eq $null) 22 | { 23 | $configurationNode = $doc.SelectSingleNode("//configuration") 24 | $telerikNode = $doc.CreateElement("telerik") 25 | $sitefinityNode = $doc.CreateElement("sitefinity") 26 | $testingNode = $doc.CreateElement("testing") 27 | $testingNode.SetAttribute("enabled", "true") 28 | $testingNode.SetAttribute("loadBalancingSyncLoggingEnabled", "false") #disable nlb sync logging becase it is peformance overhead. 29 | $environmentNode = $doc.CreateElement("environment") 30 | $environmentNode.SetAttribute("platform", "WindowsAzure") 31 | $sitefinityConfigNode = $doc.CreateElement("sitefinityConfig") 32 | $sitefinityConfigNode.SetAttribute("storageMode", "Database") 33 | $sitefinityNode.AppendChild($testingNode) 34 | $sitefinityNode.AppendChild($environmentNode) 35 | $sitefinityNode.AppendChild($sitefinityConfigNode) 36 | $telerikNode.AppendChild($sitefinityNode) 37 | $configurationNode.AppendChild($telerikNode) 38 | } 39 | 40 | $doc.Save($webConfig) 41 | } 42 | 43 | function UpdateSitefinityDataConfig($websiteRootDirectory, $azureServer, $user, $password, $database) 44 | { 45 | $dataConfig = Join-Path $websiteRootDirectory "\App_Data\Sitefinity\Configuration\DataConfig.config" 46 | Set-ItemProperty $dataConfig -name IsReadOnly -value $false 47 | $doc = New-Object System.Xml.XmlDocument 48 | $doc.Load($dataConfig) 49 | $connectionString = "Server=$azureServer;User ID=$user;Password=$password;Database=$database; Trusted_Connection=False;Encrypt=True" 50 | $connectionStringAttr = $doc.SelectSingleNode("//dataConfig/connectionStrings/add/@connectionString") 51 | $connectionStringAttr.Value = $connectionString 52 | 53 | $dbTypeAttr = $doc.SelectSingleNode("//dataConfig/connectionStrings/add/@dbType") 54 | if($dbTypeAttr) 55 | { 56 | $dbTypeAttr.Value = "SqlAzure" 57 | } 58 | else 59 | { 60 | $connectionStringNode = $doc.SelectSingleNode("//dataConfig/connectionStrings/add") 61 | $dbTypeAttribute = $doc.CreateAttribute("dbType") 62 | $dbTypeAttribute.Value = "SqlAzure" 63 | $connectionStringNode.Attributes.Append($dbTypeAttribute) 64 | } 65 | 66 | $doc.Save($dataConfig) 67 | } 68 | 69 | function EnableAzureTraceListener($websiteRootDirectory) 70 | { 71 | $webConfig = Join-Path $websiteRootDirectory "\web.config" 72 | Set-ItemProperty $webConfig -name IsReadOnly -value $false 73 | $doc = New-Object System.Xml.XmlDocument 74 | $doc.Load($webConfig) 75 | 76 | 77 | if($doc.SelectSingleNode("//configuration/system.diagnostics/trace/listeners/add[@name='AzureDiagnostics']") -eq $null) 78 | { 79 | $traceListenersNode = $doc.SelectSingleNode("//configuration/system.diagnostics/trace/listeners") 80 | if($traceListenersNode -eq $null) 81 | { 82 | $traceNode = $doc.SelectSingleNode("//configuration/system.diagnostics/trace") 83 | if($traceNode -eq $null) 84 | { 85 | $diagnosticsNode = $doc.SelectSingleNode("//configuration/system.diagnostics") 86 | if($diagnosticsNode -eq $null) 87 | { 88 | $diagnosticsNode = $doc.CreateElement("system.diagnostics") 89 | $doc.SelectSingleNode("//configuration").AppendChild($diagnosticsNode) 90 | } 91 | $traceNode = $doc.CreateElement("trace") 92 | $diagnosticsNode.AppendChild($traceNode) 93 | } 94 | $traceListenersNode = $doc.CreateElement("listeners") 95 | $traceNode.AppendChild($traceListenersNode) 96 | } 97 | $azureDiagnosticsNode = $doc.CreateElement("add") 98 | $azureDiagnosticsNode.SetAttribute("type", "Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=2.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35") 99 | $azureDiagnosticsNode.SetAttribute("name", "AzureDiagnostics") 100 | $filterNode = $doc.CreateElement("filter") 101 | $filterNode.SetAttribute("type", "") 102 | $azureDiagnosticsNode.AppendChild($filterNode) 103 | $traceListenersNode.AppendChild($azureDiagnosticsNode) 104 | $doc.Save($webConfig) 105 | } 106 | } 107 | 108 | function ConfigureAzureSearchService($searchConfig, $azureServiceAdminKey, $azureSearchServiceName) 109 | { 110 | Set-ItemProperty $searchConfig -name IsReadOnly -value $false 111 | $doc = New-Object System.Xml.XmlDocument 112 | $doc.Load($searchConfig) 113 | $azureSearchServiceNode = $doc.SelectSingleNode("//searchConfig/searchServices/add") 114 | $azureSearchServiceNode.Attributes['azureServiceAdminKey'].Value = $azureServiceAdminKey 115 | $azureSearchServiceNode.Attributes['azureSearchServiceName'].Value = $azureSearchServiceName 116 | $doc.Save($searchConfig) 117 | } --------------------------------------------------------------------------------