Merge "HOT for MSSQL Server with unit tests"

This commit is contained in:
Jenkins 2014-09-26 02:21:49 +00:00 committed by Gerrit Code Review
commit 063e6a0930
5 changed files with 1002 additions and 0 deletions

View File

@ -0,0 +1,41 @@
#ps1_sysnative
# Copyright 2014 Cloudbase Solutions Srl
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
$ErrorActionPreference = 'Stop'
$moduleName = "MSSQL.psm1"
$cfnFolder = "C:\cfn"
$modulePath = Join-Path $cfnFolder $moduleName
Import-Module -Name $modulePath -DisableNameChecking -Force
$mssqlServiceUsername="mssql-service-username"
$mssqlServicePassword = "mssql-service-user-password"
$mssqlSaPassword = "mssql-sa-password"
$mssqlFeatures = "mssql-features"
$mssqlInstanceName = "mssql-instance-name"
$mssqlIsoUNCPath = "mssql_iso_unc_path"
$mssqlWaitConditionEndpoint = "mssql_wait_condition_endpoint"
$mssqlWaitConditionToken = "mssql_wait_condition_token"
Install-MSSQL -MssqlServiceUsername $mssqlServiceUsername `
-MssqlServicePassword $mssqlServicePassword `
-MssqlSaPassword $mssqlSaPassword `
-MssqlFeatures $mssqlFeatures `
-MssqlInstanceName $mssqlInstanceName `
-MssqlIsoUNCPath $mssqlIsoUNCPath `
-MssqlWaitConditionEndpoint $mssqlWaitConditionEndpoint `
-MssqlWaitConditionToken $MssqlWaitConditionToken

View File

@ -0,0 +1,214 @@
#ps1_sysnative
# Copyright 2014 Cloudbase Solutions Srl
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
$ErrorActionPreference = 'Stop'
$modulePath = "heat-powershell-utils.psm1"
$currentLocation = Split-Path -Parent $MyInvocation.MyCommand.Path
$fullPath = Join-Path $currentLocation $modulePath
Import-Module -Name $fullPath -DisableNameChecking -Force
$heatTemplateName = "MSSQL"
$sqlLogFile = Join-Path ${ENV:ProgramFiles} -ChildPath `
"\Microsoft SQL Server\110\Setup Bootstrap\Log\Summary.txt"
function Log {
param(
$message
)
LogTo-File -LogMessage $message -Topic $heatTemplateName
Log-HeatMessage $message
}
function Install-RequiredFeatures {
param()
$windowsFeatures = @('NET-Framework-Core')
$installStatus = Install-WindowsFeatures $windowsFeatures
if ($installStatus.Reboot -eq $true) {
Log "System will reboot to finish install updates."
ExitFrom-Script $rebootAndReexecuteCode
}
}
function Add-MSSQLUser {
param(
$mssqlServiceUsername,
$mssqlServicePassword
)
Log "Adding the MSSQL user."
Add-WindowsUser $mssqlServiceUsername $mssqlServicePassword
}
function Copy-FilesLocal {
param(
$From
)
$copyLocal = ${ENV:Temp}
$fileName = Split-Path -Path $From -Leaf
$localPath = Join-Path -Path $copyLocal $fileName
Copy-Item -Force -Recurse -Path $From -Destination $localPath
Log ("Local iso path:" + $localPath)
return $localPath
}
function Get-MSSQLParameters {
param(
$MssqlServiceUsername,
$MssqlServicePassword,
$MssqlSaPassword,
$MssqlFeatures,
$MssqlInstanceName
)
$parameters = "/ACTION=install "
$parameters += "/Q "
$parameters += "/IACCEPTSQLSERVERLICENSETERMS=1 "
$parameters += "/INSTANCENAME=$MssqlInstanceName "
$parameters += "/FEATURES=$MssqlFeatures "
$parameters += "/SQLSYSADMINACCOUNTS=Admin "
$parameters += "/UpdateEnabled=1 "
$parameters += "/AGTSVCSTARTUPTYPE=Automatic "
$parameters += "/BROWSERSVCSTARTUPTYPE=Automatic "
$parameters += "/SECURITYMODE=SQL "
$parameters += "/SAPWD=$MssqlSaPassword "
$parameters += "/SQLSVCACCOUNT=.\$MssqlServiceUsername "
$parameters += "/SQLSVCPASSWORD=$MssqlServicePassword "
$parameters += "/SQLSVCSTARTUPTYPE=Automatic "
$parameters += "/NPENABLED=1 "
$parameters += "/TCPENABLED=1 /ERRORREPORTING=1"
return $parameters
}
function Get-MSSQLError {
param()
$sqlErrorString = "Failed: see details below"
$errorsCount = (Select-String $sqlErrorString -Path $sqlLogFile).Length
if ($errorsCount -ne 0) {
Log "MSSQL log file has an error."
return $true
}
return $false
}
function Add-NetRules {
param()
Open-Port 80 "TCP" "HTTP"
Open-Port 443 "TCP" "HTTPS"
Open-Port 1434 "UDP" "SQL Browser"
Open-Port 135 "TCP" "SQL Debugger/RPC"
Open-Port 5355 "UDP" "Link Local Multicast Name Resolution"
netsh.exe firewall set multicastbroadcastresponse ENABLE
$sqlServerBinaryPath = Join-Path ${ENV:ProgramFiles} -ChildPath `
"Microsoft SQL Server\MSSQL11.MSSQL\MSSQL\Binn\sqlservr.exe"
New-NetFirewallRule -DisplayName "Allow TCP Sql Server Ports" `
-Direction Inbound -Action Allow -EdgeTraversalPolicy Allow `
-Protocol UDP -LocalPort 100-65000 -Program $sqlServerBinaryPath
New-NetFirewallRule -DisplayName "Allow TCP Sql Server Ports" `
-Direction Inbound -Action Allow -EdgeTraversalPolicy Allow `
-Protocol TCP -LocalPort 100-65000 -Program $sqlServerBinaryPath
}
function Install-MSSQLInternal {
param(
$MssqlServiceUsername,
$MssqlServicePassword,
$MssqlSaPassword,
$MssqlFeatures,
$MssqlInstanceName,
$MssqlIsoUNCPath,
$MssqlWaitConditionEndpoint,
$MssqlWaitConditionToken
)
Log "Started MSSQL instalation."
Install-RequiredFeatures
Add-MSSQLUser $MssqlServiceUsername $MssqlServicePassword
$localIsoPath = Copy-FilesLocal $MssqlIsoUNCPath
Log "MSSQL ISO Mount."
$iso = Mount-DiskImage -PassThru $localIsoPath
$isoSetupPath = (Get-Volume -DiskImage $iso).DriveLetter + ":\setup.exe"
if (Test-Path $sqlLogFile) {
Remove-Item $sqlLogFile -Force
}
$parameters = Get-MSSQLParameters `
-MssqlServiceUsername $mssqlServiceUsername `
-MssqlServicePassword $mssqlServicePassword `
-MssqlSaPassword $mssqlSaPassword `
-MssqlFeatures $mssqlFeatures `
-MssqlInstanceName $mssqlInstanceName
ExecuteWith-Retry -Command {
param($isoSetupPath, $parameters)
Start-Process -Wait -FilePath $isoSetupPath `
-ArgumentList $parameters
} -Arguments @($isoSetupPath, $parameters)
Dismount-DiskImage -ImagePath $iso.ImagePath
Remove-Item $localIsoPath
if ((Get-MSSQLError) -eq $true) {
throw "Failed to install MSSQL Server."
}
Add-NetRules
$successMessage = "Finished MSSQL instalation."
Log $successMessage
Send-HeatWaitSignal -Endpoint $MssqlWaitConditionEndpoint `
-Message $successMessage -Success $true `
-Token $MssqlWaitConditionToken
}
function Install-MSSQL {
param(
$MssqlServiceUsername,
$MssqlServicePassword,
$MssqlSaPassword,
$MssqlFeatures,
$MssqlInstanceName,
$MssqlIsoUNCPath,
$MssqlWaitConditionEndpoint,
$MssqlWaitConditionToken
)
try {
Install-MSSQLInternal -MssqlServiceUsername $mssqlServiceUsername `
-MssqlServicePassword $mssqlServicePassword `
-MssqlSaPassword $mssqlSaPassword `
-MssqlFeatures $mssqlFeatures `
-MssqlInstanceName $mssqlInstanceName `
-MssqlIsoUNCPath $mssqlIsoUNCPath `
-MssqlWaitConditionEndpoint $mssqlWaitConditionEndpoint `
-MssqlWaitConditionToken $MssqlWaitConditionToken
} catch {
Log "Failed to install template."
Send-HeatWaitSignal -Endpoint $MssqlWaitConditionEndpoint `
-Message $_.Exception.Message -Success $false `
-Token $MssqlWaitConditionToken
}
}
Export-ModuleMember -Function * -ErrorAction SilentlyContinue

View File

@ -0,0 +1,173 @@
heat_template_version: 2013-05-23
description: >
Deploys a Microsoft SQL Server instance.
parameters:
key_name:
description: Name of an existing keypair to encrypt the Admin password
type: string
flavor:
description: Id or name of an existing flavor
type: string
default: m1.small
image:
description: Id or name of an existing Windows image
type: string
public_network_id:
type: string
description: >
ID of an existing public network where a floating IP will be
allocated.
private_network_id:
type: string
description: Id of an existing private network
mssql_service_username:
type: string
default: mssqluser
description: >
The username under which the mssql service runs
mssql_service_user_password:
type: string
hidden: true
constraints:
- length: { min: 8, max: 64 }
- allowed_pattern: "(?=^.{6,255}$)((?=.*\\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\\d)(?=.*[^A-Za-z0-9])(?=.*[a-z])|(?=.*[^A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z])|(?=.*\\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9]))^.*"
description: >
The password of the user under which the mssql service runs
mssql_sa_password:
type: string
hidden: true
description: >
The password of the sa mssql user
mssql_features:
type: string
default: >
SQLENGINE,ADV_SSMS
description: >
The mssql features to be installed
mssql_instance:
type: string
default: mssql
description: >
The mssql instance name
mssql_iso_unc_path:
type: string
description: >
The mssql iso file UNC path. It can be a local path or a network path.
mssql_max_timeout:
type: number
default: 3600
description: >
The maximum allowed time for the mssql instalation to finish.
resources:
server_port:
type: OS::Neutron::Port
properties:
network_id: { get_param: private_network_id }
server_floating_ip:
type: OS::Neutron::FloatingIP
depends_on: server_port
properties:
floating_network_id: { get_param: public_network_id }
port_id: { get_resource: server_port }
utils_module:
type: OS::Heat::SoftwareConfig
properties:
group: ungrouped
config: { get_file: heat-powershell-utils.psm1 }
mssql_module:
type: OS::Heat::SoftwareConfig
properties:
group: ungrouped
config: { get_file: MSSQL.psm1 }
mssql_main:
type: OS::Heat::SoftwareConfig
properties:
group: ungrouped
config:
str_replace:
template: { get_file: MSSQL.ps1 }
params:
mssql-service-username:
{ get_param: mssql_service_username }
mssql-service-user-password:
{ get_param: mssql_service_user_password }
mssql-sa-password:
{ get_param: mssql_sa_password }
mssql-features:
{ get_param: mssql_features }
mssql-instance-name:
{ get_param: mssql_instance }
mssql_iso_unc_path:
{ get_param: mssql_iso_unc_path }
mssql_wait_condition_endpoint:
{ get_attr: [ mssql_wait_condition_handle, endpoint ] }
mssql_wait_condition_token:
{ get_attr: [ mssql_wait_condition_handle, token ] }
mssql_init:
type: OS::Heat::MultipartMime
depends_on: mssql_wait_condition_handle
properties:
parts:
[ {
filename: "heat-powershell-utils.psm1",
subtype: "x-cfninitdata",
config: { get_resource: utils_module }
},
{
filename: "MSSQL.psm1",
subtype: "x-cfninitdata",
config: { get_resource: mssql_module }
},
{
filename: "cfn-userdata",
subtype: "x-cfninitdata",
config: { get_resource: mssql_main }
}
]
mssql:
type: OS::Nova::Server
depends_on: [ server_port, mssql_init ]
properties:
image: { get_param: image }
flavor: { get_param: flavor }
key_name: { get_param: key_name }
networks:
- port: { get_resource: server_port }
user_data_format: RAW
user_data: { get_resource: mssql_init }
mssql_wait_condition:
type: OS::Heat::WaitCondition
depends_on: mssql_wait_condition_handle
properties:
count: 1
handle: { get_resource: mssql_wait_condition_handle }
timeout: { get_param: mssql_max_timeout }
mssql_wait_condition_handle:
type: OS::Heat::WaitConditionHandle
outputs:
mssql_server_public_ip:
description: The MSSQL public IP address
value: { get_attr: [ server_floating_ip, floating_ip_address ] }

View File

@ -0,0 +1,205 @@
#ps1_sysnative
# Copyright 2014 Cloudbase Solutions Srl
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
$moduleName = "MSSQL"
$modulePath = "../MSSQL.psm1"
$currentLocation = Split-Path -Parent $MyInvocation.MyCommand.Path
$fullPath = Join-Path $currentLocation $modulePath
Remove-Module -Name $moduleName -ErrorAction SilentlyContinue
Import-Module -Name $fullPath -DisableNameChecking -Force
InModuleScope $moduleName {
Describe "Test Add MSSQL User" {
Context "On success" {
Mock Log { return $true } -Verifiable
Mock Add-WindowsUser { return $true } -Verifiable
Add-MSSQLUser "fakeuser" "fakepassword"
It "should verify caled all mocks" {
Assert-VerifiableMocks
}
}
}
}
InModuleScope $moduleName {
Describe "Test Add Net Rules" {
Context "On success" {
Mock netsh.exe { return $true } -Verifiable
Mock New-NetFirewallRule { return $true } -Verifiable
Add-NetRules
It "should verify caled all mocks" {
Assert-VerifiableMocks
}
}
}
}
InModuleScope $moduleName {
Describe "Test Get MSSQL Error" {
Context "With errors" {
$fakeErrors = @("err1")
Mock Select-String { return $fakeErrors } -Verifiable
Mock Log { return $true } -Verifiable
$result = Get-MSSQLError
It "should return true" {
$result | Should Be $true
}
It "should verify caled all mocks" {
Assert-VerifiableMocks
}
}
Context "No errors" {
$fakeErrors = @()
Mock Select-String { return $fakeErrors } -Verifiable
$result = Get-MSSQLError
It "should return false" {
$result | Should Be $false
}
It "should verify caled all mocks" {
Assert-VerifiableMocks
}
}
}
}
InModuleScope $moduleName {
Describe "Test Install Required Features" {
Context "No reboot" {
$fakeStatus = @{"Reboot"=$false}
Mock Install-WindowsFeatures { return $fakeStatus } -Verifiable
Install-RequiredFeatures
It "should verify caled all mocks" {
Assert-VerifiableMocks
}
}
Context "With reboot" {
$fakeStatus = @{"Reboot"=$true}
Mock Install-WindowsFeatures { return $fakeStatus } -Verifiable
Mock ExitFrom-Script { return $true } -Verifiable
Mock Log { return $true } -Verifiable
Install-RequiredFeatures
It "should verify caled all mocks" {
Assert-VerifiableMocks
}
}
}
}
InModuleScope $moduleName {
Describe "Test Install internal MSSQL" {
$mssqlServiceUsername="mssql-service-username";
$mssqlServicePassword = "mssql-service-user-password";
$mssqlSaPassword = "mssql-sa-password";
$mssqlFeatures = "mssql-features";
$mssqlInstanceName = "mssql-instance-name";
$mssqlIsoUNCPath = "mssql_iso_unc_path";
$mssqlWaitConditionEndpoint = "mssql_wait_condition_endpoint";
$mssqlWaitConditionToken = "mssql_wait_condition_token";
Context "On success" {
$cimInstance = New-CimInstance -ClassName "MSFT_DiskImage" `
-Namespace "root/Microsoft/Windows/Storage" `
-ClientOnly `
-Property @{"ImagePath"="fakePath"}
Mock Log { return $true } -Verifiable
Mock Install-RequiredFeatures { return $true } -Verifiable
Mock Add-MsSQLUser { return $true } -Verifiable
Mock Copy-FilesLocal { return $true } -Verifiable
Mock Mount-DiskImage { return $cimInstance } -Verifiable
Mock Get-Volume { return $true } -Verifiable
Mock Test-Path { return $true } -Verifiable
Mock Remove-Item { return $true } -Verifiable
Mock Get-MSSQLParameters { return $true } -Verifiable
Mock ExecuteWith-Retry { return $true } -Verifiable
Mock Dismount-DiskImage { return $true } -Verifiable
Mock Get-MssqlError { return $false } -Verifiable
Mock Add-NetRules { return $true } -Verifiable
Mock Send-HeatWaitSignal { return $true } -Verifiable
$result = Install-MSSQLInternal -MssqlServiceUsername $mssqlServiceUsername `
-MssqlServicePassword $mssqlServicePassword `
-MssqlSaPassword $mssqlSaPassword `
-MssqlFeatures $mssqlFeatures `
-MssqlInstanceName $mssqlInstanceName `
-MssqlIsoUNCPath $mssqlIsoUNCPath `
-MssqlWaitConditionEndpoint $mssqlWaitConditionEndpoint `
-MssqlWaitConditionToken $MssqlWaitConditionToken
It "should succeed" {
$result | Should Be $true
}
It "should verify caled all mocks" {
Assert-VerifiableMocks
}
}
Context "On failure internal" {
Mock Install-MSSQLInternal { throw } -Verifiable
Mock Log { return $true } -Verifiable
Mock Send-HeatWaitSignal { return $true } -Verifiable
Install-MSSQL -MssqlServiceUsername $mssqlServiceUsername `
-MssqlServicePassword $mssqlServicePassword `
-MssqlSaPassword $mssqlSaPassword `
-MssqlFeatures $mssqlFeatures `
-MssqlInstanceName $mssqlInstanceName `
-MssqlIsoUNCPath $mssqlIsoUNCPath `
-MssqlWaitConditionEndpoint $mssqlWaitConditionEndpoint `
-MssqlWaitConditionToken $MssqlWaitConditionToken
It "should verify caled all mocks" {
Assert-VerifiableMocks
}
}
Context "On succes internal" {
Mock Install-MSSQLInternal { return $true } -Verifiable
$result = Install-MSSQL -MssqlServiceUsername $mssqlServiceUsername `
-MssqlServicePassword $mssqlServicePassword `
-MssqlSaPassword $mssqlSaPassword `
-MssqlFeatures $mssqlFeatures `
-MssqlInstanceName $mssqlInstanceName `
-MssqlIsoUNCPath $mssqlIsoUNCPath `
-MssqlWaitConditionEndpoint $mssqlWaitConditionEndpoint `
-MssqlWaitConditionToken $MssqlWaitConditionToken
It "should be successful" {
Assert-VerifiableMocks
}
}
}
}

View File

@ -0,0 +1,369 @@
#ps1_sysnative
# Copyright 2014 Cloudbase Solutions Srl
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
$rebotCode = 1001
$reexecuteCode = 1002
$rebootAndReexecuteCode = 1003
function Log-HeatMessage {
param(
[string]$Message
)
Write-Host $Message
}
function ExitFrom-Script {
param(
[int]$ExitCode
)
exit $ExitCode
}
function ExecuteWith-Retry {
param(
[ScriptBlock]$Command,
[int]$MaxRetryCount=10,
[int]$RetryInterval=3,
[array]$Arguments=@()
)
$currentErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = "Continue"
$retryCount = 0
while ($true) {
try {
$res = Invoke-Command -ScriptBlock $Command `
-ArgumentList $Arguments
$ErrorActionPreference = $currentErrorActionPreference
return $res
} catch [System.Exception] {
$retryCount++
if ($retryCount -gt $MaxRetryCount) {
$ErrorActionPreference = $currentErrorActionPreference
throw $_.Exception
} else {
Write-Error $_.Exception
Start-Sleep $RetryInterval
}
}
}
}
function Execute-Command {
param(
[ScriptBlock]$Command,
[array]$Arguments=@(),
[string]$ErrorMessage
)
$res = Invoke-Command -ScriptBlock $Command -ArgumentList $Arguments
if ($LASTEXITCODE -ne 0) {
throw $ErrorMessage
}
return $res
}
function Install-WindowsFeatures {
param(
[Parameter(Mandatory=$true)]
[array]$Features,
[int]$RebootCode=$rebootAndReexecuteCode
)
$winVer = (Get-WmiObject -class Win32_OperatingSystem).Version.Split('.')
$isWinServer2008R2 = (($winVer[0] -eq 6) -and ($winVer[1] -eq 1))
if ($isWinServer2008R2 -eq $true) {
Import-Module ServerManager
}
$rebootNeeded = $false
foreach ($feature in $Features) {
if ($isWinServer2008R2 -eq $true) {
$state = ExecuteWith-Retry -Command {
Add-WindowsFeature -Name $feature -ErrorAction Stop
}
} else {
$state = ExecuteWith-Retry -Command {
Install-WindowsFeature -Name $feature -ErrorAction Stop
}
}
if ($state.Success -eq $true) {
if ($state.RestartNeeded -eq 'Yes') {
$rebootNeeded = $true
}
} else {
throw "Install failed for feature $feature"
}
}
if ($rebootNeeded -eq $true) {
ExitFrom-Script $RebootCode
}
}
function CopyFrom-SambaShare {
param(
[Parameter(Mandatory=$true)]
[string]$SambaDrive,
[Parameter(Mandatory=$true)]
[string]$SambaShare,
[Parameter(Mandatory=$true)]
[string]$SambaFolder,
[Parameter(Mandatory=$true)]
[string]$FileName,
[Parameter(Mandatory=$true)]
[string]$Destination
)
$p = New-PSDrive -Name $SambaDrive -Root $SambaShare -PSProvider FileSystem
$samba = ($SambaDrive + ":\\" + $SambaFolder)
$localPath = "$Destination\$FileName"
if (!(Test-Path $localPath)) {
Copy-Item "$samba\$FileName" $Destination -Recurse
}
return $localPath
}
function Unzip-File {
param(
[Parameter(Mandatory=$true)]
[string]$ZipFile,
[Parameter(Mandatory=$true)]
[string]$Destination
)
$shellApp = New-Object -ComObject Shell.Application
$zipFileNs = $shellApp.NameSpace($ZipFile)
$destinationNs = $shellApp.NameSpace($Destination)
$destinationNs.CopyHere($zipFileNs.Items(), 0x4)
}
function Download-File {
param(
[Parameter(Mandatory=$true)]
[string]$DownloadLink,
[Parameter(Mandatory=$true)]
[string]$DestinationFile
)
$webclient = New-Object System.Net.WebClient
ExecuteWith-Retry -Command {
$webclient.DownloadFile($DownloadLink, $DestinationFile)
}
}
# Get-FileHash for Powershell versions less than 4.0 (SHA1 algorithm only)
function Get-FileSHA1Hash {
[CmdletBinding()]
param(
[parameter(Mandatory=$true)]
[string]$Path,
[string]$Algorithm = "SHA1"
)
process
{
if ($Algorithm -ne "SHA1") {
throw "Unsupported algorithm: $Algorithm"
}
$fullPath = Resolve-Path $Path
$f = [System.IO.File]::OpenRead($fullPath)
$sham = $null
try {
$sham = New-Object System.Security.Cryptography.SHA1Managed
$hash = $sham.ComputeHash($f)
$hashSB = New-Object System.Text.StringBuilder `
-ArgumentList ($hash.Length * 2)
foreach ($b in $hash) {
$sb = $hashSB.AppendFormat("{0:x2}", $b)
}
return [PSCustomObject]@{ Algorithm="SHA1";
Hash=$hashSB.ToString().ToUpper();
Path=$fullPath }
}
finally {
$f.Close()
if($sham) {
$sham.Clear()
}
}
}
}
function Check-FileIntegrityWithSHA1 {
param(
[Parameter(Mandatory=$true)]
[string]$File,
[Parameter(Mandatory=$true)]
[string]$ExpectedSHA1Hash
)
if ($PSVersionTable.PSVersion.Major -lt 4) {
$hash = (Get-FileSHA1Hash -Path $File).Hash
} else {
$hash = (Get-FileHash -Path $File -Algorithm "SHA1").Hash
}
if ($hash -ne $ExpectedSHA1Hash) {
throw ("SHA1 hash not valid for file: $filename. " +
"Expected: $ExpectedSHA1Hash Current: $hash")
}
}
function Install-Program {
param(
[Parameter(Mandatory=$true)]
[string]$DownloadLink,
[Parameter(Mandatory=$true)]
[string]$DestinationFile,
[Parameter(Mandatory=$true)]
[string]$ExpectedSHA1Hash,
[Parameter(Mandatory=$true)]
[string]$Arguments,
[Parameter(Mandatory=$true)]
[string]$ErrorMessage
)
Download-File $DownloadLink $DestinationFile
Check-FileIntegrityWithSHA1 $DestinationFile $ExpectedSHA1Hash
$p = Start-Process -FilePath $DestinationFile `
-ArgumentList $Arguments `
-PassThru `
-Wait
if ($p.ExitCode -ne 0) {
throw $ErrorMessage
}
Remove-Item $DestinationFile
}
function Set-IniFileValue {
[CmdletBinding()]
param(
[parameter(Mandatory=$true, ValueFromPipeline=$true)]
[string]$Key,
[parameter()]
[string]$Section = "DEFAULT",
[parameter(Mandatory=$true)]
[string]$Value,
[parameter(Mandatory=$true)]
[string]$Path
)
process
{
$Source = @"
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace PSCloudbase
{
public sealed class Win32IniApi
{
[DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern uint GetPrivateProfileString(
string lpAppName,
string lpKeyName,
string lpDefault,
StringBuilder lpReturnedString,
uint nSize,
string lpFileName);
[DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool WritePrivateProfileString(
string lpAppName,
string lpKeyName,
StringBuilder lpString, // Don't use string, as Powershell replaces $null with an empty string
string lpFileName);
[DllImport("Kernel32.dll")]
public static extern uint GetLastError();
}
}
"@
Add-Type -TypeDefinition $Source -Language CSharp
$retVal = [PSCloudbase.Win32IniApi]::WritePrivateProfileString($Section, $Key, $Value, $Path)
if (!$retVal -and [PSCloudbase.Win32IniApi]::GetLastError()) {
throw "Cannot set value in ini file: " + [PSCloudbase.Win32IniApi]::GetLastError()
}
}
}
function LogTo-File {
param(
$LogMessage,
$LogFile="C:\cfn\userdata.log",
$Topic="general"
)
$date = Get-Date
$fullMessage = "$Date | $Topic | $LogMessage"
Add-Content -Path $logFile -Value $fullMessage
}
function Open-Port($port, $protocol, $name) {
netsh.exe advfirewall firewall add rule name=$name dir=in action=allow `
protocol=$protocol localport=$port
}
function Add-WindowsUser {
param(
[parameter(Mandatory=$true)]
[string]$Username,
[parameter(Mandatory=$true)]
[string]$Password
)
NET.EXE USER $Username $Password '/ADD'
}
function Send-HeatWaitSignal {
param(
[parameter(Mandatory=$true)]
[string]$Endpoint,
[parameter(Mandatory=$true)]
[string]$Token,
$Message,
$Success=$true
)
$statusMap = @{
$true="SUCCESS";
$false="FAILURE"
}
$heatMessage = @{
"status"=$statusMap[$Success];
"reason"="Configuration script has been executed.";
"data"=$Message;
}
$headers = @{
"X-Auth-Token"=$Token;
"Accept"="application/json";
"Content-Type"= "application/json";
}
$heatMessageJSON = $heatMessage | ConvertTo-JSON
$result = Invoke-RestMethod -Method POST -Uri $Endpoint `
-Body $heatMessageJSON -Headers $headers
}
Export-ModuleMember -Function *