diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/Config.ps1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/Config.ps1 new file mode 100644 index 0000000..acf2f51 --- /dev/null +++ b/Deployment/WindowsPowerShell/Modules/CoreFunctions/Config.ps1 @@ -0,0 +1,29 @@ +<# +Naming convention: + +== Normal variables +** Set: $NormalVar = 123 +** Get: Write-Host $NormalVar + +== Script-scope variables +** Set: $script:__ScriptScopeVar = 123 +** Get: Write-Host $__ScriptScopeVar + +== Global-scope variables +** Set: $global:__GlobalScopeVar__ = 123 +** Get: Write-Host $__GlobalScopeVar__ +#> + +$script:__ModulePath = $PsScriptRoot +$script:__ModuleName = $PsScriptRoot.Split("\")[-1] +$script:__DefaultLogPath = [IO.Path]::Combine([IO.Path]::GetTempPath(), "PowerShell_$__ModuleName.log") + + +$script:__RequiredModules = @("ServerManager", "DnsClient") +$script:__ImportModulesExplicitely = $true +$script:__ImportModulesErrorAction = "Stop" + + +$global:__StopExecutionThrowsExeption__ = $true +$global:__StopExecutionExitsSession__ = $false + diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/CoreFunctions.psd1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/CoreFunctions.psd1 new file mode 100644 index 0000000..0d52bf7 Binary files /dev/null and b/Deployment/WindowsPowerShell/Modules/CoreFunctions/CoreFunctions.psd1 differ diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/CoreFunctions.psm1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/CoreFunctions.psm1 new file mode 100644 index 0000000..dc9abfe --- /dev/null +++ b/Deployment/WindowsPowerShell/Modules/CoreFunctions/CoreFunctions.psm1 @@ -0,0 +1,34 @@ +# Import config first +. "$PsScriptRoot\Config.ps1" + +# Import functions from 'Include' subfolder +Get-ChildItem "$PsScriptRoot\Include" -Filter "*.ps1" | + ForEach-Object { + . "$($_.FullName)" + } + +trap { Stop-Execution $_ } + +Export-ModuleMember -Function * -Alias * + +if ($__ImportModulesExplicitely) { + foreach ($Module in $__RequiredModules) { + Write-Log "Importing module '$Module' ..." + Import-Module -Name "$Module" -ErrorAction "$__ImportModulesErrorAction" + } +} + +Write-Log "Module loaded from '$PsScriptRoot'" + +#------------------------------------------------------------------------------- + +switch ($Args[0]) { + 'installTo' { + Install-Module -InstallPath $args[1] -ModulePath $PsScriptRoot + } + 'register' { + Register-Module "$PsScriptRoot" + } + default { + } +} diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/en-US/about_CoreFunctions.help.txt b/Deployment/WindowsPowerShell/Modules/CoreFunctions/en-US/about_CoreFunctions.help.txt new file mode 100644 index 0000000..e69de29 diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Functions.ps1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Functions.ps1 new file mode 100644 index 0000000..3de8ff3 --- /dev/null +++ b/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Functions.ps1 @@ -0,0 +1,747 @@ +Function Stop-Execution { +<# +.SYNOPSIS +Breaks execution with specified error code. + +.DESCRIPTION +Function break script execution with error code provided. Error code may be 0 in case of non-error stop. + +It also tries to parse ErrorRecord or Exception object (if provided) and logs this information. +#> + param ( + $InputObject = $null, + [String] $ExitString = "", + [Int] $ExitCode = 1, + [Switch] $Success + ) + + Function Do-ExitFailure { + Write-LogFatal "STOP ($ExitCode): $ExitString" + if ($__StopExecutionThrowsExeption__) { + throw $InputObject + } + elseif ($__StopExecutionExitsSession__) { + exit $ExitCode + } + else { + break + } + } + + Function Do-ExitSuccess { + Write-LogInfo "STOP (0): $ExitString" + if ($__StopExecutionThrowsExeption__) { + exit 0 + } + elseif ($__StopExecutionExitsSession__) { + exit 0 + } + else { + break + } + } + + if ($Success -eq $true) { + if ($ExitString -eq "") { + $ExitString = "Script stopped with NO ERROR." + } + Do-ExitSuccess + } + + if ($ExitString -ne "") { + Do-ExitFailure + } + + + if ($InputObject -eq $null) { + $ExitString = "***** SCRIPT INTERRUPTED *****" + Do-ExitFailure + } + + + if ($ExitString -eq "") { + try { + $ErrorRecord = [System.Management.Automation.ErrorRecord] $InputObject + $ExitString = @" +$($ErrorRecord.ToString()) + +*** Invocation Info *** +$($ErrorRecord.InvocationInfo.PositionMessage) + +*** CategoryInfo *** +$($ErrorRecord.CategoryInfo.ToString()) + +*** FullyQualifiedErrorId *** +$($ErrorRecord.FullyQualifiedErrorId.ToString()) + +*** ScriptStackTrace *** +$($ErrorRecord.ScriptStackTrace.ToString()) +*** *** *** +"@ + } + catch { + $ErrorRecord = $null + Write-LogWarning "Unable to cast InputObject to [System.Management.Automation.ErrorRecord]" + } + } + + + if ($ExitString -eq "") { + try { + $Exception = [System.Exception] $InputObject + $ExitString = $Exception.ToString() + } + catch { + $Exception = $null + Write-LogWarning "Unable to cast InputObject to [System.Exception]" + } + } + + + if ($ExitString -eq "") { + try { + $ExitString = [String] $InputObject + } + catch { + Write-LogWarning "Unable to cast InputObject of type [$($InputObject.GetType())] to any of supported types." + } + } + + Do-ExitFailure +} + + +Function Set-ComputerName { + param ( + [String] $Name + ) + + + # Rename the computer + if ($Name -ne "") { + if (Test-ComputerName -ComputerName $Name) { + Stop-Execution -Success -ExitString "Computer name already configured" + } + else { + Write-Log "Renaming computer to '$Name'" + + Rename-Computer -NewName $NewName -Force -ErrorAction Stop + + Stop-Execution -ExitCode 3010 -ExitString "Please restart the computer now" + } + } +} + + + +Function Test-ComputerName { +<# +.SYNOPSIS +Test if computer name is set, and the computer belongs to specified domain / workgroup. + +.DESCRIPTION +Function tests the following conditions: +* the computer name is equal to the provided one +* the computer is a part of domain +* the computer belongs to the specified domain +* the computer belongs to the specified workgroup + +Multiple checks are logically ANDed. +#> + [CmdletBinding()] + param ( + [String] $ComputerName, + [String] $DomainName, + [String] $WorkgroupName, + [Switch] $PartOfDomain + ) + process { + $ComputerSystem = Get-WmiObject Win32_ComputerSystem + + if (($ComputerName -ne "") -and ($ComputerSystem.Name -ne "$ComputerName")) { + Write-Error "ComputerName is not equal to '$ComputerName'" + return $false + } + + if (($DomainName -ne "") -and ($ComputerSystem.Domain -ne "$DomainName")) { + Write-Error "DomainName is not equal to '$DomainName'" + return $false + } + + if (($WorkgroupName -ne "") -and ($ComputerSystem.Workgroup -ne "$WorkgroupName")) { + Write-Error "WorkgroupName is not equal to '$WorkgroupName'" + return $false + } + + if (($PartOfDOmain -eq $true) -and ($ComputerSystem.PartOfDomain -eq $false)) { + Write-Error "Computer is not the part of any domain." + return $false + } + + return $true + } +} + + + +Function Show-EthernetNetworkAdapters { + Get-WmiObject Win32_NetworkAdapter -Filter "PhysicalAdapter = 'True' AND AdapterTypeId = '0'" | + Select-Object 'Index','MACAddress','NetConnectionId' +} + + + +Function Set-NetworkAdapterConfiguration { +<# +.SYNOPSIS +Set network adapter configuration. + +.DESCRIPTION + + +.EXAMPLE +PS> Set-NetworkAdapterConfiguration -MACAddress aa:bb:cc:dd:ee:ff -Auto + +Convert "dynamic" parameters (DHCP) to "static" (manual) for network adapter with MAC address aa:bb:cc:dd:ee:ff + +.EXAMPLE +PS> Set-NetworkAdapterConfiguration -MACAddress aa:bb:cc:dd:ee:ff -DNSServer "192.168.0.1","192.168.0.2" + +Configure DNS servers list for network adapter with MAC address aa:bb:cc:dd:ee:ff + +#> + param ( + [String] $MACAddress = "", + + [Parameter(ParameterSetName="ManualConfig")] + [String] $IPAddress = "", + + [Parameter(ParameterSetName="ManualConfig")] + [String] $IPNetmask = "", + + [Parameter(ParameterSetName="ManualConfig")] + [String[]] $IPGateway = @(), + + [Parameter(ParameterSetName="ManualConfig")] + [String[]] $DNSServer = @(), + + [Parameter(ParameterSetName="ManualConfig")] + [Switch] $FirstAvailable, + + [String] $Name = "", + + [Parameter(ParameterSetName="AutoConfig",Mandatory=$true)] + [Switch] $Auto, + + [Parameter(ParameterSetName="AutoConfig")] + [Switch] $All + ) + + Write-Log "Configuring network adapter(s) ..." + + :SetIPAddress switch($PSCmdlet.ParameterSetName) { + "AutoConfig" { + Write-Log "'auto' mode" + + $IPv4RegExp = "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" + + if ($All -eq $true) { + $Filter = { $_.AdapterTypeId -eq 0 } + $Name = "" + } + else { + $Filter = { $_.MACAddress -eq $MACAddress } + } + + Get-WmiObject Win32_NetworkAdapter | + Where-Object $Filter | + ForEach-Object { + $NetworkAdapter = $_ + $AdapterConfig = Get-WmiObject Win32_NetworkAdapterConfiguration | + Where-Object { $_.Index -eq $NetworkAdapter.DeviceId } + + Write-Log "Configuring '$($NetworkAdapter.Name)' ..." + + for ($i = 0; $i -lt $AdapterConfig.IPAddress.Length; $i++) { + if ($AdapterConfig.IPAddress[$i] -match $IPv4RegExp) { + $IPAddress = $AdapterConfig.IPAddress[$i] + $IPNetmask = $AdapterConfig.IPSubnet[$i] + $IPGateway = $AdapterConfig.DefaultIPGateway + $DNSServer = $AdapterConfig.DNSServerSearchOrder + + Write-Log "Setting IP address ($IPAddress), netmask ($IPNetmask) ..." + $AdapterConfig.EnableStatic($IPAddress, $IPNetmask) | Out-Null + + Write-Log "Setting default gateways ($IPGateway) ..." + $AdapterConfig.SetGateways($IPGateway) | Out-Null + + Write-Log "Setting DNS servers ($DNSServer) ..." + $AdapterConfig.SetDNSServerSearchOrder($DNSServer) | Out-Null + } + } + + Write-Log "'$($NetworkAdapter.Name)' configured" + } + } + "ManualConfig" { + Write-Log "'manual' mode" + if ( $FirstAvailable ) { + Write-Log "Selecting first available network adapter ..." + $NetworkAdapter = Get-WmiObject Win32_NetworkAdapter | + Where-Object { $_.AdapterTypeId -eq 0 } | + Select-Object -First 1 + } + else { + $NetworkAdapter = Get-WmiObject Win32_NetworkAdapter | + Where-Object { $_.MACAddress -eq $MACAddress } + } + + if ( $NetworkAdapter -eq $null ) { + Write-LogError "Network adapter with MAC = '$MACAddress' not found." + return + } + + $AdapterConfig = Get-WmiObject Win32_NetworkAdapterConfiguration | + Where-Object { $_.Index -eq $NetworkAdapter.DeviceId } + + if (($IPAddress -ne "") -and ($IPNetmask -ne "")) { + Write-Log "Configuring IP address / netmask for '$($NetworkAdapter.Name)' ..." + + <# + for ($i = 0; $i -lt $AdapterConfig.IPAddress.Length; $i++) + { + if (($AdapterConfig.IPAddress[$i] -eq $IPAddress) -and ($AdapterConfig.IPSubnet[$i] -eq $IPNetmask)) + { + Write-Log "There is an adapter with required configuration." + break SetIPAddress + } + } + #> + Write-Log "Setting IP address $IPAddress, netmask $IPNetmask" + $AdapterConfig.EnableStatic("$IPAddress", "$IPNetmask") | Out-Null + + Write-Log "IP address configured." + } + + if ($IPGateway.Count -gt 0) { + Write-Log "Configuring IP gateway for '$($NetworkAdapter.Name)' ..." + + $AdapterConfig.SetGateways($IPGateway) | Out-Null + + Write-Log "IP gateway configured." + } + + if ($DNSServer.Count -gt 0) { + Write-Log "Configuring DNS server(s) for '$($NetworkAdapter.Name)' ..." + + $AdapterConfig.SetDNSServerSearchOrder($DNSServer) | Out-Null + + Write-Log "DNS configured." + } + } + } + + if ($Name -ne "") { + Write-Log "Changing adapter name '$($NetworkAdapter.NetConnectionId)' --> '$Name'" + $NetworkAdapter.NetConnectionId = "$Name" + $NetworkAdapter.Put() | Out-Null + } +} + + + +Function Test-WmiReturnValue { +<# +.SYNOPSIS +Check the ReturnValue property of the object provided. + +.DESCRIPTION +This funciton checks if ReturnValue property is equal to 0. + +=== TODO === +If it is not, then funtion should try to provide desctiption for the error code based on the WMI object type. +WMI object type must be provided explicitely. +#> + param ( + [Parameter(ValueFromPipeline=$true,Mandatory=$true)] + $InputObject, + [String] $Type = "" + ) + + try { + $ReturnValue = $InputObject.ReturnValue + } + catch { + throw "Property 'ReturnValue' not found on this object" + } + + if ($ReturnValue -eq 0) { + Write-Log "WMI operation completed successfully" + } + else { + throw "Operation failed with status code = $ReturnValue" + } +} + + + +Function Add-WindowsFeatureWrapper { +<# +.SYNOPSIS +Wraps Install-WindowsFeature function. + +.DESCRIPTION +This function adds some logic to multiple feature installation. + +It fails if any of required features fails. + +It reports that reboot required if it is required, or restarts the computer. +#> + param ( + [Parameter(Mandatory=$true)] + [String[]] $Name, + [Switch] $IncludeManagementTools, + [Switch] $AllowRestart, + [Switch] $NotifyRestart + ) + + $RestartNeeded = $false + + foreach ($Feature in $Name) { + Write-Log "Installing feature '$Feature' ..." + $Action = Install-WindowsFeature ` + -Name $Feature ` + -IncludeManagementTools:$IncludeManagementTools ` + -ErrorAction Stop + + if ($Action.Success -eq $true) { + if ($Action.FeatureResult.RestartNeeded -eq $true) { + Write-LogWarning "Restart required" + $RestartNeeded = $true + } + Write-Log "Feature '$Feature' installed successfully" + } + else { + Stop-Execution "Failed to install feature '$Feature'" + } + } + + if ($RestartNeeded) { + Write-Log "Restart required to finish feature(s) installation." + if ($AllowRestart) { + Write-Log "Restarting computer ..." + Restart-Computer -Force + } + elseif ($NotifyRestart) { + Stop-Execution -ExitCode 3010 -ExitString "Please restart the computer now." + } + } +} + + + +Function Get-PasswordAsSecureString { +<# +.SYNOPSIS +Convert to / request password as secure string. +#> + param ( + [String] $Password, + [String] $Prompt = "Please enter password" + ) + + if ($Password -eq "") { + Read-Host -Prompt $Prompt -AsSecureString + } + else { + ConvertTo-SecureString -String "$Password" -AsPlainText -Force + } +} + + + +Function New-Credential { +<# +.SYNOPSIS +Create new creadential object with username and password provided. +#> + param ( + [Parameter(Mandatory=$true)] + [String] $UserName, + + [String] $Password + ) + + $SecurePassword = Get-PasswordAsSecureString -Password "$Password" + New-Object System.Management.Automation.PSCredential( "$UserName", $SecurePassword ) +} + + + +Function Join-Domain { +<# +.SYNOPSIS +Executes "Join domain" action. +#> + param ( + [String] $DomainName, + [String] $UserName, + [String] $Password, + [Switch] $AllowRestart + ) + + $Credential = New-Credential -UserName "$DomainName\$UserName" -Password $Password + + # Add the computer to the domain + if (Test-ComputerName -DomainName $DomainName) { + #Stop-Execution -Success -ExitString "Computer already joined to domain '$DomainName'" + Write-LogWarning "Computer already joined to domain '$DomainName'" + } + else { + Write-Log "Joining computer to domain '$DomainName' ..." + + Add-Computer -DomainName $DomainName -Credential $Credential -Force -ErrorAction Stop + + if ($AllowRestart) { + Write-Log "Restarting computer ..." + Restart-Computer -Force + } + else { + #Stop-Execution -ExitCode 3010 -ExitString "Please restart the computer now." + Write-Log "Please restart the computer now." + } + } +} + + + +Function Invoke-WMSettingsChange { + if (-not ("win32.nativemethods" -as [type])) { + # Import SendMessageTimeout from Win32 + Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @" +[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] +public static extern IntPtr SendMessageTimeout( + IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam, + uint fuFlags, uint uTimeout, out UIntPtr lpdwResult); +"@ + } + + $HWND_BROADCAST = [IntPtr]0xFFFF + $WM_SETTINGCHANGE = 0x001A + $result = [UIntPtr]::Zero + + # Notify all windows of environment block change + Write-Log "Executing 'SendMessageTimeout' ..." + + $retval = [Win32.NativeMethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, + [UIntPtr]::Zero, "Environment", 2, 5000, [ref] $result) + + Write-Log "'SendMessageTimeout' returned '$retval' (non-zero is OK)" +} + + + +Function Set-AutoLogonCredentials { + param ( + [String] $DomainName, + [String] $UserName, + [String] $Password + ) + + $KeyName = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" + + if ($DomainName -ne "") { + $UserName = "$DomainName\$UserName" + } + + Write-Log "Setting AutoLogon credentials ..." + try { + [Microsoft.Win32.Registry]::SetValue($KeyName, "DefaultUserName", "$UserName", [Microsoft.Win32.RegistryValueKind]::String) + [Microsoft.Win32.Registry]::SetValue($KeyName, "DefaultPassword", "$Password", [Microsoft.Win32.RegistryValueKind]::String) + [Microsoft.Win32.Registry]::SetValue($KeyName, "AutoAdminLogon", "1", [Microsoft.Win32.RegistryValueKind]::String) + [Microsoft.Win32.Registry]::SetValue($KeyName, "ForceAutoLogon", "1", [Microsoft.Win32.RegistryValueKind]::String) + } + catch { + Write-LogError "FAILED" + return + } + + Write-Log "SUCCESS" +} + + + +Function Start-Program { + param ( + [String] $FilePath, + [String[]] $ArgumentList = @(' '), + [Int] $Timeout = 0, + [Switch] $NoWait, + [Switch] $PassThru + ) + + trap { + Write-LogError $_.Exception.Message + return $null + } + + Write-Log "Starting program: $FilePath $ArgumentList" + + $ProcessStartInfo = New-Object System.Diagnostics.ProcessStartInfo + $ProcessStartInfo.FileName = $FilePath + $ProcessStartInfo.Arguments = $ArgumentList + $ProcessStartInfo.CreateNoWindow = $true + $ProcessStartInfo.RedirectStandardOutput = $true + $ProcessStartInfo.RedirectStandardError = $true + $ProcessStartInfo.UseShellExecute = $false + + $Process = [System.Diagnostics.Process]::Start($ProcessStartInfo) + + if ($NoWait) { + if ($PassThru) { + return $Process + } + else { + return $null + } + } + else { + if ($Timeout -eq 0) { + $Process.WaitForExit() + } + else { + $Process.WaitForExit($Timeout) + } + } + + Write-Log ( "STDOUT:`n{0}" -f $Process.StandardOutput.ReadToEnd() ) + Write-Log ":STDOUT" + + Write-Log ( "STDERR:`n{0}" -f $Process.StandardError.ReadToEnd() ) + Write-Log ":STDERR" + + Write-Log "Program has finished with exit code ($($Process.ExitCode))" + + if ($PassThru) { + return $Process + } + else { + return $null + } +} +New-Alias -Name Exec -Value Start-Program + + + +function Test-ModuleVersion { +<# +.SYNOPSIS +Test module version. + +.DESCRIPTION +Function specified module (current module by default), and compares it's version to version provided. +Returned values: +* -2 : error occured +* -1 : module's version is lower than one provided +* 0 : module's version is equal to one provided +* 1 : module's version is greater than one provided +#> + param ( + [String] $Name = "$__ModuleName", + [String] $Version + ) + + $ModuleVersion = (Get-Module -Name $Name -ListAvailable).Version + + if ($ModuleVersion -eq $null) { + Write-Log "Module '$Name' not found." + return -2 + } + + try { + $RequiredVersion = [System.Version]::Parse($Version) + } + catch { + Write-Log "'$Version' is not a correct version string." + return -2 + } + + $ModuleVersion.CompareTo($RequiredVersion) +} + + + +Function Set-LocalUserPassword { + param ( + [String] $UserName, + [String] $Password, + [Switch] $Force + ) + + trap { Stop-Execution $_ } + + if ((Get-WmiObject Win32_UserAccount -Filter "LocalAccount = 'True' AND Name='$UserName'") -eq $null) { + throw "Unable to find local user account '$UserName'" + } + + if ($Force) { + Write-Log "Changing password for user '$UserName' to '*****'" # :) + ([ADSI] "WinNT://./$UserName").SetPassword($Password) | Out-Null + } + else { + Write-LogWarning "You are trying to change the password for the user '$UserName'. To do this please run the command again with -Force parameter." + } +} + + + +Function Resolve-LdapDnsName { + param ( + [String] $DomainName + ) + + Resolve-DNSName -Type "SRV" -Name "_ldap._tcp.dc._msdcs.$DomainName" | + Where-Object { $_.Type -eq "A" } | + Select-Object -Property Name,IPAddress +} + + + +Function Wait-LdapServerAvailable { + param ( + [String] $DomainName, + [Int] $PingSeqCountThreshold = 10, + [Int] $PingSeqPerHostThreshold = 5 + ) + + $LdapServerList = @( Resolve-LdapDnsName $DomainName ) + Write-Log @( "Ldap server list:", ( $LdapServerList | Out-String ) ) + + :MainLoop foreach ($LdapServer in $LdapServerList) { + $PingSeqCount = 0 + $PingSeqPerHost = 0 + while ($PingSeqPerHost -lt $PingSeqPerHostThreshold) { + if (Test-Connection -ComputerName $LdapServer.IpAddress -Count 1 -Quiet) { + Write-Log "Ping '$($LdapServer.Name)' OK" + $PingSeqCount++ + } + else { + Write-Log "Ping '$($LdapServer.Name)' FAILED" + $PingSeqCount = 0 + $PingSeqPerHost++ + } + + if ($PingSeqCount -ge $PingSeqCountThreshold) { + Write-Log "Returning true" + return $true + } + + Start-Sleep -Seconds 1 + } + } + + Write-Log "Returning false" + return $false +} + + diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Logger.ps1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Logger.ps1 new file mode 100644 index 0000000..e04db7b --- /dev/null +++ b/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Logger.ps1 @@ -0,0 +1,139 @@ +Function Initialize-Logger { + param ( + [String] $ModuleName = $__ModuleName, + [String] $LogPath = $__DefaultLogPath + ) + + if (-not ("log4net.LogManager" -as [type])) { + $FileStream = ([System.IO.FileInfo] (Get-Item "$__ModulePath\log4net.dll")).OpenRead() + + $AssemblyBytes = New-Object Byte[] $FileStream.Length + [Void] $FileStream.Read($AssemblyBytes, 0, $FileStream.Length) + + $FileStream.Close() + + [Void] [System.Reflection.Assembly]::Load($AssemblyBytes) + } + + [log4net.GlobalContext]::Properties["LogPath"] = $LogPath + [log4net.GlobalContext]::Properties["ModuleName"] = $ModuleName + + $script:__Logger = [log4net.LogManager]::GetLogger("PowerShell") + + $Log4NetConfig = New-Object System.IO.FileInfo("$__ModulePath\log4net.config") + + [log4net.Config.XmlConfigurator]::Configure($Log4NetConfig) + + $__Logger.info("Logger initialized. Log file: '$LogPath'") +} + + + +Function Write-LogInfo { + param ( + [String[]] $Text + ) + foreach ($Line in $Text) { + $__Logger.info($Line) + } +} +New-Alias -Name Write-Log -Value Write-LogInfo + + + +Function Out-LogInfo { + param ( + [Parameter(ValueFromPipeline=$true)] + [String] $Text + ) + $__Logger.info($Text) +} +New-Alias -Name Out-Log -Value Out-LogInfo + + + +Function Write-LogWarning { + param ( + [String] $Text + ) + foreach ($Line in $Text) { + $__Logger.warn($Line) + } +} + + + +Function Out-LogWarning { + param ( + [Parameter(ValueFromPipeline=$true)] + [String] $Text + ) + $__Logger__.warn($Text) +} + + + +Function Write-LogError { + param ( + [String] $Text + ) + foreach ($Line in $Text) { + $__Logger.error($Line) + } +} + + + +Function Out-LogError { + param ( + [Parameter(ValueFromPipeline=$true)] + [String] $Text + ) + $__Logger.error($Text) +} + + + +Function Write-LogFatal { + param ( + [String] $Text + ) + foreach ($Line in $Text) { + $__Logger.fatal($Line) + } +} + + + +Function Out-LogFatal { + param ( + [Parameter(ValueFromPipeline=$true)] + [String] $Text + ) + $__Logger.fatal($Text) +} + + + +Function Write-LogDebug { + param ( + [String] $Text + ) + foreach ($Line in $Text) { + $__Logger.debug($Line) + } +} + + + +Function Out-LogDebug { + param ( + [Parameter(ValueFromPipeline=$true)] + [String] $Text + ) + $__Logger.debug($Text) +} + + + +Initialize-Logger diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Module.ps1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Module.ps1 new file mode 100644 index 0000000..0615c60 --- /dev/null +++ b/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/Module.ps1 @@ -0,0 +1,172 @@ +Function Get-ModuleHelp { + param ( + [String] $ModuleName = $__ModuleName, + [String] $Path = "", + [Switch] $File, + [Int] $Width = 80 + ) + + $sb = { + $Module = Get-Module $ModuleName + + "`n" + "Module: $($Module.Name)" + "Module version: $($Module.Version)" + "`n" + "{0} Module Description {0}" -f ('=' * 30) + "`n" + + Get-Help "about_$($Module.Name)" | Out-String -Width $Width + + "{0} Exported Functions {0}" -f ('=' * 30) + "`n" + + foreach ($CommandName in $Module.ExportedCommands.Keys) { + '-' * 80 + Get-Help -Name $CommandName -Detailed | Out-String -Width $Width + } + } + + if (($File) -and ($Path -eq "")) { + $Path = [IO.Path]::GetTempFileName() + } + + if ($Path -ne "") { + & $sb | Out-File -FilePath $Path -Force + } + else { + & $sb | Out-Default + } + + if ($File) { + notepad.exe "$Path" + } +} + + + +function Update-PsModulePath { + param ( + [String] $AddPath = "" + ) + + $NewPsModulePath = ( + @([Environment]::GetEnvironmentVariable("PsModulePath", [EnvironmentVariableTarget]::Machine) -split ";") + @($AddPath) ` + | Select-Object -Unique + ) -join ';' + + [Environment]::SetEnvironmentVariable("PsModulePath", $NewPsModulePath, [EnvironmentVariableTarget]::Machine) + + Invoke-WMSettingsChange +} + + + +Function Install-Module { + param ( + [String] $InstallPath, + [String] $ModulePath, + [String] $ModuleName + ) + + if ($ModuleName -eq "") { + if ($ModulePath -eq "") { + Stop-Execution -ExitString "Don't know which module should be installed." + } + else { + $ModuleName = $ModulePath.Split("\")[-1] + } + } + + if ($InstallPath -eq "") { + Stop-Execution -ExitString "To install the module destination path must be provided." + } + else { + Write-Log "Installing the module to '$InstallPath'" + + $NewModulePath = [IO.Path]::Combine($InstallPath, $ModuleName) + if ([IO.Directory]::Exists($NewModulePath)) { + [IO.Directory]::Delete($NewModulePath, $true) + } + + Copy-Item -Path $ModulePath -Destination $InstallPath -Recurse -Force -ErrorAction Stop + + Update-PsModulePath -AddPath "$InstallPath" + } +} + + + +Function Register-Module { + param ( + [String] $ModulePath + ) + $ModuleRoot = Split-Path -Path $ModulePath -Parent + Write-Log "Registering the module at '$ModuleRoot'" + Update-PsModulePath -AddPath "$ModuleRoot" +} + + + +Function New-ModuleTemplate { + param ( + [Parameter(Mandatory=$true)] + [String] $Name, + + [String] $Path = "$($Env:USERPROFILE)\Documents\WindowsPowerShell\Modules", + + [Switch] $Force + ) + if ([IO.Directory]::Exists("$Path\$Name")) { + if ($Force) { + [IO.Directory]::Delete("$Path\$Name", $true) + } + else { + Write-Error "Folder '$Path\$Name' already exists. Remove it manually or specify -Force switch." + return + } + } + + + [IO.Directory]::CreateDirectory("$Path\$Name") + [IO.Directory]::CreateDirectory("$Path\$Name\en-US") + [IO.Directory]::CreateDirectory("$Path\$Name\include") + + + Set-Content -Path "$Path\$Name\en-US\about_$Name.help.txt" -Value @' +'@ + + + Set-Content -Path "$Path\$Name\Config.ps1" -Value @' +$script:__ModulePath = $PsScriptRoot +$script:__ModuleName = $PsScriptRoot.Split("\")[-1] +$script:__DefaultLogPath = [IO.Path]::Combine([IO.Path]::GetTempPath(), "PowerShell_$__ModuleName.log") + +$global:__StopExecutionExitsSession__ = $false +'@ + + + Set-Content -Path "$Path\$Name\$Name.psm1" -Value @' +# Import config first +. "$PsScriptRoot\Config.ps1" + +# Import functions from 'Include' subfolder +Get-ChildItem "$PsScriptRoot\Include" -Filter "*.ps1" | + ForEach-Object { + . "$($_.FullName)" + } + +Export-ModuleMember -Function * -Alias * + +Initialize-Logger -ModuleName $__ModuleName -LogPath $__DefaultLogPath + +Write-Log "Module loaded from '$PsScriptRoot'" +'@ + + + New-ModuleManifest ` + -Path "$Path\$Name\$Name.psd1" ` + -ModuleToProcess "$Name.psm1" ` + -RequiredModules "CoreFunctions" + +} diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/SqlFunctions.ps1 b/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/SqlFunctions.ps1 new file mode 100644 index 0000000..a8f3215 --- /dev/null +++ b/Deployment/WindowsPowerShell/Modules/CoreFunctions/include/SqlFunctions.ps1 @@ -0,0 +1,120 @@ +function New-SqlServerConnection { + param ( + [String] $ServerName, + [String] $UserName = '', + [String] $Password = '', + $Credentials, + [Switch] $SqlAuth + ) + + if ($Credentials -eq $null) { + if ($UserName -eq '') { + throw "User name must be provided in order to create credentials object!" + } + + $Credentials = New-Credential -UserName $UserName -Password $Password + } + + $Server = New-Object ` + -TypeName Microsoft.SqlServer.Management.Smo.Server ` + -ArgumentList $ServerName + + $LoginName = $Credentials.UserName -replace("^\\", "") + + try { + if ($SqlAuth) { + $Server.ConnectionContext.set_LoginSecure($false) + $Server.ConnectionContext.set_Login($LoginName) + $Server.ConnectionContext.set_SecurePassword($Credentials.Password) + } + else { + throw "Not implemented!" + } + } + catch { + return $null + } + + $Server +} + + + +function Import-SqlServerAssemblies { +<# +.SYNOPSIS +Import assemblies required to work with Sql Server instance from PowerShell + +.DESCRIPTION +Possible assembly list: + "Microsoft.SqlServer.Management.Common" + "Microsoft.SqlServer.Smo" + "Microsoft.SqlServer.Dmf" + "Microsoft.SqlServer.Instapi" + "Microsoft.SqlServer.SqlWmiManagement" + "Microsoft.SqlServer.ConnectionInfo" + "Microsoft.SqlServer.SmoExtended" + "Microsoft.SqlServer.SqlTDiagM" + "Microsoft.SqlServer.SString" + "Microsoft.SqlServer.Management.RegisteredServers" + "Microsoft.SqlServer.Management.Sdk.Sfc" + "Microsoft.SqlServer.SqlEnum" + "Microsoft.SqlServer.RegSvrEnum" + "Microsoft.SqlServer.WmiEnum" + "Microsoft.SqlServer.ServiceBrokerEnum" + "Microsoft.SqlServer.ConnectionInfoExtended" + "Microsoft.SqlServer.Management.Collector" + "Microsoft.SqlServer.Management.CollectorEnum" + "Microsoft.SqlServer.Management.Dac" + "Microsoft.SqlServer.Management.DacEnum" + "Microsoft.SqlServer.Management.Utility" + +.LINKS +http://msdn.microsoft.com/en-us/library/cc281962%28v=sql.105%29.aspx +#> + $AssemblyList = @( + "Microsoft.SqlServer.Smo" + "Microsoft.SqlServer.SmoExtended" + ) + + foreach ($asm in $AssemblyList) { + [System.Reflection.Assembly]::LoadWithPartialName($asm) | Out-Null + } +} + + + +function Import-SqlServerProvider { + $SqlPsReg="HKLM:\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.SqlServer.Management.PowerShell.sqlps" + + if (Get-ChildItem $SqlPsReg -ErrorAction "SilentlyContinue") { + throw "SQL Server Provider for Windows PowerShell is not installed." + } + else { + $Item = Get-ItemProperty $SqlPsReg + $SqlPsPath = [System.IO.Path]::GetDirectoryName($Item.Path) + } + + # + # Set mandatory variables for the SQL Server provider + # + $global:SqlServerMaximumChildItems = 0 + $global:SqlServerConnectionTimeout = 30 + $global:SqlServerIncludeSystemObjects = $false + $global:SqlServerMaximumTabCompletion = 1000 + + # + # Load the snapins, type data, format data + # + Push-Location + Set-Location $sqlpsPath + + Add-PSSnapin SqlServerCmdletSnapin100 + Add-PSSnapin SqlServerProviderSnapin100 + + Update-TypeData -PrependPath SQLProvider.Types.ps1xml + Update-FormatData -PrependPath SQLProvider.Format.ps1xml + + Pop-Location +} + diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/log4net.config b/Deployment/WindowsPowerShell/Modules/CoreFunctions/log4net.config new file mode 100644 index 0000000..ea7aa0e --- /dev/null +++ b/Deployment/WindowsPowerShell/Modules/CoreFunctions/log4net.config @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Deployment/WindowsPowerShell/Modules/CoreFunctions/log4net.dll b/Deployment/WindowsPowerShell/Modules/CoreFunctions/log4net.dll new file mode 100644 index 0000000..1e66c82 Binary files /dev/null and b/Deployment/WindowsPowerShell/Modules/CoreFunctions/log4net.dll differ