heat-templates/hot/Windows/PuppetAgent/heat-powershell-utils.psm1

472 lines
13 KiB
PowerShell
Executable File

#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
# UNTESTABLE METHODS
function ExitFrom-Script {
param(
[int]$ExitCode
)
exit $ExitCode
}
function Get-LastExitCode () {
return $LASTEXITCODE
}
function Get-PSMajorVersion () {
return $PSVersionTable.PSVersion.Major
}
function Open-FileForRead ($FilePath) {
return [System.IO.File]::OpenRead($FilePath)
}
function Write-PrivateProfileString ($Section, $Key, $Value, $Path) {
return [PSCloudbase.Win32IniApi]::WritePrivateProfileString(
$Section, $Key, $Value, $Path)
}
function Get-LastError () {
return [PSCloudbase.Win32IniApi]::GetLastError()
}
function Create-WebRequest ($Uri) {
return [System.Net.WebRequest]::Create($Uri)
}
function Get-Encoding ($CodePage) {
return [System.Text.Encoding]::GetEncoding($CodePage)
}
function Execute-Process ($DestinationFile, $Arguments) {
if (($Arguments.Count -eq 0) -or ($Arguments -eq $null)) {
$p = Start-Process -FilePath $DestinationFile `
-PassThru `
-Wait
} else {
$p = Start-Process -FilePath $DestinationFile `
-ArgumentList $Arguments `
-PassThru `
-Wait
}
return $p
}
# TESTABLE METHODS
function Log-HeatMessage {
param(
[string]$Message
)
Write-Host $Message
}
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-ExternalCommand {
param(
[ScriptBlock]$Command,
[array]$Arguments=@(),
[string]$ErrorMessage
)
$res = Invoke-Command -ScriptBlock $Command -ArgumentList $Arguments
if ((Get-LastExitCode) -ne 0) {
throw $ErrorMessage
}
return $res
}
function Is-WindowsServer2008R2 () {
$winVer = (Get-WmiObject -Class Win32_OperatingSystem).Version.Split('.')
return (($winVer[0] -eq 6) -and ($winVer[1] -eq 1))
}
function Install-WindowsFeatures {
param(
[Parameter(Mandatory=$true)]
[array]$Features,
[int]$RebootCode=$rebootAndReexecuteCode
)
if ((Is-WindowsServer2008R2) -eq $true) {
Import-Module -Name ServerManager
}
$rebootNeeded = $false
foreach ($feature in $Features) {
if ((Is-WindowsServer2008R2) -eq $true) {
$state = ExecuteWith-Retry -Command {
Add-WindowsFeature -Name $feature -ErrorAction Stop
} -MaxRetryCount 13 -RetryInterval 2
} else {
$state = ExecuteWith-Retry -Command {
Install-WindowsFeature -Name $feature -ErrorAction Stop
} -MaxRetryCount 13 -RetryInterval 2
}
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 -ExitCode $RebootCode
}
}
function Copy-FileToLocal {
param(
$UNCPath
)
$tempLocation = ${ENV:Temp}
$fileName = Split-Path -Path $UNCPath -Leaf
$localPath = Join-Path -Path $tempLocation -ChildPath $fileName
Copy-Item -Path $UNCPath -Destination $localPath -Recurse -Force
Log-HeatMessage ("Local file path: " + $localPath)
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)
} -MaxRetryCount 13 -RetryInterval 2
}
# 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 = Open-FileForRead $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 ((Get-PSMajorVersion) -lt 4) {
$hash = (Get-FileSHA1Hash -Path $File).Hash
} else {
$hash = (Get-FileHash -Path $File -Algorithm "SHA1").Hash
}
if ($hash -ne $ExpectedSHA1Hash) {
$errMsg = "SHA1 hash not valid for file: $filename. " +
"Expected: $ExpectedSHA1Hash Current: $hash"
throw $errMsg
}
}
function Install-Program {
param(
[Parameter(Mandatory=$true)]
[string]$DownloadLink,
[Parameter(Mandatory=$true)]
[string]$DestinationFile,
[Parameter(Mandatory=$true)]
[string]$ExpectedSHA1Hash,
[array]$Arguments,
[Parameter(Mandatory=$true)]
[string]$ErrorMessage
)
Download-File $DownloadLink $DestinationFile
Check-FileIntegrityWithSHA1 $DestinationFile $ExpectedSHA1Hash
$p = Execute-Process $DestinationFile $Arguments
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 = Write-PrivateProfileString $Section $Key $Value $Path
$lastError = Get-LastError
if (!$retVal -and $lastError) {
throw ("Cannot set value in ini file: " + $lastError)
}
}
}
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) {
Execute-ExternalCommand -Command {
netsh.exe advfirewall firewall add rule `
name=$Name dir=in action=allow protocol=$Protocol localport=$Port
} -ErrorMessage "Failed to add firewall rule"
}
function Add-WindowsUser {
param(
[parameter(Mandatory=$true)]
[string]$Username,
[parameter(Mandatory=$true)]
[string]$Password
)
Execute-ExternalCommand -Command {
NET.EXE USER $Username $Password '/ADD'
} -ErrorMessage "Failed to create new user"
}
# Invoke-RestMethod for Powershell versions less than 4.0
function Invoke-RestMethodWrapper {
param(
[Uri]$Uri,
[Object]$Body,
[System.Collections.IDictionary]$Headers,
[string]$Method
)
$request = Create-WebRequest $Uri
$request.Method = $Method
foreach ($key in $Headers.Keys) {
try {
$request.Headers.Add($key, $Headers[$key])
} catch {
$property = $key.Replace('-', '')
$request.$property = $Headers[$key]
}
}
if (($Body -ne $null) -and ($Method -eq "POST")) {
$encoding = Get-Encoding "UTF-8"
$bytes = $encoding.GetBytes($Body)
$request.ContentLength = $bytes.Length
$writeStream = $request.GetRequestStream()
$writeStream.Write($bytes, 0, $bytes.Length)
}
$response = $request.GetResponse()
$requestStream = $response.GetResponseStream()
$readStream = New-Object System.IO.StreamReader $requestStream
$data = $readStream.ReadToEnd()
return $data
}
function Invoke-HeatRestMethod {
param(
$Endpoint,
[System.String]$HeatMessageJSON,
[System.Collections.IDictionary]$Headers
)
if ((Get-PSMajorVersion) -lt 4) {
$result = Invoke-RestMethodWrapper -Method "POST" `
-Uri $Endpoint `
-Body $HeatMessageJSON `
-Headers $Headers
} else {
$result = Invoke-RestMethod -Method "POST" `
-Uri $Endpoint `
-Body $HeatMessageJSON `
-Headers $Headers
}
}
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 = ConvertTo-JSON -InputObject $heatMessage
Invoke-HeatRestMethod -Endpoint $Endpoint `
-HeatMessageJSON $heatMessageJSON `
-Headers $headers
}
Export-ModuleMember -Function *