├── .gitignore ├── README.md ├── .gitmodules ├── ActiveDirectory ├── Get-RecursiveGroupMembers.ps1 └── Get-NewADAccounts.ps1 ├── VMware ├── Set-UpdateTools.ps1 ├── Get-HostRAIDLevel.ps1 ├── Export-RVTools.ps1 ├── Enable-HotAdd.ps1 ├── Export-VCSADatabase.ps1 ├── Compare-VCRole.ps1 ├── VMNoteSync.ps1 └── Set-PostDeployConfig.ps1 ├── Server ├── Windows │ ├── Invoke-WindowsUpdates.ps1 │ └── Enable-CleanMgr.ps1 ├── DHCP │ ├── Sync-DHCPScope.ps1 │ └── Invoke-DHCPMigration.ps1 └── Dell │ └── Set-DRACConfig.ps1 ├── Games └── Get-SteamAchievements.ps1 ├── Misc ├── Move-PhotoDate.ps1 └── Update-Readme.ps1 ├── Profile └── Microsoft.PowerShell_profile.ps1 ├── Exchange └── Start-MailSearch.ps1 └── _Tools └── WmiExplorer.ps1 /.gitignore: -------------------------------------------------------------------------------- 1 | /.project 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sneddo/Powershell/HEAD/README.md -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "_Modules/PSReadLine"] 2 | path = _Modules/PSReadLine 3 | url = https://github.com/lzybkr/PSReadLine.git 4 | -------------------------------------------------------------------------------- /ActiveDirectory/Get-RecursiveGroupMembers.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Get all members of AD group, and nested groups 4 | 5 | .DESCRIPTION 6 | Get all members of AD group, and nested groups 7 | 8 | .NOTES 9 | File Name : Get-RecursiveGroupMembers.ps1 10 | Author : John Sneddon 11 | Version : 1.0.0 12 | 13 | .REQUIREMENTS 14 | WinSCP 15 | #> 16 | 17 | function Get-RecursiveGroupMembers ($grp) 18 | { 19 | $grpMembers = Get-ADGroup $grp -properties members 20 | $tmpMembers =@() 21 | 22 | foreach ($member in $grpMembers.members) 23 | { 24 | $mem = Get-ADObject $member 25 | 26 | if ($mem.ObjectClass -eq "group") 27 | { 28 | $tmpMembers += Get-RecursiveGroupMembers $member 29 | } 30 | else 31 | { 32 | # user only 33 | $tmpMembers += $member 34 | } 35 | } 36 | 37 | $tmpMembers 38 | } -------------------------------------------------------------------------------- /VMware/Set-UpdateTools.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Quick script to set VMware tools to update on reboot. 4 | 5 | .DESCRIPTION 6 | Sets all VMs in specified vCenter to update tools on reboot. 7 | 8 | .NOTES 9 | File Name : Set-UpdateTools.ps1 10 | Author : John Sneddon 11 | Version : 1.0 12 | 13 | .PARAMETER ComputerName 14 | Specify the vCenter server name 15 | #> 16 | param 17 | ( 18 | [ValidateScript({Test-Connection $_ -Quiet -Count 1})] 19 | [string]$ComputerName 20 | ) 21 | 22 | # Adding PowerCLI core snapin 23 | if (!(get-pssnapin -name VMware.VimAutomation.Core -erroraction silentlycontinue)) { 24 | add-pssnapin VMware.VimAutomation.Core 25 | } 26 | 27 | 28 | Connect-VIServer $ComputerName 29 | 30 | $vmConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec 31 | $vmConfigSpec.Tools = New-Object VMware.Vim.ToolsConfigInfo 32 | $vmConfigSpec.Tools.ToolsUpgradePolicy = "UpgradeAtPowerCycle" 33 | 34 | Foreach ($vm in (Get-View -ViewType VirtualMachine)) { 35 | $vm.ReconfigVM($vmConfigSpec) 36 | } 37 | -------------------------------------------------------------------------------- /Server/Windows/Invoke-WindowsUpdates.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Invoke Windows update search and install 4 | 5 | .DESCRIPTION 6 | Runs an update check and install, with automatic reboot at the end if 7 | required. 8 | 9 | .NOTES 10 | File Name : Invoke-WindowsUpdates.ps1 11 | Author : John Sneddon 12 | Version : 1.0.0 13 | #> 14 | 15 | #Define update criteria. 16 | $Criteria = "IsInstalled=0 and Type='Software'" 17 | 18 | #Search for relevant updates. 19 | $Searcher = New-Object -ComObject Microsoft.Update.Searcher 20 | $SearchResult = $Searcher.Search($Criteria).Updates 21 | 22 | #Download updates. 23 | $Session = New-Object -ComObject Microsoft.Update.Session 24 | $Downloader = $Session.CreateUpdateDownloader() 25 | $Downloader.Updates = $SearchResult 26 | $Downloader.Download() 27 | 28 | #Install updates. 29 | $Installer = New-Object -ComObject Microsoft.Update.Installer 30 | $Installer.Updates = $SearchResult 31 | $Result = $Installer.Install() 32 | 33 | #Reboot if required by updates. 34 | If ($Result.rebootRequired) { shutdown.exe /t 0 /r } -------------------------------------------------------------------------------- /VMware/Get-HostRAIDLevel.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Get the RAID config of all hosts 4 | 5 | .DESCRIPTION 6 | Get the RAID detail for ESX hosts 7 | 8 | .NOTES 9 | File Name : Get-HostRAIDLevel.ps1 10 | Author : John Sneddon 11 | Version : 1.0.0 12 | #> 13 | $timeout = 2 14 | $HostCred = Get-Credential 15 | $CIOpt = New-CimSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck -Encoding Utf8 -UseSsl 16 | 17 | $VMH = Get-VMHost | Where {$_.PowerState -eq "PoweredOn" -and $_.ConnectionState -eq "Connected" } 18 | 19 | $i=0 20 | foreach ($h in $VMH) 21 | { 22 | $RAID = "CIM Error" 23 | Write-Progress -Activity "Fetching RAID detail" -status $h.name -percentcomplete ($i/($vmh.count)) 24 | $Session = New-CimSession -Authentication Basic -Credential $HostCred -ComputerName $h.Name -port 443 -SessionOption $CIOpt -OperationTimeoutSec $timeout -ErrorAction SilentlyContinue 2>$null 3>$null 25 | if ($Session) 26 | { 27 | $RAID = Get-CimInstance -CimSession $Session -OperationTimeoutSec $timeout -ClassName CIM_StorageVolume -ErrorAction SilentlyContinue 2>$null 3>$null | Select -expandproperty ElementName 28 | } 29 | New-object PSObject -Property @{"Name" = $h.Name; "ESX"=$h.Version; "RAID" = $RAID } 30 | $i=$i+100 31 | } 32 | Write-Progress -Status "Done" -Activity "Done" -Completed 33 | -------------------------------------------------------------------------------- /VMware/Export-RVTools.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Performs full export from RVTools 4 | .DESCRIPTION 5 | Performs full export from RVTools. Archives old versions. 6 | .NOTES 7 | File Name : RVToolsExport.ps1 8 | Author : John Sneddon 9 | Version : 1.0.1 10 | .PARAMETER Servers 11 | Specify which vCenter server(s) to connect to 12 | .PARAMETER BasePath 13 | Specify the path to export to. Server name and date appended. 14 | .PARAMETER OldFileDays 15 | How many days to retain copies 16 | #> 17 | 18 | param 19 | ( 20 | $Servers = @("VCENTER"), 21 | $BasePath = "C:\Scripts\Powershell\RVToolsExport\Archive", 22 | $OldFileDays = 30 23 | ) 24 | 25 | $Date = (Get-Date -f "yyyyMMdd") 26 | 27 | foreach ($Server in $Servers) 28 | { 29 | # Create Directory 30 | New-Item -Path "$BasePath\$Server\$Date" -ItemType Directory -ErrorAction SilentlyContinue | Out-Null 31 | 32 | # Run Export 33 | . "C:\Program Files (x86)\RobWare\RVTools\RVTools.exe" -passthroughAuth -s $Server -c ExportAll2csv -d "$BasePath\$Server\$Date" 34 | 35 | # Cleanup old files 36 | $Items = Get-ChildItem -Directory "$BasePath\$server" 37 | foreach ($item in $items) 38 | { 39 | $itemDate = ("{0}/{1}/{2}" -f $item.name.Substring(6,2),$item.name.Substring(4,2),$item.name.Substring(0,4)) 40 | 41 | if ((((Get-date).AddDays(-$OldFileDays))-(Get-Date($itemDate))).Days -gt 0) 42 | { 43 | $item | Remove-Item -Recurse 44 | } 45 | } 46 | } 47 | 48 | # Changelog: 49 | # 1.0.0 - Initial release 50 | # 1.0.1 - Fixed cleanup routine with uninitialised variable $path 51 | -------------------------------------------------------------------------------- /Games/Get-SteamAchievements.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Grab achievement stats for a Steam account 4 | 5 | .DESCRIPTION 6 | Scrapes achievement data for a Steam profile. Must be set to public to work. 7 | 8 | .NOTES 9 | File Name : Get-SteamAchievements.ps1 10 | Author : John Sneddon 11 | Version : 1.0.0 12 | #> 13 | 14 | $User = Read-Host "Steam short name" 15 | $webclient = new-object System.Net.WebClient 16 | $GamesURLContent = $webclient.DownloadString("http://steamcommunity.com/id/{0}/games/?tab=all" -f $User) 17 | 18 | $Achieves = @() 19 | 20 | if ($GamesURLContent.Content -match "var rgGames = \[(.*)\]") 21 | { 22 | $Games = (ConvertFrom-Json ("[{0}]" -f $Matches[1])) | Where {$_.availStatLinks.achievements -and ($_.hours_forever -gt 0)} 23 | 24 | foreach ($Game in $Games) 25 | { 26 | $GameAchieveContent = Invoke-WebRequest ("http://steamcommunity.com/id/{0}/stats/{1}/?tab=achievements" -f $User, $Game.friendlyURL) 27 | 28 | if ($GameAchieveContent -match "([0-9]+) of ([0-9]+) \(([0-9]+)%\) achievements earned") 29 | { 30 | Write-Host ("{0} {1}/{2} ({3})" -f $Game.Name, $Matches[1], $Matches[2], $Matches[3]) 31 | $Achieves += New-Object PSObject -Property @{"Name" = $Game.Name; "Completed" = [int]$Matches[1]; "Total" = [int]$Matches[2]; "Percentage" = [int]$Matches[3] } 32 | } 33 | else 34 | { 35 | Write-Warning ("No achievement progress found for {0}" -f $Game.Name) 36 | $Achieves += New-Object PSObject -Property @{"Name" = $Game.Name; "Completed" = 0; "Total" = 0; "Percentage" = 0 } 37 | } 38 | } 39 | } 40 | 41 | $Achieves | Sort-Object Name | Select Name, Completed, Total, Percentage 42 | -------------------------------------------------------------------------------- /Misc/Move-PhotoDate.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Move photos into a date folder based on EXIF Date Taken field 4 | 5 | .DESCRIPTION 6 | Move photos to a folder in yyyy-MM-dd format, based on EXIF data. 7 | 8 | .NOTES 9 | File Name : Move-PhotoDate.ps1 10 | Author : John Sneddon 11 | Version : 1.0.0 12 | #> 13 | 14 | param([string]$file) 15 | 16 | function Get-TakenDate($file) 17 | { 18 | try 19 | { 20 | $image = New-Object System.Drawing.Bitmap -ArgumentList $file 21 | $date = $image.GetPropertyItem(36867).Value 22 | $takenValue = [System.Text.Encoding]::Default.GetString($date, 0, $date.Length - 1) 23 | $date = [DateTime]::ParseExact($takenValue, 'yyyy:MM:dd HH:mm:ss', $null) 24 | $image.Dispose() 25 | if ($date -eq $null) { 26 | Write-Host '{ No ''Date Taken'' in Exif }' -ForegroundColor Cyan 27 | return $null 28 | } 29 | return $date.ToString('yyyy-MM-dd') 30 | } 31 | catch 32 | { 33 | return $null 34 | } 35 | } 36 | 37 | [Reflection.Assembly]::LoadFile('C:\Windows\Microsoft.NET\Framework64\v4.0.30319\System.Drawing.dll') | Out-Null 38 | 39 | gci *.jpg | foreach { 40 | Write-Host "$_`t->`t" -ForegroundColor Cyan -NoNewLine 41 | 42 | $Date = Get-TakenDate( $_.FullName) 43 | 44 | if ($Date) 45 | { 46 | $newDir = $_.DirectoryName +"\" + $date + "\" 47 | 48 | if (-not (Test-Path $newDir)) 49 | { 50 | New-Item -ItemType Directory $NewDir | Out-Null 51 | } 52 | Write-Host ($newDir+$_.Name) -ForegroundColor Cyan 53 | mv $_.FullName ($newDir+$_.Name) 54 | } 55 | else 56 | { 57 | Write-Host "No Date Found" -ForegroundColor Yellow 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /VMware/Enable-HotAdd.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Enable memory and CPU hot-add for 2008 R2 servers 4 | .DESCRIPTION 5 | Basic script to bulk update VMs to enable CPU and memory Hot add. Only 6 | supports Server 2008 R2 at present, and VM will require a reboot for option 7 | to be available to use. 8 | .NOTES 9 | File Name : Enable-HotAdd.ps1 10 | Author : John Sneddon - @JohnSneddonAU 11 | Version : 1.0 12 | 13 | Version History 14 | --------------- 15 | 1.0.0 - Initial release 16 | 17 | .INPUTS 18 | vCenter Server must be specified 19 | .OUTPUTS 20 | HTML formatted email or HTML File 21 | 22 | .PARAMETER vCenter 23 | Specify the vCenter server(s) to connect to 24 | 25 | .EXAMPLE 26 | ./Enable-HotAdd -vCenter "vc.fqdn.domain.com" 27 | #> 28 | param 29 | ( 30 | [ValidateScript({Test-Connection -Count 1 -Quiet -ComputerName $_})] 31 | $vCenter=Read-Host "Enter vCenter Server name" 32 | ) 33 | 34 | function Enable-CPUMemHotAdd ($VMView) 35 | { 36 | $vmConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec 37 | 38 | $extra = New-Object VMware.Vim.optionvalue 39 | $extra.Key="vcpu.hotadd" 40 | $extra.Value="true" 41 | $vmConfigSpec.extraconfig += $extra 42 | 43 | $extra = New-Object VMware.Vim.optionvalue 44 | $extra.Key="mem.hotadd" 45 | $extra.Value="true" 46 | $vmConfigSpec.extraconfig += $extra 47 | 48 | $vmview.ReconfigVM($vmConfigSpec) 49 | } 50 | # Add Snapin 51 | if (!(Get-PSSnapin -name VMware.VimAutomation.Core -erroraction silentlycontinue)) { 52 | Add-PSSnapin VMware.VimAutomation.Core 53 | } 54 | Connect-VIServer $vCenter 55 | 56 | $VMs = Get-View -ViewType VirtualMachine | ?{$_.guest.guestFullName -eq "Microsoft Windows Server 2008 R2 (64-bit)"} 57 | 58 | $i = 0 59 | foreach ($VM in $VMs) 60 | { 61 | $perc = (100*$i++)/$VMs.Count 62 | Write-Progress -Activity ("Updating VM ({0} of {1})" -f $i, $VMs.Count) -Status ("{0:n2}% - {1}" -f $perc, $VM.Name) -PercentComplete $perc 63 | Enable-CPUMemHotAdd $VM 64 | } 65 | Write-Progress -Activity "Updating VM" -Status "Done" -Completed 66 | -------------------------------------------------------------------------------- /Misc/Update-Readme.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Quick script to recreate README.md 4 | 5 | .DESCRIPTION 6 | Loop over child items and generate a new README.md content from script headers. 7 | 8 | .NOTES 9 | File Name : Update-Readme.ps1 10 | Author : John Sneddon 11 | Version : 1.0.0 12 | #> 13 | 14 | function Get-ScriptSynopsis 15 | { 16 | param ($File) 17 | 18 | try 19 | { 20 | $synopsis = (Select-String -path $File -pattern ".SYNOPSIS" -Context 1).Context.PostContext.trim() 21 | } 22 | catch 23 | { 24 | Write-Warning ("No Synopsis found for {0}" -f $File) 25 | $synopsis = "" 26 | } 27 | 28 | return $synopsis 29 | } 30 | 31 | $currDirectory = "" 32 | 33 | $content = "Personal Collection of Powershell scripts. No guarantees provided with any of these scripts.`n`n" 34 | 35 | foreach ($dir in (Get-ChildItem -Directory -Path ../ | Where-Object {$_.Name -notmatch "^_"} | Sort-Object Name)) 36 | { 37 | $indent = "" 38 | $prevSubDir = "" 39 | $content += ("`n{0}`n=================`n" -f $dir.Name) 40 | foreach ($script in (Get-ChildItem -Path $dir.FullName -Recurse -Filter *.ps1 )) 41 | { 42 | $subDir = $script.DirectoryName.replace($dir.FullName, "").Trim("\") 43 | 44 | if ($subDir -ne "") 45 | { 46 | if ($SubDir -ne $prevSubDir) 47 | { 48 | if ($indent.length -ge 2) 49 | { 50 | $indent = $indent.Substring(2) 51 | } 52 | $content += ("{0}* {1}`n" -f $indent, $subDir) 53 | $indent += " " 54 | } 55 | else 56 | { 57 | #$indent = $indent.Substring(2) 58 | } 59 | $prevSubDir = $SubDir 60 | 61 | } 62 | else 63 | { 64 | $indent = "" 65 | } 66 | $currDirectory = $script.Directory.name 67 | $content += ("{0}* **{1}** - {2}`n" -f $indent, $script.name, (Get-ScriptSynopsis $Script.FullName)) 68 | } 69 | } 70 | $content | Out-File ..\README.md 71 | 72 | # Changelog 73 | # ========= 74 | # 1.0.0 - 2017-02-03 - Initial release 75 | -------------------------------------------------------------------------------- /Server/DHCP/Sync-DHCPScope.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Triggered based on events from DHCP server to sync reservations. 4 | 5 | .DESCRIPTION 6 | Script is trigged based on events 106 and 107 in DHCP server event log to 7 | sync DHCP reservations in a particular scope to failover partner. 8 | 9 | .NOTES 10 | File Name : Invoke-DHCPSync.ps1 11 | Author : John Sneddon - @JohnSneddonAU 12 | Version : 1.0.0 13 | 14 | .LINK 15 | http://www.sneddo.net/2015/05/dhcp-fail-over/ 16 | 17 | .INPUTS 18 | No inputs required 19 | .OUTPUTS 20 | None 21 | 22 | .PARAMETER eventRecordID 23 | Event ID of the triggering event 24 | 25 | .PARAMETER eventChannel 26 | Event Channel of the triggering event - will always be Microsoft-Windows-Dhcp-Server/Operational 27 | #> 28 | param($eventRecordID,$eventChannel) 29 | 30 | $regKey = "SYSTEM\CurrentControlSet\Services\DHCPServer\Parameters" 31 | $regKeyName = "SyncInProgress" 32 | 33 | # Get the exact event 34 | $event = Get-WinEvent -LogName $eventChannel -FilterXPath "" 35 | 36 | if (!(Get-Item Registry::HKEY_LOCAL_MACHINE\$regkey -ErrorAction SilentlyContinue).Value) 37 | { 38 | # Match the event Property to ScopeID - Format [[172.x.x.x]Scope name] 39 | if ($event.Properties[1].value -match "\[\[(.*)\].*\]") 40 | { 41 | $date = Get-date 42 | $scope = $Matches[1] 43 | $state = (Get-DhcpServerv4Scope $scope).State 44 | 45 | # Get scope and find the partner 46 | $partner = (Get-DhcpServerv4Failover -ScopeId $scope).PartnerServer 47 | 48 | # set reg key on partner 49 | $BaseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$partner) 50 | $SubKey = $BaseKey.OpenSubKey($regKey,$true) 51 | $SubKey.SetValue($regKeyName , 1, [Microsoft.Win32.RegistryValueKind]::DWORD) 52 | 53 | # start replication 54 | Invoke-DhcpServerv4FailoverReplication –ScopeID $scope -Force 55 | 56 | # Remove reg key from partner 57 | $SubKey.SetValue($regKeyName , 0, [Microsoft.Win32.RegistryValueKind]::DWORD) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Server/Windows/Enable-CleanMgr.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Enables CleanMgr on the system 4 | 5 | .DESCRIPTION 6 | Copies the required files to use CleanMgr on a 2008 to 2012 server 7 | 8 | .NOTES 9 | File Name : Enable-CleanMgr.ps1 10 | Author : John Sneddon 11 | Version : 1.0 12 | #> 13 | $OS = (Get-WMIObject Win32_OperatingSystem).Caption -match "Microsoft Windows Server (20[0-9]{2} (R2)?) Enterprise|Standard" 14 | switch ($Matches[1]) 15 | { 16 | "2012" 17 | { 18 | Copy-Item "$($env:SystemRoot)\winsxs\amd64_microsoft-windows-cleanmgr_31bf3856ad364e35_6.2.9200.16384_none_c60dddc5e750072a\cleanmgr.exe" "$($env:SystemRoot)\System32" 19 | Copy-Item "$($env:SystemRoot)\winsxs\amd64_microsoft-windows-cleanmgr.resources_31bf3856ad364e35_6.2.9200.16384_en-us_b6a01752226afbb3\cleanmgr.exe.mui" "$($env:SystemRoot)\System32\en-US" 20 | } 21 | "2008 R2" 22 | { 23 | Copy-Item "$($env:SystemRoot)\winsxs\amd64_microsoft-windows-cleanmgr_31bf3856ad364e35_6.1.7600.16385_none_c9392808773cd7da\cleanmgr.exe" "$($env:SystemRoot)\System32" 24 | Copy-Item "$($env:SystemRoot)\winsxs\amd64_microsoft-windows-cleanmgr.resources_31bf3856ad364e35_6.1.7600.16385_en-us_b9cb6194b257cc63\cleanmgr.exe.mui" "$($env:SystemRoot)\System32\en-US" 25 | } 26 | "2008" 27 | { 28 | switch ((Get-WMIObject Win32_OperatingSystem).OSArchitecture) 29 | { 30 | "64-bit" 31 | { 32 | Copy-Item "$($env:SystemRoot)\winsxs\amd64_microsoft-windows-cleanmgr_31bf3856ad364e35_6.0.6001.18000_none_c962d1e515e94269\cleanmgr.exe" "$($env:SystemRoot)\System32" 33 | Copy-Item "$($env:SystemRoot)\winsxs\amd64_microsoft-windows-cleanmgr.resources_31bf3856ad364e35_6.0.6001.18000_en-us_b9f50b71510436f2\cleanmgr.exe.mui" "$($env:SystemRoot)\System32\en-US" 34 | } 35 | "32-bit" 36 | { 37 | Copy-Item "$($env:SystemRoot)\winsxs\x86_microsoft-windows-cleanmgr_31bf3856ad364e35_6.0.6001.18000_none_6d4436615d8bd133\cleanmgr.exe" "$($env:SystemRoot)\System32" 38 | Copy-Item "$($env:SystemRoot)\winsxs\x86_microsoft-windows-cleanmgr.resources_31bf3856ad364e35_6.0.6001.18000_en-us_5dd66fed98a6c5bc\cleanmgr.exe.mui" "$($env:SystemRoot)\System32\en-US" 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ActiveDirectory/Get-NewADAccounts.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script sends a HTML formatted email to user containing a list of AD 4 | account created in the period specified. 5 | 6 | .DESCRIPTION 7 | Sends an email to the specified email address with the parameters specified. 8 | Email contains a list of AD user accounts that were created in the specified 9 | period, containing the SamAccountName, account used to create the account, 10 | and when it was created. 11 | 12 | The "owner" permission is used to determine the creating account, however if 13 | the creator is a member of "Domain Admins" group, the name will not be 14 | displayed. 15 | 16 | .NOTES 17 | Author : John Sneddon 18 | Twitter : @JohnSneddonAU 19 | Web : http://www.sneddo.net 20 | 21 | .PARAMETER daysToReport 22 | Integer containing the number of days in reporting period. Defaults to 7. 23 | 24 | .PARAMETER mailServer 25 | Outgoing Mail (SMTP) server address. If not supplied, script will prompt 26 | for address. 27 | 28 | .PARAMETER mailFrom 29 | Address to use as the from address. If not supplied, script will prompt 30 | for address. 31 | 32 | .PARAMETER mailTo 33 | Email address of recipient. If not supplied, script will prompt 34 | for address. 35 | 36 | .PARAMETER mailSubject 37 | Subject line for the email. Defaults to "AD accounts created in past x days" 38 | Where x is the value of daysToReport parameter 39 | 40 | .EXAMPLE 41 | Send email to foo@bar.com using mail.bar.com as SMTP server, 42 | from foobar@bar.com containing accounts created in the past 7 days. 43 | 44 | AuditNewAccounts.ps1 -mailTo foo@bar.com -mailServer mail.bar.com -mailFrom foobar@bar.com 45 | 46 | .EXAMPLE 47 | Report on accounts created in past day 48 | AuditNewAccounts.ps1 -daysToReport 1 -mailTo foo@bar.com -mailServer mail.bar.com -mailFrom foobar@bar.com 49 | #> 50 | param ( 51 | [int]$daysToReport=7, 52 | [string]$mailServer = (Read-Host "Enter mail server address"), 53 | [string]$mailFrom = (Read-Host "Send email from"), 54 | [string]$mailTo = (Read-Host "Send email to"), 55 | [string]$mailSubject = "AD accounts created in past $daysToReport days" 56 | ) 57 | 58 | # Import AD Module 59 | Import-Module ActiveDirectory 60 | 61 | # Get date object for today minus $daysToReport 62 | $d = (Get-Date).AddDays(-$daysToReport) 63 | 64 | # Get AD users created since then and return owner, 65 | $users = Get-ADUser -filter {whenCreated -gt $d} -properties whenCreated, nTSecurityDescriptor | 66 | Select-Object SamAccountName, Name, @{"Name"="Creator"; "Expression"={$_.nTSecurityDescriptor.owner}}, whenCreated 67 | 68 | # Create a basic HTML table 69 | $mailContent = $users | ConvertTo-HTML -body "Users created since $d" 70 | 71 | # send email 72 | Send-MailMessage -From $mailFrom -To $mailTo -Subject $mailSubject -SmtpServer $mailServer -BodyAsHtml $mailContent -------------------------------------------------------------------------------- /VMware/Export-VCSADatabase.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Backup and download the VCSA database 4 | 5 | .DESCRIPTION 6 | Uses WinSCP to connect to the VCSA, backup the database and then download to 7 | the local computer. 8 | 9 | .NOTES 10 | File Name : Export-VCSADatabase.ps1 11 | Author : John Sneddon 12 | Version : 1.0.0 13 | 14 | .REQUIREMENTS 15 | WinSCP 16 | #> 17 | 18 | $VCServers = @{$VCServers = @{"server" = @{"SshHostKeyFingerprint" = "ssh-rsa 1024 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"; "Username" = "root"; "Password" = "SECURESTRING"; }} 19 | 20 | $BackupLocation = "C:\temp\" 21 | 22 | "[{0}] Adding PowerCLI Snapin..." -f (Get-Date -Format "HH:mm:ss") 23 | Add-PSSnapIn VMware.VimAutomation.Core 24 | 25 | foreach ($VCServer in $VCServers.Keys.GetEnumerator()) 26 | { 27 | "[{0}] Connecting to vCenter ({1})..." -f (Get-Date -Format "HH:mm:ss"), $VCServer 28 | Connect-VIServer $VCServer | Out-Null 29 | 30 | 31 | $Cred = (New-Object System.Management.Automation.PSCredential $VCServers[$VCServer].Username, ($VCServers[$VCServer].Password | ConvertTo-SecureString)) 32 | $remoteFile = "/tmp/DBBackup-{0}-{1}.bak.gz" -f $VCServer, (Get-Date -f "yyyyMMdd") 33 | 34 | $cmd = "cd /opt/vmware/vpostgres/1.0/bin/; ./pg_dump VCDB -U vc -Fp -c | gzip > {0}" -f $remoteFile 35 | 36 | "[{0}] Starting backup..." -f (Get-Date -Format "HH:mm:ss") 37 | $r = Invoke-VMScript -VM (Get-VM $VCServer) -ScriptText $cmd -GuestCredential $cred 38 | 39 | if ($r.ExitCode -eq 0) 40 | { 41 | "[{0}] Backup successful" -f (Get-Date -Format "HH:mm:ss") 42 | try 43 | { 44 | # Load WinSCP .NET assembly 45 | Add-Type -Path "WinSCPnet.dll" 46 | 47 | # Setup session options 48 | $sessionOptions = New-Object WinSCP.SessionOptions 49 | $sessionOptions.Protocol = [WinSCP.Protocol]::Sftp 50 | $sessionOptions.HostName = $VCServer 51 | $sessionOptions.UserName = $VCServers[$VCServer].Username 52 | $sessionOptions.Password = $Cred.GetNetworkCredential().Password 53 | $sessionOptions.SshHostKeyFingerprint = $VCServers[$VCServer].SshHostKeyFingerprint 54 | 55 | $session = New-Object WinSCP.Session 56 | 57 | try 58 | { 59 | "[{0}] SFTP Connect" -f (Get-Date -Format "HH:mm:ss") 60 | # Connect 61 | $session.Open($sessionOptions) 62 | 63 | "[{0}] Begin transfer" -f (Get-Date -Format "HH:mm:ss") 64 | $transferResult = $session.GetFiles($remoteFile, $BackupLocation, $true) 65 | 66 | # Throw on any error 67 | $transferResult.Check() 68 | 69 | "[{0}] Transfer Complete" -f (Get-Date -Format "HH:mm:ss") 70 | } 71 | finally 72 | { 73 | # Disconnect, clean up 74 | $session.Dispose() 75 | } 76 | } 77 | catch [Exception] 78 | { 79 | "[{0}] {1}" -f (Get-Date -Format "HH:mm:ss"), $_.Exception.Message 80 | } 81 | } 82 | else 83 | { 84 | "[{0}] Backup FAILED" -f (Get-Date -Format "HH:mm:ss") 85 | } 86 | 87 | Disconnect-VIServer $VCServer | Out-Null 88 | } 89 | -------------------------------------------------------------------------------- /VMware/Compare-VCRole.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Generate a HTML report comparing two vCenter roles 4 | 5 | .DESCRIPTION 6 | Generate a HTML report containing the differences between two vCenter roles. 7 | 8 | Items highlighted in green are present in that role, but not the other. 9 | 10 | .NOTES 11 | File Name : Compare-VCRole.ps1 12 | Author : John Sneddon 13 | Version : 1.0 14 | 15 | .PARAMETER ComputerName 16 | Specify the vCenter server name 17 | .PARAMETER Role1 18 | First Role 19 | .PARAMETER Role2 20 | Second Role 21 | #> 22 | Param 23 | ( 24 | [ValidateScript({Test-Connection $_ -Quiet -Count 1})] 25 | [string]$ComputerName, 26 | [string]$Role1, 27 | [string]$Role2 28 | ) 29 | 30 | $report = @" 31 | 32 | 33 | 44 | 45 | 46 | 47 |

This report shows differences between two vCenter roles. Those highlighted in green are added to the role compared to the other role.

48 | 49 | "@ 50 | # Import Snapin 51 | if (!(Get-PSSnapin -name VMware.VimAutomation.Core -erroraction silentlycontinue)) { 52 | Add-PSSnapin VMware.VimAutomation.Core 53 | } 54 | 55 | # Connect to vCenter 56 | Connect-VIServer -Server $ComputerName | Out-Null 57 | 58 | # Get role privileges and differences 59 | $role1 = Get-VIRole $role1 60 | $role2 = Get-VIRole $role2 61 | $diff = Compare-Object $role1.PrivilegeList $role2.PrivilegeList 62 | 63 | $i = 0 64 | $c = $role1.PrivilegeList.count+$role1.PrivilegeList.count 65 | $report += "
$($role1)$($role2)
    " 66 | foreach ($p in $role1.PrivilegeList) 67 | { 68 | Write-Progress -Activity "Compiling differences" -Status "Role1" -PercentComplete ($i*100/$c) 69 | $pObj = (Get-VIPrivilege -Id $p) 70 | 71 | switch (($diff | Where {$_.inputobject -eq $p}).sideIndicator) 72 | { 73 | "<=" { $class = " class='add'" } 74 | "=>" { $class = " class='delete'" } 75 | default { $class = "" } 76 | } 77 | $report += ("{1}\{2}" -f $class, $pObj.ParentGroup, $pObj.Name) 78 | $i++ 79 | } 80 | $report += "
    " 81 | foreach ($p in $role2.PrivilegeList) 82 | { 83 | Write-Progress -Activity "Compiling differences" -Status "Role2" -PercentComplete ($i*100/$c) 84 | $pObj = (Get-VIPrivilege -Id $p) 85 | 86 | switch (($diff | Where {$_.inputobject -eq $p}).sideIndicator) 87 | { 88 | "=>" { $class = " class='add'" } 89 | "<=" { $class = " class='delete'" } 90 | default { $class = "" } 91 | } 92 | $report += ("{1}\{2}" -f $class, ($pObj.ParentGroup), ($pObj.Name)) 93 | } 94 | Write-Progress -Activity "Compiling differences" -Status "Complete" -Completed 95 | $report += "
" 96 | 97 | $report | out-file "$($env:temp)\r.html" 98 | . "$($env:temp)\r.html" 99 | -------------------------------------------------------------------------------- /Server/DHCP/Invoke-DHCPMigration.ps1: -------------------------------------------------------------------------------- 1 | # Requires -Modules DHCPServer -RunAsAdministrator 2 | Import-Module DHCPServer 3 | 4 | <# 5 | .SYNOPSIS 6 | Migrate a scope from the Source DHCP server to a new server 7 | .DESCRIPTION 8 | 9 | .NOTES 10 | File Name : Invoke-DHCPMigration.ps1 11 | Author : John Sneddon 12 | Version : 1.0.0 13 | 14 | .PARAMETER Source 15 | Source DHCP server. 16 | .PARAMETER Scope 17 | Scope to migrate 18 | .PARAMETER Destination 19 | Destination DHCP Server 20 | .PARAMETER BackupPath 21 | Path to backup DHCP server on import 22 | #> 23 | function Invoke-DHCPMigration 24 | { 25 | [CmdletBinding()] 26 | param 27 | ( 28 | [Parameter(Mandatory=$true)] 29 | [string$Source, 30 | 31 | [Parameter(Mandatory=$true)] 32 | [string]$Scope, 33 | 34 | [Parameter(Mandatory=$true)] 35 | [string]$Destination, 36 | 37 | [string]$BackupPath="C:\DHCP\Backup", 38 | 39 | [switch]$DisableScope=$false 40 | ) 41 | Begin 42 | { 43 | # Validate Source server is alive and is DHCP server 44 | if (-not (Test-Connection $Source -Quiet -Count 1)) 45 | { 46 | Write-Error "Source server unavailable" 47 | } 48 | else 49 | { 50 | try 51 | { 52 | Get-DhcpServerv4Statistics -ComputerName $Source | Out-Null 53 | } 54 | catch 55 | { 56 | Write-Error "Cannot connect to Source DHCP server" 57 | } 58 | } 59 | 60 | # Validate Destination server is alive and is DHCP server 61 | if (-not (Test-Connection $Destination -Quiet -Count 1)) 62 | { 63 | Write-Error "Destination server unavailable" 64 | } 65 | else 66 | { 67 | try 68 | { 69 | Get-DhcpServerv4Statistics -ComputerName $Destination | Out-Null 70 | } 71 | catch 72 | { 73 | Write-Error "Cannot connect to Destination DHCP server" 74 | } 75 | } 76 | 77 | # Validate Scope is valid 78 | <# Fix this later - use regex to determine ipv4/6 79 | try 80 | { 81 | Invoke-Expression "Get-DhcpServer$($version)scope -ComputerName $source -ScopeId $Scope" 82 | } 83 | catch 84 | { 85 | Write-Error "Could not get scope information from source server" 86 | } 87 | #> 88 | } 89 | 90 | Process 91 | { 92 | $File = ("{0}\{1}.xml" -f $Env:temp, $Scope) 93 | 94 | if ($DisableScope) 95 | { 96 | Write-Verbose "Disable Scope on Source server" 97 | Set-DhcpServerv4Scope -ScopeId $Scope -ComputerName $Source -State InActive 98 | } 99 | Write-Verbose "Exporting scope to file: $File" 100 | Export-DhcpServer -ComputerName $Source -ScopeId $Scope -Leases -File $File -Force 101 | 102 | Write-Verbose "Importing scope" 103 | Import-DhcpServer -ComputerName $Destination -File $File -BackupPath $BackupPath -Leases -ScopeOverwrite -Force 104 | 105 | if ($DisableScope) 106 | { 107 | Write-Verbose "Enable Scope on Destination server" 108 | Set-DhcpServerv4Scope -ScopeId $Scope -ComputerName $Destination -State Active 109 | } 110 | } 111 | 112 | End 113 | { 114 | Write-Verbose "Deleting migration file: $File" 115 | Remove-Item $File -Force 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /VMware/VMNoteSync.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Script syncs details from AD to vCenter notes. 4 | .DESCRIPTION 5 | Script performs the following general steps: 6 | 1. Find all VMs in all linked vCenter servers. 7 | 2. Query AD to find the Description and ManagedBy attibutes of the computer 8 | account. 9 | 3. The ManagedBy attribute is then resolved to individual users/groups 10 | 4. This information is then written back to vCenter 11 | .NOTES 12 | File Name : VMNoteSync.ps1 13 | Author : John Sneddon - @JohnSneddonAU 14 | Version : 1.1.6 15 | .PARAMETER VCServers 16 | Specify which vCenter server(s) to connect to 17 | .PARAMETER LogFile 18 | Specify the path to the logfile. Path to file must exist. Only used when 19 | DEBUG is set 20 | .PARAMETER DEBUG 21 | Enable Debug logging 22 | .PARAMETER Verbose 23 | Enable verbose output 24 | .EXAMPLE 25 | Normal operation 26 | ./VMNoteSync.ps1 27 | .EXAMPLE 28 | 29 | 30 | #> 31 | # 1.0.0 - Initial release 32 | # 1.1.0 - Only set notes if they differ 33 | # 1.1.1 - Bug fixes for groups not resolving correctly 34 | # 1.1.2 - Added step to update PowerCLI defaults to prevent script hanging 35 | # 1.1.3 - Write to logfile to aid debugging, move to parameters 36 | # 1.1.4 - Further work on Log file, bug fixes 37 | # 1.1.5 - Fix for server name not showing 38 | # 1.1.6 - Remove employer-specific code for release to web 39 | 40 | param 41 | ( 42 | # [Parameter(Mandatory=$true)] 43 | [string[]]$VCServers = Read-Host "Enter vCenter Server Address", 44 | 45 | # Set log locations 46 | [ValidateScript({Split-Path $_ | Test-Path})] 47 | [string]$LogFile="C:\Program Files\KIT\VMware\debug.log", 48 | 49 | [switch]$DEBUG, 50 | [switch]$verbose, 51 | 52 | # Email details 53 | $mailServer = "smtp.yourdomain.com", 54 | $mailFrom = ("VMware Reports<{0}@yourdomain.com>" -f $env:computername), 55 | $mailSubject = "VM Notes updated", 56 | $mailTo = @("you@yourdomain.com"), 57 | 58 | [switch]$WhatIf 59 | ) 60 | 61 | # Setup email style 62 | $emailContent = @" 63 | 64 | 65 | 74 | 75 | 76 |

VM notes updated

77 |

This list contains any VM notes that have been updated from AD information

78 | "@ 79 | 80 | # AD cmdlets ignore ErrorAction parameter, ignore errors and handle errors by returned value 81 | $EAP = $ErrorActionPreference 82 | $ErrorActionPreference = "silentlyContinue" 83 | 84 | ################################################################################ 85 | # FUNCTIONS # 86 | ################################################################################ 87 | # Function to output to CLI and optionally file 88 | function Write-Log ($str, $showInCLI=$true) 89 | { 90 | $outStr = ("[{0}] {1}" -f (Get-Date -Format "HH:mm:ss"), $str) 91 | if ($showInCLI) 92 | { 93 | Write-Host $outStr 94 | } 95 | 96 | # if in debug mode, output to file 97 | if ($script:DEBUG) 98 | { 99 | ($script:debugLog).WriteLine($outStr) 100 | ($script:debugLog).flush() 101 | } 102 | } 103 | 104 | ############################################################################### 105 | # Setup # 106 | ############################################################################### 107 | # setup log file and write script start time to file 108 | if ($DEBUG) 109 | { 110 | $debugLog = [System.IO.StreamWriter] $LogFile 111 | Write-Log ("Script Started {0}" -f (Get-Date)) $false 112 | } 113 | 114 | 115 | ############################################################################### 116 | # Retrieve VMs # 117 | ############################################################################### 118 | # Add snapin and connect to VC 119 | Write-Log "Connecting to vCenter..." 120 | Add-PSSnapin VMware.VimAutomation.Core -ErrorAction SilentlyContinue 121 | Connect-VIServer -Server $VCServers -AllLinked | Out-Null 122 | 123 | # Allow multiple connections 124 | Set-PowerCLIConfiguration -DefaultVIServerMode Multiple -confirm:$false | Out-Null 125 | 126 | # get all Powered on VMs - ignore test PCs 127 | Write-Log "Fetching VMs..." 128 | $VMs = (Get-VM * | Where {$_.Powerstate -eq "PoweredOn" -and $_.Name -notmatch "^TPC.*" -and $_.Name -notmatch "PCWIN7DEV*"}) 129 | 130 | # create a blank array and fill it with custom objects with all annotations 131 | Write-Log "Fetching VM Notes..." 132 | $VMList = @() 133 | foreach ($VM in $VMs) 134 | { 135 | if ($verbose) 136 | { 137 | Write-Log "`t $($VM.Name)" 138 | } 139 | $tmpObj = New-Object PSObject -Property @{"Name" = $VM.Name} 140 | ( $VM | Get-Annotation | foreach {$tmpObj | Add-Member NoteProperty $_.Name $_.Value}) 141 | $VMList += $tmpObj 142 | 143 | Write-Progress -Activity "Fetching VM details..." -Status $VM.Name -PercentComplete (($VMList.count/$VMs.count)*100) 144 | } 145 | Write-Progress -Activity "Fetching VM details..." -Status "Complete" -Completed 146 | 147 | ############################################################################### 148 | # Find info from AD # 149 | ############################################################################### 150 | Write-Log "Finding Description/Manager information..." 151 | Import-Module ActiveDirectory 152 | 153 | $ManageGroups = @{} # Keep track of resolved groups to speed things up 154 | $ADList = @{} # Hash table to store Description/Manager for server 155 | 156 | # Find SME and System Owner 157 | for ($i=0; $i -lt $VMList.count; $i++) 158 | { 159 | try 160 | { 161 | $Computername = Get-ADComputer $VMList[$i].Name -Properties Description, ManagedBy 162 | } 163 | catch 164 | { 165 | $ComputerName = $null 166 | } 167 | $Manager = "" 168 | 169 | # Check if there is a manager listed 170 | if ($computerName.ManagedBy -ne $null) 171 | { 172 | if ($ManageGroups.Keys -notcontains $computerName.ManagedBy) 173 | { 174 | $addGroup = $true; 175 | 176 | try 177 | { 178 | $managers = Get-ADGroupMember $computerName.ManagedBy 179 | 180 | if ($managers -ne $null) 181 | { 182 | foreach ($manager in $managers) 183 | { 184 | $Manager += ";"+$manager.name 185 | } 186 | } 187 | } 188 | catch 189 | { 190 | # getting the group failed, the field must be a user - set as System Owner 191 | $addGroup = $false 192 | $Manager = ";"+(Get-ADUser $computername.ManagedBy).Name 193 | } 194 | 195 | # trim Manager strings to remove first semicolon 196 | # if no Manger, return a hyphen 197 | if ($Manager.length -gt 0) 198 | { 199 | $Manager = $Manager.SubString(1) 200 | } 201 | else 202 | { 203 | $Manager = "-" 204 | } 205 | 206 | # Add the details to a hashtable to avoid looking it up again 207 | if ($addGroup) 208 | { 209 | $ManageGroups.Add($computerName.ManagedBy,@{"Manager"=$Manager;"Description"=$computerName.Description}) 210 | } 211 | } 212 | # We have seen this group before 213 | else 214 | { 215 | $Manager = $ManageGroups[$computerName.ManagedBy]["Manager"] 216 | $Description = $ManageGroups[$computerName.ManagedBy]["Description"] 217 | } 218 | 219 | # Create a new object and assign to $ADList 220 | $ADList.Add($computerName.Name, @{"Manager" = $Manager; 221 | "Description" = $computerName.Description}) 222 | Write-Progress -Activity "Finding Description/Manager" -Status $Computername.Name -PercentComplete ((100*$i)/$VMList.count) -ErrorAction SilentlyContinue 223 | if ($verbose) 224 | { 225 | Write-Log (New-Object PSObject -Property @{ "Manager" = $Manager; "Description" = $computerName.Description } | Format-Table) 226 | } 227 | } 228 | } 229 | Write-Progress -Activity "Finding Description/Manager" -Status "Complete!" -Completed 230 | 231 | ############################################################################### 232 | # Set Notes on VMs # 233 | ############################################################################### 234 | Write-Log "Importing notes..." 235 | $updates = @() 236 | foreach ($VM in $VMList) 237 | { 238 | if ($ADList.Keys -contains $VM.Name) 239 | { 240 | # Get VM object reference 241 | $VMref = $VMs | Where {$_.Name -eq $VM.Name} 242 | 243 | if ($WhatIf) 244 | { 245 | if ($ADList[$VM.Name]["Description"] -ne $VM.Description) 246 | { 247 | $VMref | Set-Annotation -CustomAttribute "Description" -Value $ADList[$VM.Name]["Description"] -Whatif 248 | } 249 | if ($ADList[$VM.Name]["Manager"] -ne $VM."Manager") 250 | { 251 | $VMref | Set-Annotation -CustomAttribute "Manager" -Value $ADList[$VM.Name]["Manager"] -Whatif 252 | } 253 | } 254 | else 255 | { 256 | foreach ($field in @("Description", "Manager")) 257 | { 258 | if ($ADList[$VM.Name][$field] -ne $VM.$field) 259 | { 260 | ($VMref | Set-Annotation -CustomAttribute $field -Value $ADList[$VM.Name][$field]) 261 | $updates += New-Object PSObject -Property @{"Server" = $VM.Name; 262 | "Field" = $field; 263 | "Value" = $ADList[$VM.Name][$field]} 264 | if ($verbose) 265 | { 266 | Write-Log "$($VM.Name) - $field updated $($ADList[$VM.Name][$field])" 267 | } 268 | } 269 | } 270 | } 271 | } 272 | else 273 | { 274 | if ($verbose) 275 | { 276 | Write-Log "$($VM.Name) - No update required" 277 | } 278 | } 279 | } 280 | 281 | # Close VC connection 282 | Disconnect-VIServer * -Confirm:$False 283 | 284 | ############################################################################### 285 | # Send email with updates made # 286 | ############################################################################### 287 | if ($updates.count -gt 0) 288 | { 289 | Write-Log "Sending Email..." 290 | $emailContent += "" 291 | for ($i = 0; $i -lt $updates.count; $i++) 292 | { 293 | # Setup zebra table classes 294 | if ($i%2) { $class='alt' } else { $class='normal' } 295 | 296 | # Get Old value 297 | $OldValue = ($VMList | ?{$_.Name -eq $updates[$i].Server} | Select -ExpandProperty $updates[$i].Field) 298 | $updates[$i] 299 | $emailContent += ("" -f $class, $updates[$i].Server, $updates[$i].Field, $oldValue, $updates[$i].Value) 300 | } 301 | $emailContent += "
ServerFieldOld ValueNew Value
{1}{2}{3}{4}
" 302 | Send-MailMessage -From $mailFrom -To $mailTo -Subject $mailSubject -SmtpServer $mailServer -BodyAsHtml $emailContent 303 | } 304 | else 305 | { 306 | Write-Log "No changes, no email generated" 307 | } 308 | 309 | # Close fileStream if used 310 | if ($DEBUG) 311 | { 312 | $debugLog.close() 313 | } 314 | 315 | # Reset EAP 316 | $ErrorActionPreference = $EAP 317 | -------------------------------------------------------------------------------- /Profile/Microsoft.PowerShell_profile.ps1: -------------------------------------------------------------------------------- 1 | # Set Profile version - Used for update check 2 | $Version= 0.50 3 | # Profile location - should be network accessible share 4 | $ProfileLocation = "\\server\path\_profile\Microsoft.Powershell_profile.ps1" 5 | 6 | # Personal drive 7 | $PersonalDrive = "\\server\path\user" 8 | 9 | # Set History settings here - for persistent history accross sessions 10 | $HistoryLocation = "\\server\path\_profile\history.xml" 11 | $HistoryCount = 500 # Max: ([Int16]::MaxValue) 12 | 13 | # PSReadLine Location - directory containing module 14 | $PSReadLineLocation = "\\server\path\_profile\PSReadline" 15 | 16 | # Array of vCenter servers to prompt to join when PowerCLI snapin loaded 17 | $vCenterServers = @("server1", "server2", "server3") 18 | 19 | 20 | ################################################################################ 21 | # FUNCTIONS # 22 | ################################################################################ 23 | function Install-PowerCLIXmlSerializer { 24 | <# 25 | .SYNOPSIS 26 | Installs all the PowerCLI XmlSerializers. 27 | 28 | .DESCRIPTION 29 | Installs all the PowerCLI XmlSerializers. 30 | This is needed to speed-up the execution of the first PowerCLI cmdlet that is run in a PowerCLI session. 31 | After you install a new version of PowerCLI you only have to run this function once. 32 | If you use both the 32-bit and the 64-bit version of PowerCLI then you have to run this function in both versions. 33 | You must run this function with administrative privileges enabled. 34 | 35 | .EXAMPLE 36 | PS C:\> Install-PowerCLIXmlSerializers 37 | 38 | .INPUTS 39 | None 40 | 41 | .OUTPUTS 42 | System.String 43 | 44 | .NOTES 45 | Author: Robert van den Nieuwendijk 46 | Date: 18-2-2012 47 | Version: 1.0 48 | 49 | .LINK 50 | http://rvdnieuwendijk.com/2012/02/18/function-to-speed-up-the-execution-of-the-first-powercli-cmdlet/ 51 | #> 52 | 53 | # Create an alias for ngen.exe 54 | Set-Alias ngen (Join-Path ([System.Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()) ngen.exe) 55 | 56 | # Install all the PowerCLI XmlSerializers 57 | Get-ChildItem -Path $env:SystemRoot\assembly\GAC_MSIL\VimService*.XmlSerializers | ` 58 | ForEach-Object { 59 | if ($_) { 60 | $Name = $_.Name 61 | Get-ChildItem -Path $_ 62 | } 63 | } | ` 64 | Select-Object -Property @{N="Name";E={$Name}},@{N="Version";E={$_.Name.Split("_")[0]}},@{N="PublicKeyToken";E={$_.Name.Split("_")[-1]}} | ` 65 | ForEach-Object { 66 | if ($_) { 67 | ngen install "$($_.Name), Version=$($_.Version), Culture=neutral, PublicKeyToken=$($_.PublicKeyToken)" 68 | } 69 | } 70 | } 71 | 72 | function Get-Addons 73 | { 74 | <# 75 | .SYNOPSIS 76 | Returns a two-column list of Modules and snapins available on this machine 77 | 78 | .DESCRIPTION 79 | Prints a two-column list of all Available Modules and all registered snapins. 80 | 81 | .EXAMPLE 82 | PS C:\> Get-Addons 83 | 84 | .INPUTS 85 | None 86 | 87 | .OUTPUTS 88 | System.String 89 | 90 | .NOTES 91 | Author: John Sneddon 92 | Date: 13-10-2014 93 | Version: 1.0 94 | #> 95 | $m = Get-Module -ListAvailable | Select -expandproperty Name 96 | $s = Get-PSSnapin -Registered | Select -ExpandProperty Name 97 | $colwidth = (Get-Host).ui.RawUI.WindowSize.Width/2 98 | 99 | # Header 100 | Write-Host "".Padright((Get-Host).ui.RawUI.WindowSize.Width-1, "=") 101 | Write-Host ("| Available Modules{0}| Available Snapins{1}|" -f "".PadRight($colWidth-20), ("".PadRight($colWidth-20))) 102 | Write-Host "".Padright((Get-Host).ui.RawUI.WindowSize.Width-1, "=") 103 | for ($i = 0; $i -lt [System.Math]::Max($m.count, $s.count); $i++) 104 | { 105 | if ($m[$i]) 106 | { 107 | Write-Host ("| {0}|" -f $m[$i].PadRight($colWidth-3)) -NoNewLine 108 | } 109 | else 110 | { 111 | Write-Host ("|{0}|" -f "".PadRight($colWidth-2)) -NoNewLine 112 | } 113 | if ($s[$i]) 114 | { 115 | Write-Host (" {0}|" -f $s[$i].PadRight($colWidth-3) ) 116 | } 117 | else 118 | { 119 | Write-Host ("{0}|" -f "".PadRight($colWidth-2)) 120 | } 121 | } 122 | Write-Host "".Padright((Get-Host).ui.RawUI.WindowSize.Width-1, "-") 123 | Write-Host 124 | } 125 | 126 | function Add-PowerCLISnapin 127 | { 128 | if (!(Get-PSSnapin -name VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) { 129 | Write-Host ("Adding PowerCLI Core{0}[ ]" -f "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-27)) -NoNewline 130 | try 131 | { 132 | Add-PSSnapin VMware.VimAutomation.Core 133 | Write-Host ("`rAdding PowerCLI Core{0}[" -f "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-27)) -NoNewline 134 | Write-Host -ForegroundColor Green " OK " -NoNewLine 135 | $Host.UI.RawUI.WindowTitle += " [VM]" 136 | } 137 | catch 138 | { 139 | Write-Host("`rAdding PowerCLI Core{0}[" -f "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-27)) -NoNewline 140 | Write-Host -ForegroundColor Red "FAIL" -NoNewLine 141 | } 142 | Write-Host "]" 143 | } 144 | 145 | Write-Host (Get-PowerCLIVersion).ToString() 146 | 147 | $vCenter = Get-Menu -Title "Connect to vCenter" -Question "Do you want to connect to a vCenter?" -Options $vCenterServers -AddNoOption 148 | 149 | if ($vCenter) 150 | { 151 | Connect-VIServer $vCenter -Credential (Get-Credential -UserName $env:username -Message "Enter vCentre Credentials") | Out-Null 152 | } 153 | } 154 | function Add-ExchangeSnapin 155 | { 156 | if (!(Get-PSSnapin -name Microsoft.Exchange.Management.PowerShell.SnapIn -ErrorAction SilentlyContinue)) { 157 | if ((Get-PSSnapin -Name Microsoft.Exchange.Management.PowerShell.SnapIn -ErrorAction SilentlyContinue -Registered).Count -gt 0) 158 | { 159 | Write-Host ("Adding Exchange Snapin{0}[ ]" -f "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-29)) -NoNewline 160 | try 161 | { 162 | Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn 163 | Write-Host ("`rAdding Exchange Snapin{0}[" -f "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-29)) -NoNewline 164 | Write-Host -ForegroundColor Green " OK " -NoNewLine 165 | $Host.UI.RawUI.WindowTitle += " [Exchange]" 166 | } 167 | catch 168 | { 169 | Write-Host("`rAdding Exchange Snapin{0}[" -f "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-29)) -NoNewline 170 | Write-Host -ForegroundColor Red "FAIL" -NoNewLine 171 | } 172 | Write-Host "]" 173 | } 174 | else 175 | { 176 | Write-Warning "Exchange Snapin not available on this host" 177 | } 178 | } 179 | } 180 | 181 | function Add-ADModule 182 | { 183 | if (!(Get-Module -name ActiveDirectoy -ErrorAction SilentlyContinue)) { 184 | Write-Host ("Adding Active Directory Module {0}[ ]" -f "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-37)) -NoNewline 185 | try 186 | { 187 | Import-Module ActiveDirectory 188 | Write-Host ("`rAdding Active Directory Module{0}[" -f "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-37)) -NoNewline 189 | Write-Host -ForegroundColor Green " OK " -NoNewLine 190 | $Host.UI.RawUI.WindowTitle += " [AD]" 191 | } 192 | catch 193 | { 194 | Write-Host("`rAdding Active Directory Module{0}[" -f "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-37)) -NoNewline 195 | Write-Host -ForegroundColor Red "FAIL" -NoNewLine 196 | } 197 | Write-Host "]" 198 | } 199 | } 200 | 201 | # Generate a Powershell console menu 202 | function Get-Menu 203 | { 204 | param ([String]$Title, [string]$Question, [string[]]$Options, [switch]$AddNoOption) 205 | $MenuOptions = @() 206 | 207 | if ($AddNoOption) 208 | { 209 | $MenuOptions += (New-Object System.Management.Automation.Host.ChoiceDescription "&No", "No") 210 | } 211 | 212 | for ($i = 1; $i -le $Options.Count; $i++) 213 | { 214 | $MenuOptions += (New-Object System.Management.Automation.Host.ChoiceDescription "&$i $($Options[$i-1])", $Options[$i-1]) 215 | } 216 | 217 | $result = $host.ui.PromptForChoice($title, $Question, $MenuOptions, 0) 218 | 219 | if ($AddNoOption) 220 | { 221 | $result = $result-1 222 | } 223 | 224 | if ($AddNoOption -and $result -eq 0) 225 | { 226 | return $null 227 | } 228 | return $Options[$result] 229 | } 230 | 231 | ################################################################################ 232 | # ALIASES # 233 | ################################################################################ 234 | Set-Alias vm Add-PowerCLISnapin 235 | Set-Alias ad Add-ADModule 236 | Set-Alias ex Add-ExchangeSnapin 237 | 238 | ################################################################################ 239 | # INIT # 240 | ################################################################################ 241 | # Check if profile exists or Profile is latest 242 | if ((!(Test-Path $Profile)) -or ($Version -lt (Get-Content $ProfileLocation | Select-String -Pattern "\$+Version\s*=").toString().split("=")[1].Trim())) 243 | { 244 | if (!(Test-Path $Profile)) 245 | { 246 | New-Item -Type Directory (Split-Path $profile) | Out-Null 247 | } 248 | Copy-Item $ProfileLocation $Profile 249 | & $Profile 250 | break 251 | } 252 | # Check if PSReadLine is installed 253 | if (! (Get-Module PSReadLine)) 254 | { 255 | # Copy the directory to the local profile location 256 | Copy-Item $PSReadLineLocation ("{0}\Modules\PSReadLine" -f (Split-Path $profile)) -Recurse -Force 257 | Import-Module PSReadLine 258 | } 259 | else 260 | { 261 | Import-Module PSReadLine 262 | } 263 | 264 | # Check if the H: drive is mapped 265 | if ($PersonalDrive -and -not (Test-Path "H:")) 266 | { 267 | New-PSDrive -Name "H" -PSProvider FileSystem -Root $PersonalDrive -Persist | Out-Null 268 | } 269 | 270 | Register-EngineEvent -SourceIdentifier powershell.exiting -SupportEvent -Action { 271 | Get-History -Count $HistoryCount | Export-Clixml $HistoryLocation 272 | } 273 | 274 | # Load History 275 | Import-CliXML $HistoryLocation | Add-History 276 | 277 | ################################################################################ 278 | # STYLE # 279 | ################################################################################ 280 | $Host.UI.RawUI.BackgroundColor = 'Black' 281 | $Host.UI.RawUI.ForegroundColor = 'White' 282 | $Host.PrivateData.ErrorForegroundColor = 'Red' 283 | $Host.PrivateData.ErrorBackgroundColor = 'Black' 284 | $Host.PrivateData.WarningForegroundColor = 'Yellow' 285 | $Host.PrivateData.WarningBackgroundColor = 'Black' 286 | $Host.PrivateData.DebugForegroundColor = 'Cyan' 287 | $Host.PrivateData.DebugBackgroundColor = 'Black' 288 | $Host.PrivateData.VerboseForegroundColor = 'DarkGray' 289 | $Host.PrivateData.VerboseBackgroundColor = 'Black' 290 | $Host.PrivateData.ProgressForegroundColor = 'White' 291 | $Host.PrivateData.ProgressBackgroundColor = 'DarkGray' 292 | 293 | # Set Console Size 294 | $pshost = Get-Host 295 | $pswindow = $pshost.ui.rawui 296 | 297 | $newsize = $pswindow.buffersize 298 | $newsize.height = 3000 299 | $newsize.width = 120 300 | $pswindow.buffersize = $newsize 301 | 302 | $newsize = $pswindow.windowsize 303 | $newsize.height = 50 304 | $newsize.width = 120 305 | $pswindow.windowsize = $newsize 306 | 307 | # Set Title 308 | if ($Host.UI.RawUI.WindowTitle -match "Administrator:") 309 | { 310 | $IsAdmin = $true 311 | $Admin = "*" 312 | } 313 | 314 | $Host.UI.RawUI.WindowTitle = ("PS{0} - User {1}{2} on {3}" -f $host.Version.ToString(), $env:username, $Admin, $env:ComputerName) 315 | Clear-Host 316 | 317 | ################################################################################ 318 | # HEADER # 319 | ################################################################################ 320 | Write-Host -ForegroundColor Green "".Padright((Get-Host).ui.RawUI.WindowSize.Width-1, "-") 321 | Write-Host -ForegroundColor Green " Profile Version: " -NoNewline 322 | Write-Host $Version 323 | Write-Host -ForegroundColor Green " User: ".PadRight(18) -NoNewLine 324 | if ($env:username -match "_a$") 325 | { 326 | Write-Host -ForegroundColor Red $env:username 327 | } 328 | else 329 | { 330 | Write-Host $env:username 331 | } 332 | Write-Host -ForegroundColor Green "".Padright((Get-Host).ui.RawUI.WindowSize.Width-1, "-") 333 | Write-Host 334 | -------------------------------------------------------------------------------- /VMware/Set-PostDeployConfig.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Post deploy script to configure VMs 4 | .DESCRIPTION 5 | Configurations applied 6 | * AD Object move 7 | * Change CD Drive letter to Z: 8 | * Initialise Data disks (2012+ only at this stage) 9 | * SCOM Agent Install 10 | * Update Group Policy 11 | * Install Updates 12 | 13 | .NOTES 14 | Author : John Sneddon 15 | Version : 1.2.1 16 | #> 17 | ################################################################################ 18 | # CONFIGURATION # 19 | ################################################################################ 20 | # This is the only section that should require changes 21 | 22 | # $Config is a hashtable with the key as the environment (i.e. Prod or Test), 23 | # and values as a hastable of the following: 24 | # OU - [string] which OU should the AD object be moved to (DN) 25 | # WSUSDefault - [string] Change the group to this after patching (ServerAutoWeek2) 26 | # memberOf - [string[]] AD Groups to add the server to 27 | $Config = @{"Prod" = @{ "OU" = ""; 28 | "WSUSDefault" = "ServerAutoWeek2"; 29 | "memberOf" = @("", 30 | "") }; 31 | "Test" = @{ "OU" = ""} 32 | } 33 | 34 | 35 | # Set Path of master script 36 | $Script = "\\Server\share\Set-PostDeployConfig.ps1" 37 | # Set Local Path 38 | $LocalScript = "C:\windows\temp\Set-PostDeployConfig.ps1" 39 | # Require user account to be in this domain 40 | $UserDomain = "SOUTHERNHEALTH" 41 | 42 | ################################################################################ 43 | # FUNCTIONS # 44 | ################################################################################ 45 | Function Get-ScriptVersion ($Path) 46 | { 47 | return [version](Get-Content $Path | Select-String -Pattern "Version\s*:").toString().split(":")[1].Trim() 48 | } 49 | 50 | Function Write-Status ($Message, $Status) 51 | { 52 | switch ($Status) 53 | { 54 | "OK" { Write-Host ("`r {0}{1}[" -f $Message, "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-($Message.Length+8))) -NoNewline 55 | Write-Host -ForegroundColor Green " OK " -NoNewLine 56 | Write-Host "]" 57 | } 58 | "FAIL" { Write-Host ("`r {0}{1}[" -f $Message, "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-($Message.Length+8))) -NoNewline 59 | Write-Host -ForegroundColor Red "FAIL" -NoNewLine 60 | Write-Host "]" 61 | } 62 | "N/A" { Write-Host ("`r {0}{1}[ N/A]" -f $Message, "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-($Message.Length+8))) 63 | } 64 | default { Write-Host (" {0}{1}[ ]" -f $Message, "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-($Message.Length+8))) -NoNewline } 65 | } 66 | } 67 | 68 | ################################################################################ 69 | # INIT # 70 | ################################################################################ 71 | # Requires a SOUTHERNHEALTH account 72 | if ($env:USERDOMAIN -ne $UserDomain) 73 | { 74 | Write-Warning "Not logged in as $UserDomain account. Please login as your admin account to complete configuration" 75 | $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null 76 | break 77 | } 78 | 79 | # Script must be run as administrator 80 | If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) 81 | { 82 | $arguments = "& '" + $myinvocation.mycommand.definition + "'" 83 | Start-Process powershell -Verb runAs -ArgumentList $arguments 84 | Break 85 | } 86 | 87 | # Check if there is a newer version of the script 88 | if ((Get-ScriptVersion $LocalScript) -lt (Get-ScriptVersion $script)) 89 | { 90 | Write-Warning "Newer version available!" 91 | Copy-Item $Script $LocalScript 92 | & $LocalScript 93 | break 94 | } 95 | 96 | ################################################################################ 97 | # INFO GATHERING # 98 | ################################################################################ 99 | ## Header # 100 | Write-Host "Monash Health Post-deployment configuration" 101 | 102 | ## Gather Information 103 | $Prod = New-Object System.Management.Automation.Host.ChoiceDescription "&Prod", "Production" 104 | $Test = New-Object System.Management.Automation.Host.ChoiceDescription "&Test", "Test" 105 | $options = [System.Management.Automation.Host.ChoiceDescription[]]($Prod, $Test) 106 | $result = $host.ui.PromptForChoice("Choose environment", "Is the server Production or Test?", $options, 0) 107 | switch ($result) 108 | { 109 | 0 { $Environment = "Prod"; } 110 | 1 { $Environment = "Test"; } 111 | } 112 | 113 | # Get the Server description for BGInfo 114 | $ServerDesc = Read-Host "Server Description" 115 | 116 | Clear-Host 117 | Write-Host "" 118 | Write-Host "" 119 | 120 | ################################################################################ 121 | # FUNCTION # 122 | ################################################################################ 123 | ######## Active Directory ######## 124 | # Move the AD Object 125 | Write-Status "Moving AD Object" 126 | Try 127 | { 128 | Import-Module ActiveDirectory 129 | Get-ADComputer $env:ComputerName | Move-ADObject -TargetPath $Config[$Environment].OU 130 | Write-Status "Moving AD Object" "OK" 131 | } 132 | catch 133 | { 134 | Write-Status "Moving AD Object" "FAIL" 135 | } 136 | 137 | ## Update Group Policy ## 138 | Write-Status "Updating Group Policy" 139 | $r = Start-Process -FilePath "C:\Windows\System32\gpupdate.exe" -ArgumentList "/Force" -NoNewWindow -Wait -RedirectStandardOutput null -PassThru 140 | if ($r.ExitCode -eq 0) 141 | { 142 | Write-Status "Updating Group Policy" "OK" 143 | } 144 | else 145 | { 146 | Write-Status "Updating Group Policy" "FAIL" 147 | } 148 | 149 | ######## Server Description ######## 150 | Write-Status "Setting Server Description" 151 | $os = Get-WmiObject Win32_OperatingSystem 152 | $os.Description = $ServerDesc 153 | $os.Put() | Out-Null 154 | Write-Status "Setting Server Description" "OK" 155 | 156 | ######## Drives ######## 157 | # Change drive letter of all CD drives, starting with Z: and work backwards 158 | Write-Status "Changing CD Drive letters" 159 | Try 160 | { 161 | $i = 0; 162 | Get-WmiObject Win32_cdromdrive | %{ $a = (mountvol $_.drive /l).Trim(); mountvol $_.drive /d; mountvol (([char](122-$i++)).ToString()+':') $a} 163 | # Wait for a bit - otherwise the drive letter not always available 164 | Start-Sleep -Seconds 5 165 | Write-Status "Changing CD Drive letters" "OK" 166 | } 167 | catch 168 | { 169 | Write-Status "Changing CD Drive letters" "FAIL" 170 | } 171 | 172 | # Use the Storage cmdlets if available 173 | Write-Host " Initialising Disks" -NoNewline 174 | if (@(Get-Command Get-Disk -ErrorAction SilentlyContinue).Count -gt 0) 175 | { 176 | # Initialize and format any extra disks 177 | $dataDisks = Get-Disk | Where partitionstyle -eq 'raw' 178 | 179 | if ($dataDisks -gt 1) 180 | { 181 | $i = 0 182 | foreach ($disk in $dataDisks) 183 | { 184 | Write-Status (" Initialising Disk {0}:" -f ([char](100+$i))) 185 | $disk | Initialize-Disk -PartitionStyle GPT -PassThru | New-Partition -UseMaximumSize | Format-Volume -FileSystem NTFS -Confirm:$false | Out-Null 186 | 187 | # Assign a drive letter - do this second otherwise we get an annoying prompt to format the disks 188 | $disk | Get-Partition | Where { $_.Type -eq "Basic" } | Set-Partition -NewDriveLetter ([char](100+$i)) 189 | Write-Status (" Initialising Disk {0}:" -f ([char](100+$i))) "OK" 190 | $i++ 191 | } 192 | } 193 | else 194 | { 195 | # No Data disks 196 | Write-Status "Initialising Disks" "N/A" 197 | } 198 | } 199 | else 200 | { 201 | Write-Warning "Storage cmdlets not available, you must be deploying an old OS" 202 | Write-Status "Initialising Disks" "N/A" 203 | } 204 | 205 | ########## Software ########## 206 | ## Install SCOM 207 | if ($Environment -eq "Prod") 208 | { 209 | Write-Status "Installing SCOM Agent" 210 | . $Env:WinDir\System32\msiexec.exe /i "\\Server\Share\SCOM-latest\MOMAgent.msi" /qn USE_SETTINGS_FROM_AD=0 USE_MANUALLY_SPECIFIED_SETTINGS=1 MANAGEMENT_GROUP=SOUTHERNHEALTH MANAGEMENT_SERVER_DNS=SHCLASCOAPP01 SECURE_PORT=5723 ACTIONS_USE_COMPUTER_ACCOUNT=1 AcceptEndUserLicenseAgreement=1 211 | Write-Status "Installing SCOM Agent" "OK" 212 | } 213 | 214 | ## Regenerate WSUS Client ID - 2008 seems to occasionally have trouble with WSUS otherwise 215 | if ((Get-WMIObject win32_OperatingSystem).Caption -match "Server 2008") 216 | { 217 | Write-Status "Fix 2008 WSUS Settings" 218 | Stop-Service wuauserv 219 | Remove-ItemProperty HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate SusClientID 220 | Remove-ItemProperty HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate AccountDomainSid 221 | Remove-ItemProperty HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate TargetGroup 222 | Remove-ItemProperty HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate TargetGroupEnabled 223 | Start-Service wuauserv 224 | Write-Status "Fix 2008 WSUS Settings" "OK" 225 | } 226 | 227 | ## Install Updates 228 | Write-Status "Searching for Windows Updates" 229 | # Search for relevant updates. 230 | $SearchResult = (New-Object -ComObject Microsoft.Update.Searcher).Search("IsInstalled=0 and Type='Software'").Updates 231 | Write-Status "Searching for Windows Updates" "OK" 232 | 233 | 234 | if ($SearchResult) 235 | { 236 | Write-Status ("Installing Windows Updates [{0}]" -f $SearchResult.Count) 237 | 238 | # Download updates. 239 | Write-Status "Downloading Updates" 240 | $Downloader = ( New-Object -ComObject Microsoft.Update.Session).CreateUpdateDownloader() 241 | $Downloader.Updates = $SearchResult 242 | $Downloader.Download() | Out-Null 243 | Write-Status " Downloading Updates" "OK" 244 | 245 | # Install updates. 246 | Write-Status " Installing Updates" 247 | $Installer = New-Object -ComObject Microsoft.Update.Installer 248 | $Installer.Updates = $SearchResult 249 | $Result = $Installer.Install() | Out-Null 250 | Write-Status ("Installing Windows Updates [{0}]" -f $SearchResult.Count) "OK" 251 | } 252 | else 253 | { 254 | Write-Status "Installing Windows Updates" "N/A" 255 | } 256 | 257 | ## Set Reg keys for Prod Servers 258 | # Set WSUS Group 259 | if ($Environment -eq "Prod") 260 | { 261 | Set-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate -Name TargetGroup $Config[$Environment].WSUSDefault 262 | } 263 | 264 | ## Add to RDP Timeout group by default 265 | foreach ($grp in $Config[$Environment].memberOf) 266 | { 267 | Add-ADGroupMember -Identity $grp -Members ("{0}$" -f $Env:Computername) 268 | } 269 | 270 | Write-Host "" 271 | Write-Host "Remember to:" 272 | Write-Host " * Create Wiki page" 273 | Write-Host " * Approve SCOM Agent" 274 | Write-Host " * Add to Hobbit" 275 | Write-Host " * Add to Backups" 276 | 277 | ################################################################################ 278 | # FINALISE # 279 | ################################################################################ 280 | # Remove from Run regkey 281 | Remove-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run DeployScript 282 | # Delete Script 283 | Remove-Item $LocalScript 284 | 285 | # Reboot 286 | $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Reboots the computer" 287 | $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Do not reboot" 288 | $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) 289 | $result = $host.ui.PromptForChoice("Reboot Required", "Do you want to reboot now?", $options, 0) 290 | 291 | if ($result -eq 0) 292 | { 293 | Restart-Computer 294 | } 295 | 296 | <# 297 | CHANGELOG 298 | --------- 299 | 1.0.0 - Initial version 300 | Configurations applied 301 | * AD Object move 302 | * Change CD Drive letter to Z: 303 | * Initialise Data disks (2012+ only at this stage) 304 | * SCOM Agent Install 305 | * Update Group Policy 306 | * Install Updates 307 | 1.1.0 - Bugs fixed 308 | * Suppress additional output from WMI set 309 | Enhancements 310 | * Better feedback on Windows Update progress (no hang on search) 311 | 1.1.1 - Added Additional steps reminder 312 | 1.2.0 - Add memberOf config to add computer object to groups 313 | 1.2.1 - Add to ServerAutoWeek2 group 314 | #> 315 | -------------------------------------------------------------------------------- /Exchange/Start-MailSearch.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Pretty GUI wrapper around mailbox search. 4 | .DESCRIPTION 5 | This script aims to make Exchange searches slightly less painful by wrapping 6 | the Search-Mailbox cmdlet in a pretty GUI. 7 | .NOTES 8 | File Name : Start-MailSearch 9 | Author : John Sneddon 10 | Version : 1.5.1 11 | 12 | CHANGELOG 13 | 1.0 - Initial script. Taken from Dave's script, and wrapped a nice UI around it 14 | 1.1 - Add a checkbox to actually do the deletecontent 15 | 1.2 - Added Search Criteria 16 | 1.3 - Add Option to run with delete if only searching. Hide the PS window. 17 | 1.4.0 - Add Sender search Criteria 18 | 1.5.0 - Add Date 19 | 1.5.1 - Add Date range options, slight refactor of code 20 | 21 | .INPUTS 22 | No inputs required 23 | .OUTPUTS 24 | No outputs 25 | #> 26 | Function Write-Status ($Message, $Status) 27 | { 28 | switch ($Status) 29 | { 30 | "OK" { Write-Host ("`r {0}{1}[" -f $Message, "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-($Message.Length+8))) -NoNewline 31 | Write-Host -ForegroundColor Green " OK " -NoNewLine 32 | Write-Host "]" 33 | } 34 | "FAIL" { Write-Host ("`r {0}{1}[" -f $Message, "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-($Message.Length+8))) -NoNewline 35 | Write-Host -ForegroundColor Red "FAIL" -NoNewLine 36 | Write-Host "]" 37 | } 38 | "N/A" { Write-Host ("`r {0}{1}[ N/A]" -f $Message, "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-($Message.Length+8))) 39 | } 40 | default { Write-Host (" {0}{1}[ ]" -f $Message, "".PadRight((Get-Host).ui.RawUI.WindowSize.Width-($Message.Length+8))) -NoNewline } 41 | } 42 | } 43 | 44 | function Show-PSWindow 45 | { 46 | $Form.Hide() 47 | [UIWin32.Methods]::ShowWindow($Handle, 5) | Out-Null 48 | } 49 | 50 | function Show-SearchDialog 51 | { 52 | #[UIWin32.Methods]::ShowWindow($Handle, 0) | out-null 53 | $Form.ShowDialog() | out-null 54 | } 55 | 56 | function Invoke-MailboxSearch 57 | { 58 | Param ( 59 | [string[]]$SearchIdentityList, 60 | [string]$SearchQuerySubject, 61 | [string]$SearchBody, 62 | [string]$SearchSender, 63 | [string]$SearchAttach, 64 | [string]$TargetMailbox, 65 | [string]$TargetMailboxFolder, 66 | $DateFrom, 67 | $DateTo, 68 | [Bool]$DeleteContent 69 | ) 70 | Show-PSWindow 71 | Write-Host "Starting Search..." 72 | 73 | $SearchIdentityList = $SearchIdentityList -split "`r`n" 74 | 75 | # Search Query - Build from supplied text 76 | $SearchQueryString = "" 77 | if ($SearchQuerySubject) 78 | { 79 | $SearchQueryString += ('AND Subject:"{0}" ' -f $SearchQuerySubject) 80 | } 81 | if ($SearchBody) 82 | { 83 | $SearchQueryString += ('AND Body:"{0}" ' -f $SearchBody) 84 | } 85 | if ($SearchSender) 86 | { 87 | $SearchQueryString += ('AND From:"{0}" ' -f $SearchSender) 88 | } 89 | if ($SearchAttach) 90 | { 91 | $SearchQueryString += ('AND attachment={0} ' -f $SearchAttach) 92 | } 93 | 94 | # Do some basic validation on dates 95 | if ($DateFrom -gt $DateTo) 96 | { 97 | $DateFrom = (Get-Date ($DateTo)).AddDays(-1) 98 | } 99 | if ($DateTo -lt $DateFrom) 100 | { 101 | $DateTo = (Get-Date ($DateFrom)).AddDays(1) 102 | } 103 | 104 | if ($DateFrom -eq $DateTo) 105 | { 106 | $SearchQueryString += ('AND Received:{0}' -f ($DateFrom.ToString("yyyy-MM-dd"))) 107 | } 108 | else 109 | { 110 | $SearchQueryString += ('AND Received:{0}..{1} ' -f ($DateFrom.ToString("yyyy-MM-dd"), $DateTo.ToString("yyyy-MM-dd"))) 111 | } 112 | 113 | # Trim off the extra "AND" 114 | $SearchQueryString = $SearchQueryString.Trim("AND ") 115 | 116 | Write-Verbose "Searching: $SearchQueryString" 117 | 118 | Write-Host "Resolving unique mailboxes..." 119 | $SearchMailbox = @() 120 | $SearchIdentityList | Sort-Object -Unique | %{$SearchMailbox += Get-Mailbox $_ -ErrorAction SilentlyContinue } 121 | 122 | # Cycle through the identities to search. Check deletion is on/off and -force is on/off depending on whether you want to be 123 | # prompted for deletion. Deletion is -DeleteContent 124 | $i = 0; 125 | $totalRemoved = 0 126 | if ($SearchMailbox.count -gt 0) 127 | { 128 | foreach ($SearchIdentity in $SearchMailbox) 129 | { 130 | try 131 | { 132 | $PercentComplete = ($i*100/$SearchIdentityList.Count) 133 | Write-Progress -Activity "Searching Mailboxes" -Status ("{0}% - {1}" -f $PercentComplete, $SearchIdentity) -PercentComplete $PercentComplete 134 | if ($DeleteContent) 135 | { 136 | $result = Search-Mailbox -identity $SearchIdentity -SearchQuery $SearchQueryString -targetmailbox $TargetMailbox -targetfolder $TargetMailboxFolder -loglevel Full -deletecontent -force -WarningAction SilentlyContinue 137 | } 138 | else 139 | { 140 | $result = Search-Mailbox -identity $SearchIdentity -SearchQuery $SearchQueryString -targetmailbox $TargetMailbox -targetfolder $TargetMailboxFolder -loglevel Full -LogOnly -force -WarningAction SilentlyContinue 141 | } 142 | Write-Host ("{0}: {1} items found" -f $SearchIdentity.Name, $result.ResultItemsCount) 143 | $totalRemoved += $result.ResultItemsCount 144 | } 145 | catch 146 | { 147 | # Do nothing, just suppress the error 148 | Write-Error "Search Failed with query: $SearchQueryString" 149 | } 150 | $i++ 151 | } 152 | } 153 | Write-Progress -Activity "Searching Mailboxes" -Status "Done" -Completed 154 | 155 | Write-Host "-------------------------------------" 156 | Write-Host ("{0} Email addresses in source list" -f $SearchIdentityList.count) 157 | Write-Host ("{0} Unique addresses" -f ($SearchIdentityList | Sort-Object -Unique).count) 158 | Write-Host ("{0} valid Mailboxes" -f $SearchMailbox.count) 159 | Write-Host ("{0} items found" -f $totalRemoved) 160 | Write-Host "" 161 | if (-not $DeleteContent) 162 | { 163 | Write-Host -ForegroundColor Yellow "********* Search only - Emails not deleted *********" 164 | 165 | $Yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Yes" 166 | $No = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "No" 167 | $Update = New-Object System.Management.Automation.Host.ChoiceDescription "&Update", "Update Search" 168 | $options = [System.Management.Automation.Host.ChoiceDescription[]]($Yes, $No, $Update) 169 | $result = $host.ui.PromptForChoice("Do you want to run the search again and delete content?", "Delete?", $options, 0) 170 | switch ($result) 171 | { 172 | 0 { # Run it again with delete 173 | Invoke-MailboxSearch $txtSearchMB.Text ` 174 | $txtSearchString.Text ` 175 | $txtSearchBody.Text ` 176 | $txtSearchSender.Text ` 177 | $txtSearchAttach.Text ` 178 | $txtTgtMB.Text ` 179 | $txtTgtFolder.Text ` 180 | $dpDateFrom.SelectedDate ` 181 | $dpDateTo.SelectedDate ` 182 | $true 183 | } 184 | 1 { <# Do nothing #> } 185 | 2 { Show-SearchDialog } 186 | } 187 | } 188 | else 189 | { 190 | $Yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Yes" 191 | $No = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "No" 192 | $options = [System.Management.Automation.Host.ChoiceDescription[]]($No, $Yes) 193 | $result = $host.ui.PromptForChoice("Do you want to run another search?", "Run another?", $options, 0) 194 | switch ($result) 195 | { 196 | 0 { Exit } 197 | 1 { Show-SearchDialog } 198 | } 199 | } 200 | } 201 | 202 | ################################################################################ 203 | # INITIALISATION # 204 | ################################################################################ 205 | Clear-Host 206 | Write-Status "Loading Requirements..." 207 | 208 | # Add WPF Type 209 | Add-Type -AssemblyName PresentationFramework 210 | 211 | # Add the Exchange Snapin if not already loaded, so you don't have to open the exchange shell 212 | if (!(Get-PSSnapin -name Microsoft.Exchange.Management.PowerShell.SnapIn -ErrorAction SilentlyContinue) -and 213 | (Get-PSSnapin -Name Microsoft.Exchange.Management.PowerShell.SnapIn -ErrorAction SilentlyContinue -Registered).Count -gt 0) 214 | { 215 | Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn 216 | } 217 | Write-Status "Loading Requirements..." "OK" 218 | 219 | # Get the window handle to enable show/hide 220 | Add-Type -Name "Methods" -Namespace "UIWin32" -PassThru -MemberDefinition '[DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);' | Out-Null 221 | $Handle = (Get-Process -id $Pid).MainWindowHandle 222 | 223 | 224 | ################################################################################ 225 | # GUI # 226 | ################################################################################ 227 | # Use XAML to define the form, data to be populated in code 228 | [xml]$XAML_Main = @" 229 | 233 | 234 | 241 | 242 | 243 | 244 |