Skip to content

Instantly share code, notes, and snippets.

@neztach
Created February 8, 2023 21:12
Show Gist options
  • Save neztach/4f04ee3ff54ae92194bd22d3b9bcc576 to your computer and use it in GitHub Desktop.
Save neztach/4f04ee3ff54ae92194bd22d3b9bcc576 to your computer and use it in GitHub Desktop.
profile
#Version 2.5
Clear-Host
#region Paths
$env:PSModulePath = $env:PSModulePath + ';C:\Program Files\WindowsPowerShell\Modules'
#endregion
If ($env:USERNAME -like '*step*'){$amJames = $true}
#region Locations
If ($amJames -eq $true){
Set-Location -Path "$env:SystemDrive\down"
} Else {
Set-Location -Path "$env:SystemDrive\"
}
#endregion
#region Prompt Functions
Function Test-Administrator {
<#
.SYNOPSIS
Checks if you are running as Administrator.
.DESCRIPTION
Tests if you are currently running powershell as administrator.
.EXAMPLE
Test-Administrator
If yes, returns true, if not, returns false
#>
# PowerShell 5.x only runs on Windows so use .NET types to determine isAdminProcess
# Or if we are on v6 or higher, check the $IsWindows pre-defined variable.
If (($PSVersionTable.PSVersion.Major -le 5) -or $IsWindows) {
$currentUser = [Security.Principal.WindowsPrincipal]([Security.Principal.WindowsIdentity]::GetCurrent())
Return $currentUser.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
# Must be Linux or OSX, so use the id util. Root has userid of 0.
Return 0 -eq (id -u)
}
Function Get-Elevation {
<#
.SYNOPSIS
Sets title value based on administrator status.
.DESCRIPTION
Sets title value based on administrator status.
.EXAMPLE
Get-Elevation
Sets title value to 'W.O.P.R.' if admin, stes to 'Joshua' if not
#>
[CmdletBinding()]
Param ()
If (Test-Administrator) {
#$script:elevation = "Admin"
$elevation = 'W.O.P.R.'
Write-Verbose -Message ('Powershell is running as: {0}' -f $elevation)
} Else {
#$script:elevation = "Non-Admin"
$elevation = 'Joshua'
Write-Verbose -Message ('Powershell is running as: {0}' -f $elevation)
}
Return $elevation
}
Function Set-WindowTitle {
<#
.SYNOPSIS
Gets computer name and adds to Get-Elevation result to finally set the window title.
.DESCRIPTION
Gets computer name and adds to Get-Elevation result to finally set the window title.
.EXAMPLE
Set-WindowTitle
Sets the window title.
#>
[CmdletBinding()]
Param ()
$host_title = [ordered]@{
'Session' = "$env:COMPUTERNAME".ToUpper()
'Elevation' = Get-Elevation
#'Version' = "v$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor)"
}
$formatted_title = "[$($host_title.Values -join ': ')]"
Write-Verbose -Message ("Setting Window Title to '{0}'" -f $formatted_title)
$Host.UI.RawUI.WindowTitle = $formatted_title
}
Function Copy-LastCommand {
<#
.SYNOPSIS
Looks through command history to copy the last command to clipboard.
.DESCRIPTION
Looks through command history to copy the last command to clipboard
.EXAMPLE
Copy-LastCommand
Looks through command history to copy the last command to clipboard
#>
Get-History -Id $(((Get-History) | Select-Object -Last 1 | Select-Object -Property ID -ExpandProperty ID)) |
Select-Object -ExpandProperty CommandLine |
& "$env:windir\system32\clip.exe"
}
New-Alias -Name copylast -Value Copy-LastCommand
Function Edit-Profile {
<#
.SYNOPSIS
Shortcut command to edit your $profile.
.DESCRIPTION
Shortcut command to edit your $profile.
.EXAMPLE
Edit-Profile
Opens your profile in Powershell_ISE
#>
If ($host.Name -match 'ise') {
$psISE.CurrentPowerShellTab.Files.Add($profile)
} Else {
& "$env:windir\system32\WindowsPowerShell\v1.0\PowerShell_ISE.exe" $profile
}
}
Function Open-HistoryFile {
<#
.SYNOPSIS
Opens yourcommand history in powershell_ise.
.DESCRIPTION
Opens your command history in powershell_ise.
.EXAMPLE
Open-HistoryFile
Opens your command history in powershell_ise
#>
& "$env:windir\system32\WindowsPowerShell\v1.0\powershell_ise.exe" (Get-PSReadLineOption | Select-Object -ExpandProperty HistorySavePath)
}
#endregion Prompt Functions
Try {
Import-Module -Name ActiveDirectory
} Catch {
Write-Warning -Message 'No ActiveDirectory Module'
}
$script:PDC = (Get-AdDomainController -Filter {OperationMasterRoles -like '*PDCEmulator*'}).HostName
#region Functions
#region Helpers
enum Colour {
Bk
DkBl
DkGr
DkCy
DkRe
DkMa
DkYe
Gy
DkGy
Bl
Gr
Cy
Re
Ma
Ye
Wh
}
Function Write-Short {
<#
.SYNOPSIS
Write-Short is a short-hand substitution for Write-Host.
.DESCRIPTION
Attempts to create a rudamentary method of using Write-Host with common parameters.
Valid Color Values:
* Bl = Black
* DkBl = DarkBlue
* DkGr = DarkGreen
* DkCy = DarkCyan
* DkRe = DarkRed
* DkMa = DarkMagenta
* DkYe = DarkYellow
* Gy = Gray
* DkGy = DarkGray
* Bl = Blue
* Gr = Green
* Cy = Cyan
* Re = Red
* Ma = Magenta
* Ye = Yellow
* Wh = White
.PARAMETER t
Text you would normally pass to Write-Host.
.PARAMETER b
BackgroundColor - Shorthand List:
.PARAMETER f
ForegroundColor - Shorthand: see BackgroundColor
.PARAMETER n
NoNewLine.
.EXAMPLE
WH -t 'This is a test' -b White -n
- eq -
Write-Host 'This is a test' -BackgroundColor White -NoNewLine
.EXAMPLE
WH -t 'This is a Color Test' -b Bl -f Cy -n
- eq -
Write-Host 'This is a Color Test' -BackgroundColor Black -ForegroundColor Cyan -NoNewLine
.INPUTS
String
.OUTPUTS
String
#>
[CmdletBinding()]
[Alias('WH')]
Param (
### Object normally sent to Write-Host (text)
[Parameter(
Mandatory = $true,
HelpMessage = 'Text'
)]
[ValidateNotNullOrEmpty()]
[Object]$t,
### BackgroundColor
[Colour]$b = $null,
### ForegroundColor
[Colour]$f = $null,
### NoNewLine
[Switch]$n
)
### Creating our Splat for Write-Host
$WriteObject = @{Object = $t}
Function Get-Color {
<#
.SYNOPSIS
Choose a Color.
#>
[CmdletBinding()]
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'Valid Colour Option'
)]
[Colour]$Color
)
Switch ($Color) {
'Bk' { $ColorChosen = 'Black' }
'DkBl' { $ColorChosen = 'DarkBlue' }
'DkGr' { $ColorChosen = 'DarkGreen' }
'DkCy' { $ColorChosen = 'DarkCyan' }
'DkRe' { $ColorChosen = 'DarkRed' }
'DkMa' { $ColorChosen = 'DarkMagenta'}
'DkYe' { $ColorChosen = 'DarkYellow' }
'Gy' { $ColorChosen = 'Gray' }
'DkGy' { $ColorChosen = 'DarkGray' }
'Bl' { $ColorChosen = 'Blue' }
'Gr' { $ColorChosen = 'Green' }
'Cy' { $ColorChosen = 'Cyan' }
'Re' { $ColorChosen = 'Red' }
'Ma' { $ColorChosen = 'Magenta' }
'Ye' { $ColorChosen = 'Yellow' }
'Wh' { $ColorChosen = 'White' }
default { $ColorChosen = $Color }
}
return $ColorChosen
}
### -BackgroundColor
If ($b) {$WriteObject.BackgroundColor = Get-Color -Color $b}
### -ForegroundColor
If ($f) {$WriteObject.ForegroundColor = Get-Color -Color $f}
### -NoNewLine
If ($n) {$WriteObject.NoNewLine = $true}
### Output our splat of Write-Host
Write-Host @WriteObject
}
Function Get-Container {
<#
.SYNOPSIS
Translates a CanonicalName to more of a container.
.DESCRIPTION
Translates a CanonicalName to a container to make it easier to read.
.PARAMETER can
CanonicalName
.EXAMPLE
Get-Container -can (Get-ADUser testuser -prop CanonicalName).CanonicalName
Turns the DistinguishedName of the OU the user is in to more of a container:
Before: CN=testuser,OU=CompanyOU,OU=Users,DC=Domain,DC=LOCAL
After : Domain.LOCAL/CompanyOU/Users
.INPUTS
String
.OUTPUTS
String
#>
[Alias('container')]
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'CanonicalName required'
)]
[ValidateNotNullOrEmpty()]
[String]$can
)
$container = $can -ireplace '\/[^\/]+$',''
Return $container
}
Function Convert-StandardPhone {
<#
.SYNOPSIS
Convert value to Phone format.
.DESCRIPTION
Determine how many digits are in input and format to proper telephone syntax.
.PARAMETER NumtoConv
Number to convert.
.EXAMPLE
Convert-StandardPhone -NumtoConv 1234567
123-4567
.EXAMPLE
Convert-StandardPhone -NumtoConv 1234567890
(123) 456-7890
.INPUTS
String
.OUTPUTS
String
#>
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'Phone Number required'
)]
[ValidateNotNullOrEmpty()]
[String]$NumtoConv
)
If ($NumtoConv -notmatch 'Last|ADSI|N/A'){
$pnum = $NumtoConv -replace '[^0-9]',''
$pnum = $pnum -replace '^0' -replace '^1' -replace '\s' -as [LONG]
$pnumlength = ($pnum | Measure-Object -Character).Characters
If ($pnumlength -eq 7) {
$newPhoneNumber = '{0:###-####}' -f ([long]$pnum)
} ElseIf ($pnumlength -eq '11') {
$pnum = $pnum.Substring(1)
$newPhoneNumber = '{0:(###) ###-####}' -f ([long]$pnum)
} ElseIf ($pnumlength -eq 10) {
$newPhoneNumber = '{0:(###) ###-####}' -f ([long]$pnum)
} Else {
$newPhoneNumber = $NumtoConv
}
} Else {
$newPhoneNumber = $null
}
Return $newPhoneNumber
}
Function Initialize-File {
<#
.SYNOPSIS
Creates an empty file
.DESCRIPTION
Creates an empty file
.PARAMETER <full path and name of file with extension>
Define the file to make
.EXAMPLE
Touch "C:\down\test.txt"
.INPUTS
String
.OUTPUTS
File
#>
[Alias('Touch')]
Param (
[Parameter(Position = 0)]
[String]$file = $args[0]
)
If ($file -eq $null) {
Throw 'No filename supplied'
}
If (Test-Path -Path $file) {
(Get-ChildItem -Path $file).LastWriteTime = Get-Date
} Else {
Write-Output -InputObject $null > $file
}
}
Function Get-IPValid {
<#
.SYNOPSIS
Test if an IP address is valid.
.DESCRIPTION
Tests if an IP address is in a valid syntax and in acceptable IP range.
.PARAMETER testip
IP to test.
.EXAMPLE
Get-IPValid -testip '192.168.3.5'
.INPUTS
String
.OUTPUTS
Boolean
#>
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'IP Address'
)]
[ValidateNotNullOrEmpty()]
[String]$testip
)
If ($testip -ne '0.0.0.0') {
### REGEX Pattern
[regex]$pattern = '^(?:(?:1\d\d|2[0-5][0-4]|2[0-4]\d|0?[1-9]\d|0?0?\d)\.){3}(?:1\d\d|2[0-5][0-4]|2[0-4]\d|0?[1-9]\d|0?0?\d)$'
### Evaluate if the test IP matches the REGEX
If ($testip -match $pattern) {
return $true
} Else {
return $false
}
} Else {
return $false
}
}
Function Get-String {
<#
.SYNOPSIS
Get string behaves like NIX grep
.DESCRIPTION
Iterates through any inbound string looking for specified string
.EXAMPLE
Get-Content whatever.txt | Get-String STRING
.NOTES
https://adamtheautomator.com/powershell-grep/
.INPUTS
String
.OUTPUTS
Stream
#>
[Alias('grep')]
[CmdletBinding()]
Param (
[Parameter(Position = 0)]
[ValidateNotNullOrEmpty()]
[String]$in = $args[0],
[Switch]$v
)
If (-not $v){
$in | Out-String -Stream | Select-String -Pattern $args
} Else {
$skipping = $in | Out-String -Stream | Select-Object -Skip ($v + 1)
$skipping
}
}
Function Read-OpenFileDialog {
<#
.SYNOPSIS
An Open file GUI dialog
.DESCRIPTION
Open File GUI
.PARAMETER WindowTitle
Window Title
.PARAMETER InitialDirectory
Where you want the window to open from
.PARAMETER Filter
What kind of file filters you want
Examples:
* 'All files (*.*)|*.*'
* 'CSV files (*.csv)|*.csv'
* 'Excel files (*.xlsx)|*.xlsx'
.PARAMETER AllowMultiSelect
Allow user to select more than one thing.
.EXAMPLE
Read-OpenFileDialog
.INPUTS
String
.OUTPUTS
String
#>
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'WindowTitle required'
)]
[ValidateNotNullOrEmpty()]
[String]$WindowTitle,
[Parameter(
Mandatory = $true,
HelpMessage = 'Starting Directory required'
)]
[ValidateNotNullOrEmpty()]
[String]$InitialDirectory,
[ValidateSet('All files (*.*)|*.*','CSV files (*.csv)|*.csv','Excel files (*.xlsx)|*.xlsx','Text files (*.txt)|*.txt')]
[String]$Filter = 'All files (*.*)|*.*',
[Switch]$AllowMultiSelect
)
Add-Type -AssemblyName System.Windows.Forms
$openFileDialog = New-Object -TypeName System.Windows.Forms.OpenFileDialog
$openFileDialog.Title = $WindowTitle
If (![string]::IsNullOrWhiteSpace($InitialDirectory)){
$openFileDialog.InitialDirectory = $InitialDirectory
}
$openFileDialog.Filter = $Filter
If ($AllowMultiSelect){
$openFileDialog.MultiSelect = $true
}
$openFileDialog.ShowHelp = $true
$openFileDialog.ShowDialog() > $null
If ($AllowMultiSelect){
Return $openFileDialog.Filenames
} Else {
Return $openFileDialog.Filename
}
}
Function Get-Tail {
<#
.SYNOPSIS
Equivalent of NIX tail -f
.DESCRIPTION
Will monitor any kind of text readable log and show changes in real time
.EXAMPLE
Get-Tail c:\somelog.log
.INPUTS
String
.OUTPUTS
Stream
#>
Param (
[Parameter(
Mandatory = $True,
HelpMessage = 'Path to file to tail'
)]
[ValidateNotNullOrEmpty()]
[String]$filepath
)
If ([String]::IsNullOrEmpty($filePath)) {
$filePath = Read-OpenFileDialog -WindowTitle 'Select Text File Example' -InitialDirectory "$env:SystemDrive\"
} ElseIf (![String]::IsNullOrEmpty($filePath)){
Get-Content -Path ('{0}' -f $filePath) -Wait
} Else {
Write-Warning -Message 'You did not select a file.'
}
}
Function Optimize-HumanReadable {
<#
.SYNOPSIS
Convert raw file size to something human-readable
.DESCRIPTION
Convert raw size to something human readable
.EXAMPLE
gci .\file.xlsx | measure length -Sum | select @{n='Size';e={HR -size $_.Sum}}
.INPUTS
String
.OUTPUTS
String
#>
[CmdletBinding()]
[Alias('HR')]
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'directory or file size summary required'
)]
[ValidateNotNullOrEmpty()]
[int]$size
)
Switch ($size) {
{$_ -ge 1PB} {
"{0:#.#' PB'}" -f ($size / 1PB)
Break
}
{$_ -ge 1TB} {
"{0:#.#' TB'}" -f ($size / 1TB)
Break
}
{$_ -ge 1GB} {
"{0:#.#' GB'}" -f ($size / 1GB)
Break
}
{$_ -ge 1MB} {
"{0:#.#' MB'}" -f ($size / 1MB)
Break
}
{$_ -ge 1KB} {
"{0:#' KB'}" -f ($size / 1KB)
Break
}
default {
'{0:n0}' -f ($size) + ' Bytes'
}
}
}
Function Out-ExcelClip {
<#
.SYNOPSIS
Will take input and convert to excel ready data and copy to clipboard
.DESCRIPTION
Takes an array output and converts it to excel ready data and copies it to clipboard
.PARAMETER in
input can be something specified, otherwise it's whatever the incoming information stream is.
.PARAMETER delimiter
delimiter is used if you have information that is already delimited somehow that you're bringing in.
.EXAMPLE
Get-ChildItem C:\down | Out-ExcelClip
sends the output of get-childitem to your clipboard ready to be pasted into excel
.INPUTS
Array
.OUTPUTS
None
#>
[CmdletBinding()]
[Alias('clipXL')]
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'Need input',
ValueFromPipelineByPropertyName = $true,
ValueFromPipeline = $true,
Position = 0
)]
[ValidateNotNullOrEmpty()]
[Array]$in,
[String]$delimiter = ','
)
End {
($in | ConvertTo-Csv -Delimiter $delimiter -NoTypeInformation) -replace "\$delimiter",[char]9 | Set-Clipboard
}
}
Function Get-PathLength {
<#
.SYNOPSIS
Counts characters in a string.
.DESCRIPTION
Intended to count characters in a filename - Windows path length is 260.
.PARAMETER path
Any string - Doesn't have to be a path as it will count the characters anyway.
.EXAMPLE
Get-PathLength -path "\\DFSServer\shared\Clients\Directory One\Directory Two\Directory Three\Directory Four\Directory 5\FileName One.pdf"
114
.EXAMPLE
Get-PathLength -path "This is a test"
14
.INPUTS
String
.OUTPUTS
String
#>
[Alias('length')]
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'Any string'
)]
[ValidateNotNullOrEmpty()]
[String]$path
)
$length = ($path | Measure-Object -Character).Characters
If ($length -gt 260) {
WH -t $length -f Re
} ElseIf ($length -gt 200) {
WH -t $length -f Ye
} Else {
WH -t $length -f Gr
}
}
#endregion Helpers
#region Credentials
Function Get-MySAUser {
<#
.SYNOPSIS
Probes AD to get your SA username.
.DESCRIPTION
Uses AD to lookup all users with your name and choose your SA account.
.EXAMPLE
Get-MySAUser
.INPUTS
None
.OUTPUTS
String
#>
$me = $env:USERNAME
$Domain = $env:USERDOMAIN
$meAD = Get-ADUser -Identity $me -ErrorAction Stop
$toMatch = 'sa-'
If (($me -notmatch $toMatch)){
Write-Verbose -Message 'Running as SA user ...No.'
$sauser = $false
### Derive SA user SamAccountName
$find = $meAD.Name
$saun = (Get-ADUser -Filter {anr -eq $find} |
Where-Object {$_.SamAccountName -ne $env:USERNAME} |
Where-Object {$_.SamAccountName -match $toMatch}).SamAccountName
} Else {
Write-Verbose -Message 'Running as SA user ...Yes.'
$sauser = $true
}
If ($sauser) {
return $Domain + '\' + $me
} Else {
return $Domain + '\' + $saun
}
}
Function Test-Credential {
<#
.SYNOPSIS
Validate credential.
#>
[CmdletBinding()]
[OutputType([bool])]
Param (
[Parameter(
Mandatory = $True,
HelpMessage = 'Username'
)]
[ValidateNotNullOrEmpty()]
[string]$UserName,
[Parameter(
Mandatory = $True,
HelpMessage = 'Password'
)]
[ValidateNotNullOrEmpty()]
[string]$PW,
[ValidateSet('Domain','Machine')]
[string]$Context = 'Domain'
)
Begin {
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$ctx = New-Object -TypeName DirectoryServices.AccountManagement.PrincipalContext -ArgumentList ($context)
}
Process {
return $ctx.ValidateCredentials($UserName, $PW)
}
End {
$ctx.Dispose()
}
}
Function Clear-SavedCredential {
<#
.SYNOPSIS
Remove config file.
#>
[CmdletBinding()]
Param()
If (Test-Path -Path $CredentialsFile) {
#Remove credential file
Remove-Item -Path $CredentialsFile -Force
}
}
Function Register-SACreds {
<#
.SYNOPSIS
Save your SA credentials in a local file.
.DESCRIPTION
Securely save SA credentials into a CLIXML file.
.EXAMPLE
Register-SACreds
.INPUTS
None
.OUTPUTS
String
#>
If ((Get-Content -Path $profile | Out-String -Stream | Select-Object -Last 1) -notlike '*Credential*') {
WH -t 'Upon relaunch we will add the ability to store your SA credentials to file for future use.'
$AddtoProfile = @'
New-Variable -Name CredentialsFile -Value (Join-Path -Path $env:USERPROFILE -ChildPath 'MyVault.cred') -Scope Script -Force
### Create credential file and/or load creds to persistent memory
If (-not (Test-Path -Path $CredentialsFile)) {
Set-SavedCredential -Id "MySAUser" -UserName (Get-MySAUser)
Start-Sleep -Seconds 1
$script:Credential = Get-SavedCredential -Id "MySAUser"
} Else {$script:Credential = Get-SavedCredential -Id "MySAUser"}
$PSDefaultParameterValues.Add("*:Credential",$Credential)
'@
$AddtoProfile >> $profile
} Else {
WH -t 'MySAUser already in profile'
}
WH -t 'Relaunch Powershell and be prompted to enter your SA credentials to be saved to file' -b Bl -f Ye
pause
}
Function Set-SavedCredential() {
<#
.SYNOPSIS
Set credential information to config file.
.DESCRIPTION
Add a more complete description of what the function does.
.PARAMETER Id
Name for Credential.
.PARAMETER UserName
UserName to store.
.PARAMETER Password
Password to Store.
.PARAMETER Validate
Test credentials.
.EXAMPLE
Set-SavedCredential -Id Value -UserName Value -Password Value -Validate
Describe what this call does
.NOTES
Password in config file is encypted by Convert-FromSecureString (DAPI is used internally).
Config file is valid for LocalMachine/Currentuser context only.
.LINK
https://gist.github.com/altrive/6043181
The first link is opened by Get-Help -Online Set-SavedCredential
.EXAMPLE
Set-SavedCredential -Id "LocalAdmin" -UserName "localhost\Administrator" -Password "Password!"
.EXAMPLE
Set-SavedCredential -Id "FileShare" -UserName "localhost\FileShare" -Password "Password!"
.EXAMPLE
Set-SavedCredential -Id "DomainAdmin" -UserName "TEST\Administrator" -Password "Password!"
.EXAMPLE
Set-SavedCredential -Id "SqlAdmin" -UserName "TEST\sqlSvc" -Password "Password!"
.EXAMPLE
Set-SavedCredential -Id "SA" -UserName "SHERMCO\sa-somebody"
.INPUTS
List of input types that are accepted by this function.
.OUTPUTS
List of output types produced by this function.
#>
[CmdletBinding()]
Param (
[Parameter(
Mandatory = $True,
HelpMessage = 'ID'
)]
[ValidateNotNullOrEmpty()]
[String]$Id,
[Parameter(
Mandatory = $True,
HelpMessage = 'Username'
)]
[ValidateNotNullOrEmpty()]
[String]$UserName,
[Parameter(
Mandatory = $false,
HelpMessage = 'Password'
)]
[ValidateNotNullOrEmpty()]
[string]$PW,
[switch]$Validate
)
#Validate credential
If ($Validate) {
If ($UserName.StartsWith('localhost\')){
$validationContext = 'Machine'
} Else {
$validationContext = 'Domain'
}
$TestSplat = @{
UserName = $UserName
Password = $PW
Context = $validationContext
}
$isValid = Test-Credential @TestSplat
If (-not $isValid) {
Write-Error -Message ('Credential Validation Failed: {0}({1})' -f $Id, $UserName)
}
}
#Convert password to SecureString plain text
If ($PW) {
$securePassword = $PW | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString
} Else {
$securePassword = Get-Credential -Credential ($UserName)
}
#Get saved credential if exists
$credentials = @{}
If (Test-Path -Path $CredentialsFile) {
$credentials = Import-Clixml -Path $CredentialsFile
} Else {
#Ensure output directory exists
$baseDir = [IO.Path]::GetDirectoryName($CredentialsFile)
$null = New-Item -ItemType Directory -Path $baseDir -ErrorAction Ignore
}
#add/edit credential
$credentials[$Id] = @{
UserName = $UserName
Password = $securePassword
}
#Save Credentials as CliXml Format
$credentials | Export-Clixml -Path $CredentialsFile -Encoding UTF8 -Force
}
Function Show-SavedCredential {
<#
.SYNOPSIS
Show credentials list stored in config file.
#>
[CmdletBinding()]
[OutputType([hashtable])]
Param ()
If (!(Test-Path -Path $CredentialsFile)) {
Write-Error -Message 'Credential file not found!'
}
$result = Import-Clixml -Path $CredentialsFile -ErrorAction Ignore
return $result
}
Function Get-SavedCredential {
<#
.SYNOPSIS
Get credential information from config file. if no credential found, show credential enter dialog.
.EXAMPLE
$cred1 = Get-SavedCredential -Id "LocalAdmin"
$cred2 = Get-SavedCredential -Id "FileShare" -Validate
#>
[CmdletBinding()]
[OutputType([pscredential])]
Param (
[Parameter(Mandatory,HelpMessage='Credential ID',Position=0)]
[ValidateNotNullOrEmpty()]
[String]$Id,
[Switch]$Validate
)
$credentials = Import-Clixml -Path $CredentialsFile -ErrorAction Ignore
If (($null -eq $credentials) -or !($credentials.ContainsKey($Id))) {
return Get-Credential -Message ('Enter Credential for {0}' -f $Id) #Don't save credential for temporary use.
}
#Get saved credential
$result = $credentials[$Id]
#Convert to PSCredential
[String]$userName = $result.Values.UserName
#$securePassword = ConvertTo-SecureString -String $result.Password
$securePassword = $result.Values.Password
$cred = New-Object -TypeName System.Management.Automation.PsCredential -ArgumentList ($userName, $securePassword)
#Validate Credential
If ($Validate) {
If ($cred.GetNetworkCredential().Domain -eq 'localhost') {
$validationContext = 'Machine'
} Else {
$validationContext = 'Domain'
}
$TestSplat = @{
UserName = $cred.UserName
Password = $cred.GetNetworkCredential().Password
Context = $validationContext
}
#$isValid = Test-Credential -UserName $cred.UserName -Password $cred.GetNetworkCredential().Password -Context $validationContext
$isValid = Test-Credential @TestSplat
If (!$isValid) {
Write-Error -Message ('Credential Validation Failed: {0}({1})' -f $Id, $userName)
}
}
return $cred
}
Function Get-SavedPass {
<#
.SYNOPSIS
Get saved password as plain text to clipboard.
#>
[CmdletBinding()]
[Alias('ClipPass')]
Param (
[Parameter(
Mandatory = $True,
HelpMessage = 'Credential ID',
Position = 0
)]
[ValidateNotNullOrEmpty()]
[String]$Id
)
$credentials = Import-Clixml -Path $CredentialsFile -ErrorAction Ignore
If (($null -eq $credentials) -or !($credentials.ContainsKey($Id))) {
return Get-Credential -Message ('Enter Credential for {0}' -f $Id) #Don't save credential for temporary use.
}
#Get saved credential
$result = $credentials[$Id]
$securePassword = $result.Values.Password
$PasswordPlain = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePassword))
$PasswordPlain | Set-Clipboard
}
### Create credential file and/or load creds to persistent memory
<#
$MySAUser = 'MySAUser'
If (-not (Test-Path -Path $CredentialsFile)) {
Set-SavedCredential -Id $MySAUser -UserName (Get-MySAUser)
Start-Sleep -Seconds 1
$script:Credential = Get-SavedCredential -Id $MySAUser
} Else {
$script:Credential = Get-SavedCredential -Id $MySAUser
}
$PSDefaultParameterValues.Add('*:Credential',$Credential)
#>
#endregion Credentials
#region Domain
Function Get-DC {
<#
.SYNOPSIS
Gets domain controllers
.DESCRIPTION
Gets domain controllers and their roles
.PARAMETER
thorough
Validates DNS and gets list using the old methd
.EXAMPLE
Get-DCs | Format-Table -AutoSize
.EXAMPLE
Get-DCs | ?{$_.Roles -ne $null} | FT
Get a list of all DCs but only return those with FSMO roles | format table
.INPUTS
None
.OUTPUTS
Array
#>
[CmdletBinding()]
[Alias('GetDCs')]
Param (
[Switch]$thorough
)
If ($thorough.IsPresent){
$rSel = @{
Property = 'Forest',
@{n='Name';e={$_.Name.Split('.')[0]}},
'OSVersion',
@{n='IP';e={[Net.Dns]::GetHostByName($_.Name).AddressList[0].IPAddressToString}},
'SiteName',
'Roles'
}
$results = ([DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()).DomainControllers | Select-Object @rSel
} Else {
$rSel = @{
Property = 'Forest',
'Name',
@{n='OSVersion';e={$_.OperatingSystem}},
@{n='IP';e={$_.IPv4Address}},
@{n='SiteName';e={$_.Site}},
@{n='Roles';e={$_.OperationMasterRoles}}
}
$results = Get-AdDomainController -Filter * | Select-Object @rSel
}
Return $results
}
Function Get-DNS {
<#
.SYNOPSIS
Gets DNS controllers
.DESCRIPTION
Gets a list of DNS controllers in the domain
.EXAMPLE
Get-DNS | Sort IP
.INPUTS
None
.OUTPUTS
Array
#>
[Alias('GetDNS')]
$DNSServers = @()
$Results = ((& "$env:windir\system32\nltest.exe" /DnsGetDC:$env:USERDNSDOMAIN | Select-Object -Skip 2 | Select-Object -SkipLast 1).Trim() -Replace '\s+',',')
$Results | ForEach-Object {
If ($_ -notmatch 'WARNING|successfully'){
$DNSServers += [PSCustomObject][Ordered]@{
Name = $_.Split(',')[0].Split('.')[0].toUpper()
IP = $_.Split(',')[1]
}
}
}
$results = @()
$DNSServers | Group-Object -Property Name,IP | ForEach-Object {
$results += [PSCustomObject][Ordered]@{
Name = $_.Name.split(',')[0].trim()
IP = $_.Name.split(',')[1].trim()
}
}
Return $results
}
Function Find-PrintServers {
<#
.SYNOPSIS
Finds print servers on the network
.DESCRIPTION
Attempts to reach out to all windows servers to see if they have the Print-Services function
.EXAMPLE
Find-PrintServer
For every server it can reach, script determines if it has the Print-Services feature
installed. This will return a set of values for all matches with the following fields
DisplayName: Print and Document Services
ServerName :
IPAddress :
Location : <Physical location based on IPAddress>
.INPUTS
None
.OUTPUTS
Array
#>
[Alias('Find-PrintServer')]
$ErrorActionPreference = 'SilentlyContinue'
$InitialOutput = @()
$ScriptBlock = {Get-WindowsFeature -Name Print-Services | Where-Object {$_.Installed -match 'True'}}
$Servers = Get-ADComputer -Filter {Enabled -eq $true -AND OperatingSystem -like '*Windows Server*'} -Credential $Credential -Server $PDC |
Select-Object -ExpandProperty Name
$job = Try {
Invoke-Command -ComputerName $Servers -Credential $Credential -ScriptBlock $ScriptBlock -AsJob
} Catch {
Write-Error -Message "can't connect to $($_)"
}
While ((Get-Job -Id $job.Id).State -eq 'Running') {
Start-Sleep -MilliSeconds 500
}
$InitialOutput += Get-Job -Id $job.Id -IncludeChildJob | Receive-Job
Get-Job -Id $job.Id | Remove-Job
$PostProcessedOutput = @()
ForEach ($item in $InitialOutput){
$ServerIP = Get-ADComputer -Identity $item.PSComputerName -Credential $Credential -Properties IPv4Address -Server $PDC |
Select-Object -ExpandProperty IPv4Address
$PostProcessedOutput += [PsCustomObject][Ordered]@{
DisplayName = $item.DisplayName
ServerName = $item.PSComputerName
IPAddress = $ServerIP
}
}
Return $PostProcessedOutput
}
Function Get-EmptyGroupDetails {
<#
.SYNOPSIS
Get empty AD Groups.
.DESCRIPTION
Get all empty AD groups.
.EXAMPLE
Get-EmptyGroupDetails
.INPUTS
None
.OUTPUTS
Array
#>
Function Get-EmptyGroup {
<#
.SYNOPSIS
This function queries the Active Directory domain the initiaing computer is in for all groups that have no members.
This is common when attempting to find groups that can be removed.
This does not include default AD groups like Domain Computers, Domain Users, etc.
.EXAMPLE
PS> Get-EmptyGroup
#>
[OutputType([PSCustomObject])]
[CmdletBinding()]
Param ()
Begin {
$ErrorActionPreference = 'Stop'
}
Process {
Try {
@(Get-ADGroup -Filter * -Properties isCriticalSystemObject, Members).Where({(-not $_.isCriticalSystemObject) -and ($_.Members.Count -eq 0)})
} Catch {
$PSCmdlet.ThrowTerminatingError($_)
}
}
}
### Prep Variables
$EmptyGroupTotal = @()
$EmptyGroups = Get-EmptyGroup | Sort-Object -Property Name
$pi = 0
$Progress = @{
Activity = 'Working through Groups . . .'
CurrentOperation = 'Loading'
PercentComplete = 0
}
Foreach ($EmptyGroup in $EmptyGroups){
$pi++
[int]$percentage = ($pi / $EmptyGroups.Count)*100
$Progress.CurrentOperation = "$pi of $($EmptyGroups.Count) - $($EmptyGroup.Name)"
$Progress.PercentComplete = $percentage
Write-Progress @Progress
$cParam = @{
Identity = $EmptyGroup.Distinguishedname
Properties = 'CanonicalName',
'Created',
'Description',
'GroupCategory',
'GroupScope',
'MemberOf',
'Membership',
'Modified',
'Name'
}
$cGroup = Get-ADGroup @cParam
$EmptyGroupTotal += [PSCustomObject][Ordered]@{
Name = $cGroup.Name
Description = $cGroup.Description
GroupScope = $cGroup.GroupScope
GroupCategory = $cGroup.GroupCategory
Memberof = $cGroup.MemberOf
Membership = ($cGroup.Membership).Count
Created = $cGroup.Created
Modified = $cGroup.Modified
CanonicalName = $cGroup.CanonicalName
}
}
### Cleanup
Write-Progress -Activity 'Working through users . . .' -Status 'Ready' -Completed
### Return results
$EmptyGroupTotal | Out-GridView
}
Function Get-EmptyOUs {
<#
.SYNOPSIS
Shows empty OUs.
.DESCRIPTION
Searches for, and displays Organizational Units that contain no objects.
.EXAMPLE
Get-EmptyOUs
.INPUTS
None
.OUTPUTS
Array
#>
$remove_ous = $false
$ous_to_keep = @(
'UAP - PEAP TLS',
'UAP - PEAP TLS Only',
'Disabled Users'
)
$ad_objects = Get-ADObject -Filter "ObjectClass -eq 'user' -or ObjectClass -eq 'computer' -or ObjectClass -eq 'group' -or ObjectClass -eq 'organizationalUnit'"
$aOuDns = @()
ForEach ($o in $ad_objects) {
$sDn = $o.DistinguishedName
If ($sDn -like '*OU=*' -and $sDn -notlike '*LostAndFound*') {
$sOuDn = $sDn.Substring($sDn.IndexOf('OU='))
$aOuDns += $sOuDn
}
}
$a0CountOus = $aOuDns | Group-Object | Where-Object {$_.Count -eq 1} | ForEach-Object {$_.Name}
$empty_ous = 0
$ous_removed = 0
ForEach ($sOu in $a0CountOus) {
If (!(Get-ADObject -Filter "ObjectClass -eq 'organizationalUnit'" | Where-Object {$_.DistinguishedName -like "*$sOu*" -and $_.DistinguishedName -ne $sOu})) {
$ou = Get-AdObject -Filter {DistinguishedName -eq $sOu}
If ($ous_to_keep -notcontains $ou.Name) {
If ($remove_ous) {
Set-ADOrganizationalUnit -Identity $ou.DistinguishedName -ProtectedFromAccidentalDeletion $false -confirm:$false
Remove-AdOrganizationalUnit -Identity $ou.DistinguishedName -confirm:$false
$ous_removed++
}
$ou
$empty_ous++
}
}
}
Write-Output -InputObject '-------------------'
#echo "Total Empty OUs Removed: $ous_removed"
Write-Output -InputObject "Total Empty OUs: $empty_ous"
}
Function Get-SchemaVersion {
<#
.SYNOPSIS
Get domain Schema version.
.DESCRIPTION
Get domain Schema operating level.
.EXAMPLE
Get-SchemaVersion
Windows Server 2008 R2
.INPUTS
None
.OUTPUTS
String
#>
$SchemaVersion = (Get-ADObject -Identity (Get-ADRootDSE).schemaNamingContext -Properties objectVersion).objectVersion
$HRSchemaVer = $null
Switch ($SchemaVersion){
{'13'} {$HRSchemaVer = 'Windows Server 2000'}
{'30'} {$HRSchemaVer = 'Windows Server 2003'}
{'31'} {$HRSchemaVer = 'Windows Server 2003 R2'}
{'44'} {$HRSchemaVer = 'Windows Server 2008'}
{'47'} {$HRSchemaVer = 'Windows Server 2008 R2'}
{'56'} {$HRSchemaVer = 'Windows Server 2012'}
{'69'} {$HRSchemaVer = 'Windows Server 2012 R2'}
{'87'} {$HRSchemaVer = 'Windows Server 2016'}
{'88'} {$HRSchemaVer = 'Windows Server 2019'}
}
Return $HRSchemaVer
}
#endregion Domain
#region Updates/Misc
Function Get-UniquePerms {
<#
.SYNOPSIS
Get-UniquePerms iterates through a folder to show unique ACLs
.DESCRIPTION
Iterate through folders and show all folders that have permissions other than inherited
.PARAMETER shares
Parameter: -shares is needed to tell the function where to look
.PARAMETER csv
Parameter -csv tells the function to output in your run directory a csv output of what was found.
.EXAMPLE
Get-UniquePerms -shares '\\DFSPath\path\share'
Scans that one path
.EXAMPLE
Get-UniquePerms -shares '\\DFSPath\path\share','\\DFSPath\test\share'
Scans more than one path
.INPUTS
String
.OUTPUTS
Array
#>
Param (
[Parameter(
Mandatory = $True,
HelpMessage = 'Need share path'
)]
[Object]$shares,
[Switch]$csv
)
If ($csv){
$csvOut = "$($env:USERPROFILE)\desktop\Working_permissions.csv"
}
$output = @()
ForEach ($share in $shares) {
$folder = Get-Item -LiteralPath $share
$acl = Get-ACL -Path $folder.FullName
ForEach ($access in $acl.access) {
$output += [PSCustomObject]@{
'Share' = $share
'Folder' = $folder.name
'User' = $access.identityreference
'IsInherited' = ''
'Permissions' = $access.FileSystemRights
'ParentPath' = $folder.Parent.FullName
}
}
$folders = Get-ChildItem -LiteralPath $share -Directory -Recurse
ForEach ($folder in $folders) {
Try {
$acl = Get-ACL -Path $folder.fullname
ForEach ($access in $acl.access) {
If ($access.isinherited -eq $false) {
$output += [PsCustomObject]@{
'Share' = $share
'Folder' = $folder.name
'User' = $access.identityreference
'IsInherited' = $access.IsInherited
'Permissions' = $access.FileSystemRights
'ParentPath' = $folder.Parent.FullName
}
}
}
} Catch {
$output += [PSCustomObject]@{
'Share' = $share
'Folder' = $folder
'User' = '???'
'IsInherited' = '???'
'Permissions' = '???'
'ParentPath' = $folder.Parent.FullName
}
}
}
}
If ($csv){
$output | Export-Csv -Path $csvOut -NoTypeInformation -Encoding UTF8 -Delimiter ','
} Else {
$output | Out-GridView
}
}
Function Get-UpdatesLastNight {
<#
.SYNOPSIS
Ask a computer to list all windows updates installed in the last 24 hours.
.DESCRIPTION
Send a query to a remote computer to get an inventory of all MS updates installed in the last 24 hours.
.PARAMETER comps
Computer name.
.PARAMETER Credential
You can specify either pre-stored Credentials or it will prompt
.EXAMPLE
Get-UpdatesLastNight -comps Test-Computer -Credential Value
Attempt to query remote computer using current credentials
.EXAMPLE
$CRD = Get-Credential
Get-UpdatesLastNight -comps @('Test-Computer','Test-Computer2') -Credential $CRD | Format-Table -Auto
Pre-set Credentials to use - Query Test-Computer and Test-Computer2 to Table-View
.NOTES
To kill all updates over the last 24 hours:
Get-HotFix | ?{$_.InstalledOn -gt [datetime]::Today.AddDays(-1)} | %{$kb=$_.HotFixID.trimStart('KB'); wusa /uninstall /kb:$kb}
.INPUTS
String
.OUTPUTS
Array
#>
Param (
[Parameter(
Position = 0,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true
)]
[Alias('hostname')]
[Alias('cn')]
[ValidateNotNullOrEmpty()]
[String[]]$comps = $env:COMPUTERNAME,
[Parameter(Position=1)]
[Alias('runas')]
[ValidateNotNull()]
[System.Management.Automation.Credential()]
[PSCredential]$Credential = $Credential
)
Begin {
$res = @()
If ($comps.count -gt 1){
$pi = 0
$Progress = @{
Activity = 'Iterating thrugh servers Servers . . . '
CurrentOperation = 'Loading'
PercentComplete = 0
}
$multi = $true
} Else {
$multi = $false
}
}
Process {
ForEach ($srv in $comps){
If (-not $srv.Name) {
$srv = [PSCustomObject]@{
Name = $srv
}
}
If ($multi) {
$pi++
$Progress.CurrentOperation = ('{0} of {1} - {2}' -f $pi, $comps.Count, $srv.Name)
$Progress.PercentComplete = [int](($pi / $comps.Count) * 100)
Write-Progress @Progress
}
Try {
If ($Credential) {
$ret += Invoke-Command -ComputerName $srv.Name -ScriptBlock {
Get-HotFix | Where-Object {$_.InstalledOn -gt [datetime]::Today.AddDays(-1)}
} -Credential $Credential -ErrorAction Stop
} Else {
$ret += Invoke-Command -ComputerName $srv.Name -ScriptBlock {
Get-HotFix | Where-Object {$_.InstalledOn -gt [datetime]::Today.AddDays(-1)}
} -ErrorAction Stop
}
ForEach ($r in $ret) {
$res += [PSCustomObject]@{
ServerName = $srv.Name
OS = $srv.OperatingSystem
Description = $r.Description
HotFixID = $r.HotFixID
InstalledBy = $r.InstalledBy
InstalledOn = $r.InstalledOn
}
}
} Catch {
# Check for common DCOM errors and display "friendly" output
Switch ($_) {
{$_.Exception.FullyQualifiedErrorId -match 'ComputerNotFound'} {
$err = 'Cannot resolve Computer Name'
Break
}
{$_.CategoryInfo.FullyQualifiedErrorId -match 'WinRMOperationTimeOut'} {
$err = 'Access denied (Check User Permissions) - or Computer may be off'
Break
}
{$_.CategoryInfo.FullyQualifiedErrorId -match 'NetworkPathNotFound'} {
$err = 'Computer cannot be located on network. Still real?'
Break
}
default {
$err = $_.exception.gettype().fullname
}
}
Write-Warning -Message ('{0} - {1}' -f $srv, $err)
}
}
}
End {
If ($multi) {
Write-Progress -Activity 'Iterating thrugh servers Servers . . . ' -Status 'Ready' -Completed
}
return $res
}
}
Function Get-VPNCheck {
<#
.SYNOPSIS
Checks if a user is a member of VPN-Users group.
.DESCRIPTION
Checks if a user is a member of VPN-Users group. If not, attempts to add the user.
.PARAMETER user
username
.EXAMPLE
Get-VPNCheck -user test.user
Checks to see if the user is a member of VPN-Users.
If they aren't, prompts for SA credentials, then attempts to add the user to the VPN-Users group.
.INPUTS
String
.OUTPUTS
String
#>
[Alias('vpncheck')]
Param (
[Parameter(
Mandatory = $True,
HelpMessage = 'username'
)]
[String]$user
)
Begin {
### Get the What-is
$UPA = @{
Identity = $user
Properties = 'MemberOf'
Server = $script:PDC
}
$GDN = (Get-AdGroup -Identity 'VPN-Users').DistinguishedName
$UAD = Get-AdUser @UPA
}
Process {
### Evaluate
If ($UAD.MemberOf -contains $GDN){
$msg = 'User is already in VPN-Users Group'
} Else {
### Add the user into the VPN-Users Group
Invoke-Command -ComputerName $PDC -Credential $Credential -ScriptBlock {
Get-ADUser -Identity $args[0] | ForEach-Object {Add-ADGroupMember -Identity 'VPN-Users' -Members $_}
} -ArgumentList $user
Write-Verbose -Message 'Attempted to add user to group'
Start-Sleep -Seconds 1
### Re-Evluate for success
If ((Get-AdUser @UPA).MemberOf -contains $GDN){
$msg = 'User was missing from VPN-Users group, but was successfully added.'
} Else {
$msg = 'User was missing from VPN-Users group, but attempt to add user failed'
}
}
}
End {
return $msg
}
}
Function Get-WinupdateAll {
<#
.SYNOPSIS
Use PSWindowsUpdate Module to perform updates.
.DESCRIPTION
Fetch and use PSWindowsUpdate module to fetch all updates to be current.
.PARAMETER norestart
Don't restart.
.EXAMPLE
Get-Winupdates -norestart
.INPUTS
None
.OUTPUTS
None
#>
[CmdletBinding()]
Param (
[switch]$norestart
)
#region prereq
$Module = Get-Module -Name PSWindowsUpdate -ListAvailable
If ($Module.Count -eq 0) {
$Nuget = Get-PackageProvider -Name NuGet -ListAvailable | Select-Object -First 1
$Get = Get-Module -Name PowerShellGet -ListAvailable
If ("$($NuGet.Version.Major).$($NuGet.Version.Minor)" -gt '2.7') {
If ($Get.Count -eq 0) {
$ProcessSplat1 = @{
FilePath = 'PowerShell.exe'
ArgumentList = '-NoProfile -ExecutionPolicy Bypass -Command ""Install-Module -Name PowerShellGet -Force""'
Verb = 'RunAs'
}
Start-Process @ProcessSplat1
Import-Module -Name PowerShellGet
}
} Else {
Try {
$ProcessSplat2 = @{
FilePath = 'PowerShell.exe'
ArgumentList = '-NoProfile -ExecutionPolicy Bypass -Command ""Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force""'
Verb = 'RunAs'
Wait = $true
}
$ProcessSplat3 = @{
FilePath = 'PowerShell.exe'
ArgumentList = '-NoProfile -ExecutionPolicy Bypass -Command ""Install-Module -Name PowerShellGet -Force""'
Verb = 'RunAs'
Wait = $true
}
Start-Process @ProcessSplat2
Start-Process @ProcessSplat3
} Catch {
$msg = 'Define PowerShell to use TLS1.2 in this session, needed since 1st April 2020 (https://devblogs.microsoft.com/powershell/powershell-gallery-tls-support/)'
Write-Output -InputObject $msg
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
$ProcessSplat4 = @{
FilePath = 'PowerShell.exe'
ArgumentList = '-NoProfile -ExecutionPolicy Bypass -Command ""Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force""'
Verb = 'RunAs'
Wait = $true
}
Start-Process @ProcessSplat4
$ProcessSplat5 = @{
FilePath = 'PowerShell.exe'
ArgumentList = '-NoProfile -ExecutionPolicy Bypass -Command ""Install-Module -Name PowerShellGet -Force""'
Verb = 'RunAs'
Wait = $true
}
Start-Process @ProcessSplat5
$ProcessSplat6 = @{
FilePath = 'PowerShell.exe'
ArgumentList = '-NoProfile -ExecutionPolicy Bypass -Command ""Install-Module -Name PSWindowsUpdate -Force""'
Verb = 'RunAs'
Wait = $true
}
Start-Process @ProcessSplat6
}
}
} Else {
Import-Module -Name PSWindowsUpdate -Force
}
#endregion prereq
If ($norestart){
$ProcessSplat7 = @{
FilePath = 'PowerShell.exe'
ArgumentList = '-NoProfile -ExecutionPolicy Bypass -Command ""Get-WUInstall -AcceptAll -MicrosoftUpdate -Install -Verbose""'
Verb = 'RunAs'
}
Start-Process @ProcessSplat7
} Else {
$ProcessSplat8 = @{
FilePath = 'PowerShell.exe'
ArgumentList = '-NoProfile -ExecutionPolicy Bypass -Command ""Get-WUInstall -AutoReboot -AcceptAll -MicrosoftUpdate -Install -RecurseCycle 10 -Verbose""'
Verb = 'RunAs'
}
Start-Process @ProcessSplat8
}
}
Function Install-WinUpdates {
<#
.SYNOPSIS
Use PSWindowsUpdate Module to perform updates.
.DESCRIPTION
Fetch and use PSWindowsUpdate module to fetch all updates to be current.
.PARAMETER norestart
Don't restart.
.EXAMPLE
Install-Winupdates -norestart
Installs as many updates as possible until restart required.
.INPUTS
None
.OUTPUTS
None
#>
[CmdletBinding()]
Param(
[Switch]$norestart
)
#region prereq
$Module = Get-Module -Name PSWindowsUpdate -ListAvailable
If ($Module.Count -eq 0) {
$Nuget = Get-PackageProvider -Name NuGet -ListAvailable | Select-Object -First 1
$Get = Get-Module -Name PowerShellGet -ListAvailable
If ("$($NuGet.Version.Major).$($NuGet.Version.Minor)" -gt '2.7') {
If ($Get.Count -eq 0) {
$ProcessSplat = @{
FilePath = 'PowerShell.exe'
ArgumentList = '-NoProfile -ExecutionPolicy Bypass -Command ""Install-Module -Name PowerShellGet -Force""'
Verb = 'RunAs'
}
Start-Process @ProcessSplat
Import-Module -Name PowerShellGet
}
} Else {
Try {
$ProcessSplat1 = @{
FilePath = 'PowerShell.exe'
ArgumentList = '-NoProfile -ExecutionPolicy Bypass -Command ""Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force""'
Verb = 'RunAs'
Wait = $true
}
$ProcessSplat2 = @{
FilePath = 'PowerShell.exe'
ArgumentList = '-NoProfile -ExecutionPolicy Bypass -Command ""Install-Module -Name PowerShellGet -Force""'
Verb = 'RunAs'
Wait = $true
}
Start-Process @ProcessSplat1
Start-Process @ProcessSplat2
} Catch {
$msg = 'Define PowerShell to use TLS1.2 in this session, needed since 1st April 2020 (https://devblogs.microsoft.com/powershell/powershell-gallery-tls-support/)'
Write-Output -InputObject $msg
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
$ProcessSplat3 = @{
FilePath = 'PowerShell.exe'
ArgumentList = '-NoProfile -ExecutionPolicy Bypass -Command ""Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force""'
Verb = 'RunAs'
Wait = $true
}
$ProcessSplat4 = @{
FilePath = 'PowerShell.exe'
ArgumentList = '-NoProfile -ExecutionPolicy Bypass -Command ""Install-Module -Name PowerShellGet -Force""'
Verb = 'RunAs'
Wait = $true
}
$ProcessSplat5 = @{
FilePath = 'PowerShell.exe'
ArgumentList = '-NoProfile -ExecutionPolicy Bypass -Command ""Install-Module -Name PSWindowsUpdate -Force""'
Verb = 'RunAs'
Wait = $true
}
Start-Process @ProcessSplat3
Start-Process @ProcessSplat4
Start-Process @ProcessSplat5
}
}
} Else {
Import-Module -Name PSWindowsUpdate -Force
}
#endregion prereq
If ($norestart) {
$ProcessSplat6 = @{
FilePath = 'PowerShell.exe'
ArgumentList = '-NoProfile -ExecutionPolicy Bypass -Command ""Get-WUInstall -AcceptAll -MicrosoftUpdate -Install -Verbose""'
Verb = 'RunAs'
}
Start-Process @ProcessSplat6
} Else {
$ProcessSplat7 = @{
FilePath = 'PowerShell.exe'
ArgumentList = '-NoProfile -ExecutionPolicy Bypass -Command ""Get-WUInstall -AutoReboot -AcceptAll -MicrosoftUpdate -Install -RecurseCycle 10 -Verbose""'
Verb = 'RunAs'
}
Start-Process @ProcessSplat7
}
}
Function Invoke-ElevatedCommand {
<#
.DESCRIPTION
Invokes the provided script block in a new elevated (Administrator) powershell process,
while retaining access to the pipeline (pipe in and out). Please note, "Write-Host" output
will be LOST - only the object pipeline and errors are handled. In general, prefer
"Write-Output" over "Write-Host" unless UI output is the only possible use of the information.
Also see Community Extensions "Invoke-Elevated"/"su"
.EXAMPLE
Invoke-ElevatedCommand {"Hello World"}
.EXAMPLE
"test" | Invoke-ElevatedCommand {$input | Out-File -filepath c:\test.txt}
.EXAMPLE
Invoke-ElevatedCommand {$one = 1; $zero = 0; $throwanerror = $one / $zero}
.PARAMETER Scriptblock
A script block to be executed with elevated priviledges.
.PARAMETER InputObject
An optional object (of any type) to be passed in to the scriptblock (available as $input)
.PARAMETER EnableProfile
A switch that enables powershell profile loading for the elevated command/block
.PARAMETER DisplayWindow
A switch that enables the display of the spawned process (for potential interaction)
.SYNOPSIS
Invoke a powershell script block as Administrator
.INPUTS
ScriptBlock
.OUTPUTS
Various
#>
[CmdletBinding()]
[Alias('sudo')]
Param (
## The script block to invoke elevated. NOTE: to access the InputObject/pipeline data from the script block, use "$input"!
[Parameter(
Mandatory = $True,
HelpMessage = 'Script block to invoke elevated'
)]
[ScriptBlock]$Scriptblock,
## Any input to give the elevated process
[Parameter(
Mandatory = $True,
HelpMessage = 'Input Object',
ValueFromPipeline = $true
)]
$InputObject,
## Switch to enable the user profile
[Switch]$EnableProfile,
## Switch to display the spawned window (as interactive)
[Switch]$DisplayWindow
)
Begin {
$inputItems = New-Object -TypeName System.Collections.ArrayList
}
Process {
$null = $inputItems.Add($InputObject)
}
End {
## Create some temporary files for streaming input and output
$outputFile = [IO.Path]::GetTempFileName()
$inputFile = [IO.Path]::GetTempFileName()
$errorFile = [IO.Path]::GetTempFileName()
## Stream the input into the input file
$inputItems.ToArray() | Export-CliXml -Depth 1 -Path $inputFile
## Start creating the command line for the elevated PowerShell session
$commandLine = ''
If (-not $EnableProfile) {
$commandLine += '-NoProfile '
}
If (-not $DisplayWindow) {
$commandLine += '-Noninteractive '
$processWindowStyle = 'Hidden'
} Else {
$processWindowStyle = 'Normal'
}
## Convert the command into an encoded command for PowerShell
$commandString = "Set-Location '$($pwd.Path)'; " +
"`$output = Import-CliXml '$inputFile' | " +
'& {' +
$scriptblock.ToString() +
'} 2>&1 ; ' +
"Out-File -filepath '$errorFile' -inputobject `$error;" +
"Export-CliXml -Depth 1 -In `$output '$outputFile';"
$commandBytes = [Text.Encoding]::Unicode.GetBytes($commandString)
$encodedCommand = [Convert]::ToBase64String($commandBytes)
$commandLine += "-EncodedCommand $encodedCommand"
## Start the new PowerShell process
$process = Start-Process -FilePath (Get-Command -Name powershell).Definition -ArgumentList $commandLine -Passthru -Verb RunAs -WindowStyle $processWindowStyle
$process.WaitForExit()
$errorMessage = $(Get-Content -Path $errorFile | Out-String)
If ($errorMessage) {
Write-Error -Message $errorMessage
} Else {
If ((Get-Item -Path $outputFile).Length -gt 0) {
Import-CliXml -Path $outputFile
}
}
## Clean up
Remove-Item -Path $outputFile
Remove-Item -Path $inputFile
Remove-Item -Path $errorFile
}
}
Function New-RDPShortcut {
<#
.SYNOPSIS
Create an RDP Shortcut on desktop.
.DESCRIPTION
Create a generic RDP shortcut to desktop.
.PARAMETER CName
Computer Name.
.PARAMETER fn
Calculate Full Name.
.PARAMETER incdom
Include domain/username.
.EXAMPLE
New-RDPShortcut -CName server -fn -incdom
Creates a new RDP Shortcut on your desktop to server - attempt to resolve server FQDN, and include domain/username.
.INPUTS
String
.OUTPUTS
File
#>
# Generate RDP shortcuts from CSV file - Fills out Computer, Username, and Server Name fields
[CmdletBinding()]
Param (
[Parameter(
Mandatory = $True,
HelpMessage = 'Server Name'
)]
[ValidateNotNullOrEmpty()]
[String]$CName,
[Switch]$fn,
[Switch]$incdom
)
Add-Type -AssemblyName System.Windows.Forms
$isIP = [ipaddress]::TryParse("$($CName)",[ref][ipaddress]::Loopback)
If ($isIP) {
If (Get-IPValid -testip $CName) {
Continue
} Else {
Break
}
}
#$Orientation = [System.Windows.Forms.SystemInformation]::ScreenOrientation
### Regular: Angle0 - Rotated: Angle90;$Res=[System.Windows.Forms.SystemInformation]::PrimaryMonitorSize
$Uname = $env:USERNAME
$Domain = $env:USERDOMAIN
$DesktopPath = [Environment]::GetFolderPath('Desktop')
#https://stackoverflow.com/questions/7967699/get-screen-resolution-using-wmi-powershell-in-windows-7
$MonitorStats = @()
$screen_cnt = [Windows.Forms.Screen]::AllScreens.Count
$col_screens = [windows.forms.screen]::AllScreens
If ($screen_cnt -gt 1) {
$PrimaryMonitor = $col_screens | Where-Object {$_.Primary -eq $true}
} Else {
$PrimaryMonitor = $col_screens
}
$PrimaryMonitor | ForEach-Object {
If ("$($_.Bounds.Width)" -gt "$($_.Bounds.Height)") {
$monitor_orientation = 'Landscape'
} Else {
$monitor_orientation = 'Portrait'
}
$MonitorStats += [pscustomobject]@{
Orientation = $monitor_orientation
Bounds = $_.Bounds
Primary = $_.Primary
Name = $_.DeviceName
Width = $_.Bounds.Width
Height = $_.Bounds.Height
Resolution = "$($_.Bounds.Width) x $($_.Bounds.Height)"
BitsPerPixel = $_.BitsPerPixel
WorkingArea = $_.WorkingArea
}
}
$W = $MonitorStats.Width
$H = $MonitorStats.Height
$B = $MonitorStats.BitsPerPixel
### Include domain/username
If ($incdom.IsPresent) {$U = "$Domain\$UName"} Else {$U = $UName}
If ($isIP) {
$F = $CName
} Else {
### Resolve Full Name
If ($fn.IsPresent) {
Try {
$F = (Resolve-DNSName -Name $CName -Type PTR -ErrorAction Stop).NameHost
} Catch {
Write-Warning -Message ('No DNS Record for {0}' -f $CName)
$F = $CName
}
} Else {
$F = "$($CName.toUpper())"
}
}
$RDP = "screen mode id:i:2
use multimon:i:0
desktopwidth:i:$W
desktopheight:i:$H
session bpp:i:$B
winposstr:s:0,1,0,12,$W,$H
compression:i:1
keyboardhook:i:2
audiocapturemode:i:0
videoplaybackmode:i:1
connection type:i:7
networkautodetect:i:1
bandwidthautodetect:i:1
displayconnectionbar:i:1
username:s:$U
enableworkspacereconnect:i:0
disable wallpaper:i:0
allow font smoothing:i:0
allow desktop composition:i:0
disable full window drag:i:1
disable menu anims:i:1
disable themes:i:0
disable cursor setting:i:0
bitmapcachepersistenable:i:1
full address:s:$F
audiomode:i:0
redirectprinters:i:1
redirectcomports:i:0
redirectsmartcards:i:1
redirectclipboard:i:1
redirectposdevices:i:0
autoreconnection enabled:i:1
authentication level:i:2
prompt for credentials:i:0
negotiate security layer:i:1
remoteapplicationmode:i:0
alternate shell:s:
shell working directory:s:
gatewayhostname:s:
gatewayusagemethod:i:4
gatewaycredentialssource:i:4
gatewayprofileusagemethod:i:0
promptcredentialonce:i:1
gatewaybrokeringtype:i:0
use redirection server name:i:0
rdgiskdcproxy:i:0
kdcproxyname:s:"
$RDP | Out-File -FilePath "$DesktopPath\$F.rdp"
}
Function Start-ElevatedPowerShell {
<#
.SYNOPSIS
SU up to Administrator.
.DESCRIPTION
Spawn new Administrator window.
.EXAMPLE
Start-ElevatedPowerShell
.INPUTS
None
.OUTPUTS
New Shell
#>
[cmdletbinding()]
[Alias('SU')]
Param (
[ValidateNotNull()]
[System.Management.Automation.Credential()]
[PSCredential]$Credential = $Credential
)
#If ($Credential -eq [pscredential]::Empty) {Register-SACreds} Else {[pscredential]$Credential = $Credential}
Start-Process -FilePath PowerShell -Credential $Credential -ArgumentList '-Command &{Start-Process PowerShell -Verb Runas}'
}
Function Step-MSTSC {
<#
.SYNOPSIS
RDP to machine.
.DESCRIPTION
Starts MSTSC to specified PC.
.PARAMETER RDPServer
Nme or IP of server/pc to connect to
.EXAMPLE
Step-MSTSC -RDPServer testcomputer
.INPUTS
None
.OUTPUTS
None
#>
[CmdletBinding()]
[Alias('RDP')]
Param (
[Parameter(
Mandatory = $True,
HelpMessage = 'RDP Destination',
Position = 0
)]
[String]$RDPServer
)
Start-Process -FilePath "$env:windir\system32\mstsc.exe" -ArgumentList "/v:$RDPServer"
}
Function Update-AllPowerShellModule {
<#
.SYNOPSIS
This script will update all locally installed PowerShell modules to the newest ones if can find online.
.DESCRIPTION
The script will search the usual PowerShell module profile paths for all modules and update them to the
newest versions available online.
Updating modules depends on 'PackageManagement' and 'PowerShellGet', which are updated to the newest
versions before upgrading any modules.
By default, it searches for beta, nightly, preview versions, etc., but you can exclude those with
the "-NoPreviews" switch.
The script presents you with a list of all modules it finds and shows you if a newer version is detected
and when that new version was published.
PowerShell comes with a similar "Update-Module" command, but that does not try to update 'PackageManagement'
and 'PowerShellGet'.
It shows no data while operating, so you are left with an empty screen unless you use the "-verbose" switch,
which displays too much information.
You can use the "-AllowPrerelease", but only with a named module. This script will install Prerelease
versions of all modules if they exist.
.PARAMETER NoPreviews
If you want to avoid versions that include 'beta', 'preview', 'nightly', etc., and only upgrade to fully released
versions of the modules, use this switch.
.EXAMPLE
Update-AllPSModule
This will update all locally installed modules .
.EXAMPLE
Update-AllPSModule -NoPreviews
This will update all locally installed modules but not to versions that include 'beta', 'preview', 'nightly', etc.
.INPUTS
None
.OUTPUTS
None
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'None')]
Param ([Switch]$NoPreviews)
Begin {
$NoPreviews = $true
#region Prereq's
If ($PSVersionTable.psversion -lt [version]'5.0.0') {
$msg = ('This script only works with PowerShell 5.0 or newer. You are running {0}' -f $PSVersionTable.PSVersion)
Write-Warning -Message $msg
break
}
If ($ExecutionContext.SessionState.LanguageMode -eq 'ConstrainedLanguage') {
$msg = 'Constrained Language mode is enabled, so the script cannot continue.'
Write-Warning -Message $msg
break
}
$CurrentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
If (-not (New-Object -TypeName 'Security.Principal.WindowsPrincipal' -ArgumentList $CurrentUser).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)) {
Write-Warning -Message 'The script is not being run as administrator so cannot continue.'
Break
}
#endregion Prereq's
$StartTime = Get-Date
#region PSRepository
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$NewSessionRequired = $false
Try {
$RegisteredRepositories = Get-PSRepository -ErrorAction 'Stop' -WarningAction 'Stop'
} Catch {
$msg = "Unable to query 'PSGallery' online. The script cannot continue - check your proxy/firewall settings."
Write-Warning -Message $msg
break
}
If ($RegisteredRepositories -notmatch 'PSGallery') {
Try {
Set-PSRepository -Name 'PSGallery' -InstallationPolicy 'Trusted' -ErrorAction 'Stop'
} Catch {
Write-Warning -Message 'Unable to Set the PSRepository'
break
}
}
#endregion PSRepository
#region PackageManagement Module
WH -t "Checking which version of the 'PackageManagement' module is installed locally" -n
$PackageManagement = (Get-Module -ListAvailable -Name 'PackageManagement' | Sort-Object -Property Version -Descending)[0]
WH -t (" - found '{0}'." -f ($PackageManagement.version).tostring())
If ([version]$PackageManagement.Version -lt [version]'1.4.7') {
"An updated version is required. Attempting to install 'Nuget'"
Try {
$NugetInstall = Install-PackageProvider -Name 'Nuget' -Force -ErrorAction 'Stop'
WH -t ("Successfully installed 'Nuget' version '{0}'" -f $NugetInstall.Version)
} Catch {
$msg = 'No match was found for the specified search criteria for the provider '
If ($error[0].exception.message -match $msg) {
Write-Warning -Message 'Unable to find packages online. Check proxy settings.'
} Else {
WH -t -Object 'Unknown error.'
}
break
}
WH -t "Searching for a newer version of 'PackageManagement'."
Try {
$OnlinePackageManagement = Find-Module -Name 'PackageManagement' -Repository 'PSGallery' -ErrorAction 'Stop'
} Catch {
WH -t "Unable to find 'PackageManagement' online. Does this machine have an internet connection?"
break
}
Try {
$OnlinePackageManagement | Install-Module -Force -SkipPublisherCheck -ErrorAction 'Stop'
WH -t ("Successfully installed 'PackageManagement' version '{0}'" -f $OnlinePackageManagement.Version)
} Catch {
WH -t "Failed to install 'PackageManagement'."
break
}
WH -t "You need to close PowerShell and re-open it to use the new 'PackageManagement' module."
$NewSessionRequired = $true
}
#endregion PackageManagement Module
#region PowerShellGet Module
WH -t "Checking which version of the 'PowerShellGet' module is installed locally" -n
$PowershellGet = (Get-Module -ListAvailable -Name 'PowerShellGet' | Sort-Object -Property Version -Descending)[0]
WH -t (" - found '{0}'." -f ($PowershellGet.version).tostring())
If ([version]$PowershellGet.Version -lt [version]'1.6.0') {
$OnlinePSGet = Find-Module -Name 'PowerShellGet' -Repository 'PSGallery'
WH -t ("Version '{0}' found online, will attempt to update." -f $OnlinePSGet.version)
Try {
$OnlinePSGet | Install-Module -Force -SkipPublisherCheck -ErrorAction 'Stop'
$msg = ("Successfully installed 'PowerShellGet' version '{0}'. Close PowerShell and re-open it to use the new module." -f $OnlinePSGet.Version)
WH -t $msg
$NewSessionRequired = $true
} Catch {
$msg = "Unable to install to 'C:\Program Files\WindowsPowerShell\Modules' so will try the Current User module path instead."
WH -t $msg
Try {
$OnlinePSGet | Install-Module -Force -SkipPublisherCheck -Scope CurrentUser -ErrorAction 'Stop'
$msg = ("Successfully installed 'PowerShellGet' version '{0}'. Close PowerShell and re-open it to use the new module." -f $OnlinePSGet.Version)
WH -t $msg
$NewSessionRequired = $true
} Catch {
$msg = 'Failed to install the latest version of PowerShellGet. This will mean you may not be able to install preview, or beta versions of the modules.'
Write-Warning -Message $msg
break
}
}
}
#endregion PowerShellGet Module
}
Process {
If ($NewSessionRequired) {break}
$Failed = @()
WH -t 'Searching for all locally installed modules' -n
$ignoredModules = 'PackageManagement|PowerShellGet|Az\.|AzureRM\.|Azure\.|Microsoft.Graph.|PSReadline|VMware.PowerCLI.|VMware.Sdk.'
$InstalledModules = Get-InstalledModule | Where-Object -FilterScript {($_.Name -notmatch $ignoredModules)} | Sort-Object -Property 'Name'
If ($InstalledModules) {
$SuccessfulUpdates = 0
Write-Host -Object (' - {0} modules found.' -f ($InstalledModules | Measure-Object).count)
Write-Host -Object 'Checking for newer versions online and trying to update them.'
$MaxNameWidth = (($InstalledModules).'Name' | Sort-Object -Property 'Length' -Descending | Select-Object -First 1).length + 3
$MaxVersionWidth = (($InstalledModules).'Version' | Sort-Object -Property 'Length' -Descending | Select-Object -First 1).length + 3
ForEach ($InstalledModule in $InstalledModules) {
WH -t ("{0,-$MaxNameWidth}" -f $InstalledModule.Name) -n
Try {
$Module = Get-InstalledModule -Name $InstalledModule.Name -AllVersions | Sort-Object -Property {[version](($_.Version -split '-')[0])} -Descending | Select-Object -First 1
$LatestAvailable = Find-Module -Name $InstalledModule.Name -ErrorAction 'Stop'
} Catch {
$Failed += $InstalledModule
continue
}
WH -t ("{0,-$MaxVersionWidth}" -f $Module.Version) -n
If (
([version](($Module.Version -replace '[a-z]*', '').Replace('-', '.') -replace '\.$', '')) -ge
([version](($LatestAvailable.Version -replace '[a-z]*', '').Replace('-', '.') -replace '\.$', ''))
) {
WH -t $([char]0x2714) -f Gr
} Else {
$AllUsersFailed = $false
$Gap = (20 - (($LatestAvailable.version).length))
WH -t ("Online version found: '{0}' - attempting to update. {1,$Gap}" -f "$($LatestAvailable.version)' - Published '$(Get-Date -Date ($LatestAvailable.PublishedDate) -Format 'yyyy-MM-dd')", ' ') -f Ye -n
Try {
Update-Module -AcceptLicense -Force -Name $Module.Name -Scope 'AllUsers' -ErrorAction 'Stop'
WH -t $([char]0x2714) -f Gr
$SuccessfulUpdates++
} Catch {
$AllUsersFailed = $true
}
If ($AllUsersFailed) {
Try {
Update-Module -AcceptLicense -AllowPrerelease -RequiredVersion $LatestAvailable.version -Force -Name $Module.Name -Scope 'CurrentUser' -ErrorAction 'Stop'
WH -t $([char]0x2714) -f G -n
WH -t " ('Current User' scope only)" -f Ye
$SuccessfulUpdates++
} Catch {
WH -t ([char]0x2718) -f Re -n
WH -t (' Update not possible, will uninstall and try a new install. ') -f Ye -n
Try {
Uninstall-Module -Name $Module.Name -AllowPrerelease -AllVersions -Force -ErrorAction 'Stop'
Try {
Install-Module -Name $Module.Name -RequiredVersion $LatestAvailable.version -AcceptLicense -AllowClobber -AllowPrerelease -Force -Scope 'AllUsers' -SkipPublisherCheck -ErrorAction 'Stop'
WH -t $([char]0x2714) -f Gr
$SuccessfulUpdates++
} Catch {
Try {
Install-Module -Name $Module.Name -RequiredVersion $LatestAvailable.version -AcceptLicense -AllowClobber -AllowPrerelease -Force -Scope 'CurrentUser' -SkipPublisherCheck -ErrorAction 'Stop'
WH -t $([char]0x2714) -f Gr -n
WH -t " ('Current User' scope only)" -f Ye
$SuccessfulUpdates++
} Catch {
WH -t ([char]0x2718) -f Re
}
}
} Catch {
WH -t ([char]0x2718) -f Re
}
}
}
}
}
} Else {
'. None found.'
}
$OnlinePSGet = Find-Module -Name 'PowerShellGet' -AllowPrerelease -Repository 'PSGallery' -ErrorAction 'Stop'
If (([version](($PowershellGet.Version -split ('-'))[0])) -lt ([version](($OnlinePSGet.Version -split ('-'))[0]))) {
WH -t "A newer version of 'PowerShellGet' is available online, will attempt to update."
Try {
$OnlinePSGet | Install-Module -Force -SkipPublisherCheck -ErrorAction 'Stop'
$msg = ("Successfully installed 'PowerShellGet' version '{0}'. Close PowerShell and re-open it to use the new module." -f $OnlinePSGet.Version)
WH -t $msg
$NewSessionRequired = $true
} Catch {
$msg = "Unable to install to 'C:\Program Files\WindowsPowerShell\Modules' so will try the Current User module path instead."
WH -t $msg
Try {
$OnlinePSGet | Install-Module -Force -SkipPublisherCheck -Scope CurrentUser -ErrorAction 'Stop'
$msg = ("Successfully installed 'PowerShellGet' version '{0}'. Close PowerShell and re-open it to use the new module." -f $OnlinePSGet.Version)
WH -t
$NewSessionRequired = $true
} Catch {
$msg = 'Failed to install the latest version of PowerShellGet. This will mean you may not be able to install preview, or beta versions of the modules.'
Write-Warning -Message $msg
break
}
}
}
}
End {
If ($Failed) {
'Unable to find these modules:'
$Failed
}
$EndTime = Get-Date
$TimeTaken = ''
$TakenSpan = New-TimeSpan -Start $StartTime -End $EndTime
If ($TakenSpan.Hours) {
$TimeTaken += ('{0} hours, {1} minutes, ' -f $TakenSpan.Hours, $TakenSpan.Minutes)
} Elseif ($TakenSpan.Minutes) {
$TimeTaken += ('{0} minutes, ' -f $TakenSpan.Minutes)
}
$TimeTaken += ('{0} seconds' -f $TakenSpan.Seconds)
If ($SuccessfulUpdates) {
WH -t ('Successfully updated {0} module(s) in {1}.' -f ($SuccessfulUpdates), ($TimeTaken))
} Else {
WH -t -Object ('The script took {0} to complete and no updates were required.' -f $TimeTaken)
}
}
}
Function Step-UpdateModules {
<#
.SYNOPSIS
Update all installed modules without exception.
.DESCRIPTION
Update all installed modules without exception.
.EXAMPLE
Step-UpdateModules
Updates all modules including sub-modules
.INPUTS
None
.OUTPUTS
None
#>
[CmdletBinding()]
[Alias('UpdateModules')]
Param ()
If (Test-Administrator){
Update-AllPowerShellModules
} Else {
Invoke-ElevatedCommand -EnableProfile -DisplayWindow -Scriptblock {Update-AllPowerShellModules}
}
}
Function Connect-Office365Services {
<#
.SYNOPSIS
Connect-Office365Services
.DESCRIPTION
The functions are listed below. Note that functions may call eachother, for example to
connect to Exchange Online the Office 365 Credentials the user is prompted to enter these credentials.
Also, the credentials are persistent in the current session, there is no need to re-enter credentials
when connecting to Exchange Online Protection for example. Should different credentials be required,
call Get-Office365Credentials or Get-OnPremisesCredentials again.
Helper Functions:
=================
- Connect-AzureActiveDirectory Connects to Azure Active Directory
- Connect-AzureRMS Connects to Azure Rights Management
- Connect-ExchangeOnline Connects to Exchange Online (Graph module)
- Connect-SkypeOnline Connects to Skype for Business Online
- Connect-EOP Connects to Exchange Online Protection
- Connect-AIP Connects to Azure Information Protection
- Connect-PowerApps Connects to PowerApps
- Connect-ComplianceCenter Connects to Compliance Center
- Connect-SharePointOnline Connects to SharePoint Online
- Connect-MSTeams Connects to Microsoft Teams
- Get-Office365Credentials Gets Office 365 credentials
- Connect-ExchangeOnPremises Connects to Exchange On-Premises
- Get-OnPremisesCredentials Gets On-Premises credentials
- Get-ExchangeOnPremisesFQDN Gets FQDN for Exchange On-Premises
- Get-Office365Tenant Gets Office 365 tenant name
- Set-Office365Environment Configures Uri's and region to use
- Update-Office365Modules Updates supported Office 365 modules
- Report-Office365Modules Report on known vs online module versions
Functions to connect to other services provided by the module, e.g. Connect-MSGraph or Connect-MSTeams.
To register the PowerShell Test Gallery and install modules from there, use:
Register-PSRepository -Name PSGalleryInt -SourceLocation https://www.poshtestgallery.com/ -InstallationPolicy Trusted
Install-Module -Name MicrosoftTeams -Repository PSGalleryInt -Force -Scope AllUsers
To load the helper functions from your PowerShell profile, put Connect-Office365Services.ps1 in the same location
as your $profile file, and edit $profile as follows:
& (Join-Path $PSScriptRoot "Connect-Office365Services.ps1")
#>
##Requires -Version 3.0
Add-Type -AssemblyName WindowsBase
Add-Type -AssemblyName PresentationCore
$local:ScriptVersion = '3.11'
Function script:Set-WindowTitle {
If ($host.ui.RawUI.WindowTitle -and $script:myOffice365Services['TenantID']) {
$local:PromptPrefix = ''
$ThisPrincipal = New-Object -TypeName System.Security.Principal.WindowsPrincipal -ArgumentList ([Security.Principal.WindowsIdentity]::GetCurrent())
If ($ThisPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
$local:PromptPrefix = 'Administrator:'
}
$local:Title = '{0}{1} connected to Tenant ID {2}' -f $local:PromptPrefix, $myOffice365Services['Office365Credentials'].UserName, $script:myOffice365Services['TenantID']
$host.ui.RawUI.WindowTitle = $local:Title
}
}
Function script:Get-TenantIDfromMail {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $True, HelpMessage = 'Mail Address')]
[string]$mail
)
$domainPart= ($mail -split '@')[1]
If ($domainPart) {
$res = (Invoke-RestMethod -Uri ('https://login.microsoftonline.com/{0}/v2.0/.well-known/openid-configuration' -f $domainPart)).jwks_uri.split('/')[3]
If (!($res)) {
Write-Warning -Message 'Could not determine Tenant ID using e-mail address'
$res = $null
}
} Else {
Write-Warning -Message 'E-mail address invalid, cannot determine Tenant ID'
$res = $null
}
return $res
}
Function script:Get-TenantID {
$script:myOffice365Services['TenantID'] = Get-TenantIDfromMail $myOffice365Services['Office365Credentials'].UserName
If ($script:myOffice365Services['TenantID']) {
Write-Host ('TenantID: {0}' -f $global:myOffice365Services['TenantID'])
}
}
Function script:Get-Office365ModuleInfo {
# Menu | Submenu | Menu ScriptBlock | ModuleName | Description | (Repo)Link
@(
'Connect|Exchange Online|Connect-ExchangeOnline|ExchangeOnlineManagement|Exchange Online Management|https://www.powershellgallery.com/packages/ExchangeOnlineManagement',
'Connect|Exchange Online Protection|Connect-EOP',
'Connect|Exchange Compliance Center|Connect-ComplianceCenter',
'Connect|MSOnline|Connect-MSOnline|MSOnline|MSOnline|https://www.powershellgallery.com/packages/MSOnline',
'Connect|Azure AD (v2)|Connect-AzureAD|AzureAD|Azure Active Directory (v2)|https://www.powershellgallery.com/packages/azuread',
'Connect|Azure AD (v2 Preview)|Connect-AzureAD|AzureADPreview|Azure Active Directory (v2 Preview)|https://www.powershellgallery.com/packages/AzureADPreview',
'Connect|Azure Information Protection|Connect-AIP|AIPService|Azure Information Protection|https://www.powershellgallery.com/packages/AIPService',
'Connect|SharePoint Online|Connect-SharePointOnline|Microsoft.Online.Sharepoint.PowerShell|SharePoint Online|https://www.powershellgallery.com/packages/Microsoft.Online.SharePoint.PowerShell',
'Connect|Microsoft Teams|Connect-MSTeams|MicrosoftTeams|Microsoft Teams|https://www.powershellgallery.com/packages/MicrosoftTeams',
'Connect|Microsoft Commerce|Connect-MSCommerce|MSCommerce|Microsoft Commerce|https://www.powershellgallery.com/packages/MSCommerce',
'Connect|PnP.PowerShell|Connect-PnPOnline|PnP.PowerShell|PnP.PowerShell|https://www.powershellgallery.com/packages/PnP.PowerShell',
'Connect|PowerApps-Admin-PowerShell|Connect-PowerApps|Microsoft.PowerApps.Administration.PowerShell|PowerApps-Admin-PowerShell|https://www.powershellgallery.com/packages/Microsoft.PowerApps.Administration.PowerShell',
'Connect|PowerApps-PowerShell|Connect-PowerApps|Microsoft.PowerApps.PowerShell|PowerApps-PowerShell|https://www.powershellgallery.com/packages/Microsoft.PowerApps.PowerShell',
'Connect|MSGraph-Intune|Connect-MSGraph|Microsoft.Graph.Intune|MSGraph-Intune|https://www.powershellgallery.com/packages/Microsoft.Graph.Intune',
'Connect|Microsoft.Graph|Connect-MSGraph|Microsoft.Graph|Microsoft.Graph|https://www.powershellgallery.com/packages/Microsoft.Graph',
'Connect|MicrosoftPowerBIMgmt|Connect-PowerBIServiceAccount|MicrosoftPowerBIMgmt|MicrosoftPowerBIMgmt|https://www.powershellgallery.com/packages/MicrosoftPowerBIMgmt',
'Connect|Az|Connect-AzAccount|Az|Az|https://www.powershellgallery.com/packages/Az',
'Connect|Microsoft365DSC|New-M365DSCConnection|Microsoft365DSC|Microsoft365DSC|https://www.powershellgallery.com/packages/Microsoft36DSC',
'Connect|Whiteboard|Get-Whiteboard|WhiteboardAdmin|WhiteboardAdmin|https://www.powershellgallery.com/packages/WhiteboardAdmin',
'Connect|Microsoft Identity|Connect-MgGraph|MSIdentityTools|MSIdentityTools|https://www.powershellgallery.com/packages/MSIdentityTools',
'Settings|Office 365 Credentials|Get-Office365Credentials',
'Connect|Exchange On-Premises|Connect-ExchangeOnPremises',
'Settings|On-Premises Credentials|Get-OnPremisesCredentials',
'Settings|Exchange On-Premises FQDN|Get-ExchangeOnPremisesFQDN'
)
}
Function script:Set-Office365Environment {
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true,HelpMessage='Country')]
[ValidateSet('Germany', 'China', 'AzurePPE', 'USGovernment', 'Default')]
[string]$Environment
)
Switch ($Environment) {
'Germany' {
$script:myOffice365Services['ConnectionEndpointUri'] = 'https://outlook.office.de/PowerShell-LiveID'
$script:myOffice365Services['SCCConnectionEndpointUri'] = 'https://ps.compliance.protection.outlook.de/PowerShell-LiveId'
$script:myOffice365Services['EOPConnectionEndpointUri'] = 'https://ps.protection.protection.outlook.de/PowerShell-LiveId'
$script:myOffice365Services['AzureADAuthorizationEndpointUri'] = 'https://login.microsoftonline.de/common'
$script:myOffice365Services['SharePointRegion'] = 'Germany'
$script:myOffice365Services['AzureEnvironment'] = 'AzureGermanyCloud'
$script:myOffice365Services['TeamsEnvironment'] = ''
}
'China' {
$script:myOffice365Services['ConnectionEndpointUri'] = 'https://partner.outlook.cn/PowerShell-LiveID'
$script:myOffice365Services['SCCConnectionEndpointUri'] = 'https://ps.compliance.protection.outlook.com/PowerShell-LiveId'
$script:myOffice365Services['EOPConnectionEndpointUri'] = 'https://ps.protection.protection.outlook.com/PowerShell-LiveId'
$script:myOffice365Services['AzureADAuthorizationEndpointUri'] = 'https://login.chinacloudapi.cn/common'
$script:myOffice365Services['SharePointRegion'] = 'China'
$script:myOffice365Services['AzureEnvironment'] = 'AzureChinaCloud'
$script:myOffice365Services['TeamsEnvironment'] = ''
}
'AzurePPE' {
$script:myOffice365Services['ConnectionEndpointUri'] = ''
$script:myOffice365Services['SCCConnectionEndpointUri'] = 'https://ps.compliance.protection.outlook.com/PowerShell-LiveId'
$script:myOffice365Services['EOPConnectionEndpointUri'] = 'https://ps.protection.protection.outlook.com/PowerShell-LiveId'
$script:myOffice365Services['AzureADAuthorizationEndpointUri'] = ''
$script:myOffice365Services['SharePointRegion'] = ''
$script:myOffice365Services['AzureEnvironment'] = 'AzurePPE'
}
'USGovernment' {
$script:myOffice365Services['ConnectionEndpointUri'] = 'https://outlook.office365.com/PowerShell-LiveId'
$script:myOffice365Services['SCCConnectionEndpointUri'] = 'https://ps.compliance.protection.outlook.com/PowerShell-LiveId'
$script:myOffice365Services['EOPConnectionEndpointUri'] = 'https://ps.protection.protection.outlook.com/PowerShell-LiveId'
$script:myOffice365Services['AzureADAuthorizationEndpointUri'] = 'https://login.microsoftonline.com/common'
$script:myOffice365Services['SharePointRegion'] = 'ITAR'
$script:myOffice365Services['AzureEnvironment'] = 'AzureUSGovernment'
}
default {
$script:myOffice365Services['ConnectionEndpointUri'] = 'https://outlook.office365.com/PowerShell-LiveId'
$script:myOffice365Services['SCCConnectionEndpointUri'] = 'https://ps.compliance.protection.outlook.com/PowerShell-LiveId'
$script:myOffice365Services['EOPConnectionEndpointUri'] = 'https://ps.protection.protection.outlook.com/PowerShell-LiveId'
$script:myOffice365Services['AzureADAuthorizationEndpointUri'] = 'https://login.microsoftonline.com/common'
$script:myOffice365Services['SharePointRegion'] = 'Default'
$script:myOffice365Services['AzureEnvironment'] = 'AzureCloud'
}
}
}
Function script:Get-MultiFactorAuthenticationUsage {
$Answer = Read-host -Prompt 'Would you like to use Modern Authentication? (Y/n) '
Switch ($Answer.ToUpper()) {
'N' {$rval = $false}
Default {$rval = $true}
}
return $rval
}
Function script:Get-ExchangeOnlineClickOnceVersion {
Try {
$ManifestURI = 'https://cmdletpswmodule.blob.core.windows.net/exopsmodule/Microsoft.Online.CSE.PSModule.Client.application'
$res = Invoke-WebRequest -Uri $ManifestURI -UseBasicParsing
$xml = [xml]($res.rawContent.substring( $res.rawContent.indexOf('<?xml')))
$xml.assembly.assemblyIdentity.version
} Catch {
Write-Error -Message 'Cannot access or determine version of Microsoft.Online.CSE.PSModule.Client.application'
}
}
Function script:Connect-ExchangeOnline {
[CmdletBinding()]
Param(
[String]$ConnectionUri,
[String]$AzureADAuthorizationEndpointUri,
[Management.Automation.Remoting.PSSessionOption]$PSSessionOption,
[Switch]$BypassMailboxAnchoring = $false,
[String]$DelegatedOrganization,
[String]$Prefix,
[Switch]$ShowBanner = $False,
[String]$UserPrincipalName,
[System.Management.Automation.Credential()][pscredential]$Credential,
[Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,
[String]$CertificateFilePath,
[securestring]$CertificatePassword,
[String]$CertificateThumbprint,
[String]$AppId,
[String]$Organization,
[Switch]$EnableErrorReporting,
[String]$LogDirectoryPath,
$LogLevel,
[bool]$TrackPerformance,
[bool]$ShowProgress = $True,
[bool]$UseMultithreading,
[uint32]$PageSize,
[Switch]$Device,
[Switch]$InlineCredential,
[String[]]$CommandName = @('*'),
[String[]]$FormatTypeName = @('*'),
[Switch]$UseRPSSession = $false
)
If (!($PSBoundParameters.ContainsKey('ConnectionUri'))) {
$PSBoundParameters['ConnectionUri'] = $script:myOffice365Services['ConnectionEndpointUri']
}
If (!($PSBoundParameters.ContainsKey('AzureADAuthorizationEndpointUri'))) {
$PSBoundParameters['AzureADAuthorizationEndpointUri'] = $script:myOffice365Services['AzureADAuthorizationEndpointUri']
}
If (!($PSBoundParameters.ContainsKey('PSSessionOption'))) {
$PSBoundParameters['PSSessionOption'] = $script:myOffice365Services['SessionExchangeOptions']
}
If (
$PSBoundParameters.ContainsKey('UserPrincipalName') -or
$PSBoundParameters.ContainsKey('Certificate') -or
$PSBoundParameters.ContainsKey('CertificateFilePath') -or
$PSBoundParameters.ContainsKey('CertificateThumbprint') -or
$PSBoundParameters.ContainsKey('AppId')
) {
$script:myOffice365Services['Office365CredentialsMFA'] = $True
Write-Host ('Connecting to Exchange Online with specified Modern Authentication method ..')
} Else {
If ($PSBoundParameters.ContainsKey('Credential')) {
If (!($script:myOffice365Services['Office365Credentials'])) {
Get-Office365Credentials
}
If ($script:myOffice365Services['Office365CredentialsMFA']) {
Write-Host ('Connecting to Exchange Online with {0} using Modern Authentication ..' -f $script:myOffice365Services['Office365Credentials'].UserName)
$PSBoundParameters['UserPrincipalName'] = ($script:myOffice365Services['Office365Credentials']).UserName
} Else {
Write-Host ('Connecting to Exchange Online with {0} ..' -f $script:myOffice365Services['Office365Credentials'].username)
$PSBoundParameters['Credential'] = $script:myOffice365Services['Office365Credentials']
}
} Else {
Write-Host ('Connecting to Exchange Online with {0} using Legacy Authentication..' -f $PSBoundParameters['Credential'].UserName)
$script:myOffice365Services['Office365CredentialsMFA'] = $False
$script:myOffice365Services['Office365Credentials'] = $PSBoundParameters['Credential']
}
}
$script:myOffice365Services['Session365'] = ExchangeOnlineManagement\Connect-ExchangeOnline @PSBoundParameters
If ($script:myOffice365Services['Session365']) {
Import-PSSession -Session $global:myOffice365Services['Session365'] -AllowClobber
}
}
Function script:Connect-ExchangeOnPremises {
If (!($script:myOffice365Services['OnPremisesCredentials'])) {
Get-OnPremisesCredentials
}
If (!($script:myOffice365Services['ExchangeOnPremisesFQDN'])) {
Get-ExchangeOnPremisesFQDN
}
Write-Host ('Connecting to Exchange On-Premises {0} using {1} ..' -f $script:myOffice365Services['ExchangeOnPremisesFQDN'], $script:myOffice365Services['OnPremisesCredentials'].username)
$script:myOffice365Services['SessionExchange'] = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "http://$($script:myOffice365Services['ExchangeOnPremisesFQDN'])/PowerShell" -Credential $script:myOffice365Services['OnPremisesCredentials'] -Authentication Kerberos -AllowRedirection -SessionOption $script:myOffice365Services['SessionExchangeOptions']
If ($script:myOffice365Services['SessionExchange']) {
Import-PSSession -Session $script:myOffice365Services['SessionExchange'] -AllowClobber
}
}
Function script:Get-ExchangeOnPremisesFQDN {
$script:myOffice365Services['ExchangeOnPremisesFQDN'] = Read-Host -Prompt 'Enter Exchange On-Premises endpoint, e.g. exchange1.contoso.com'
}
Function script:Connect-ComplianceCenter {
If (!($script:myOffice365Services['Office365Credentials'])) {
Get-Office365Credentials
}
If ($script:myOffice365Services['Office365CredentialsMFA']) {
Write-Host ('Connecting to Security & Compliance Center using {0} with Modern Authentication ..' -f $script:myOffice365Services['Office365Credentials'].username)
$script:myOffice365Services['SessionCC'] = ExchangeOnlineManagement\Connect-IPPSSession -ConnectionUri $global:myOffice365Services['SCCConnectionEndpointUri'] -UserPrincipalName ($script:myOffice365Services['Office365Credentials']).UserName -PSSessionOption $script:myOffice365Services['SessionExchangeOptions']
} Else {
Write-Host ('Connecting to Security & Compliance Center using {0} ..' -f $script:myOffice365Services['Office365Credentials'].username)
$script:myOffice365Services['SessionCC'] = ExchangeOnlineManagement\Connect-IPPSSession -ConnectionUrl $script:myOffice365Services['SCCConnectionEndpointUri'] -Credential $script:myOffice365Services['Office365Credentials'] -PSSessionOption $script:myOffice365Services['SessionExchangeOptions']
}
If ($global:myOffice365Services['SessionCC']) {
Import-PSSession -Session $script:myOffice365Services['SessionCC'] -AllowClobber
}
}
Function script:Connect-EOP {
If (!($script:myOffice365Services['Office365Credentials'])) {
Get-Office365Credentials
}
If ($script:myOffice365Services['Office365CredentialsMFA']) {
Write-Host ('Connecting to Exchange Online Protection using {0} with Modern Authentication ..' -f $script:myOffice365Services['Office365Credentials'].username)
$script:myOffice365Services['SessionEOP'] = ExchangeOnlineManagement\Connect-IPPSSession -ConnectionUri $script:myOffice365Services['EOPConnectionEndpointUri'] -UserPrincipalName ($script:myOffice365Services['Office365Credentials']).UserName -PSSessionOption $script:myOffice365Services['SessionExchangeOptions']
} Else {
Write-Host ('Connecting to Exchange Online Protection using {0} ..' -f $script:myOffice365Services['Office365Credentials'].username)
$script:myOffice365Services['SessionEOP'] = ExchangeOnlineManagement\Connect-IPPSSession -ConnectionUrl $script:myOffice365Services['EOPConnectionEndpointUri'] -Credential $script:myOffice365Services['Office365Credentials'] -PSSessionOption $script:myOffice365Services['SessionExchangeOptions']
}
If ($script:myOffice365Services['SessionEOP'] ) {
Import-PSSession -Session $script:myOffice365Services['SessionEOP'] -AllowClobber
}
}
Function script:Connect-MSTeams {
If (!($script:myOffice365Services['Office365Credentials'])) {
Get-Office365Credentials
}
If ($script:myOffice365Services['Office365CredentialsMFA']) {
Write-Host ('Connecting to Microsoft Teams using {0} with Modern Authentication ..' -f $script:myOffice365Services['Office365Credentials'].username)
Connect-MicrosoftTeams -AccountId ($script:myOffice365Services['Office365Credentials']).UserName -TenantId $myOffice365Services['TenantId']
} Else {
Write-Host ('Connecting to Exchange Online Protection using {0} ..' -f $script:myOffice365Services['Office365Credentials'].username)
Connect-MicrosoftTeams -Credential $script:myOffice365Services['Office365Credentials']
}
}
Function script:Connect-SkypeOnline {
If ( !($script:myOffice365Services['Office365Credentials'])) {
Get-Office365Credentials
}
Write-Host ('Connecting to Skype Online using {0}' -f $script:myOffice365Services['Office365Credentials'].username)
$script:myOffice365Services['SessionSFBO'] = New-CsOnlineSession -Credential $global:myOffice365Services['Office365Credentials']
If ($script:myOffice365Services['SessionSFBO']) {
Import-PSSession -Session $script:myOffice365Services['SessionSFBO'] -AllowClobber
}
}
Function script:Connect-AzureActiveDirectory {
If (!(Get-Module -Name AzureAD)) {
Import-Module -Name AzureAD -ErrorAction SilentlyContinue
}
If (!(Get-Module -Name AzureADPreview)) {
Import-Module -Name AzureADPreview -ErrorAction SilentlyContinue
}
If ((Get-Module -Name AzureAD) -or (Get-Module -Name AzureADPreview)) {
If (!($script:myOffice365Services['Office365Credentials'])) {
Get-Office365Credentials
}
If ($script:myOffice365Services['Office365CredentialsMFA']) {
Write-Host 'Connecting to Azure Active Directory with Modern Authentication ..'
$Parms = @{
AccountId = $script:myOffice365Services['Office365Credentials'].UserName
AzureEnvironment = $script:myOffice365Services['AzureEnvironment']
}
} Else {
Write-Host ('Connecting to Azure Active Directory using {0} ..' -f $global:myOffice365Services['Office365Credentials'].username)
$Parms = @{
'Credential' = $script:myOffice365Services['Office365Credentials']
'AzureEnvironment' = $script:myOffice365Services['AzureEnvironment']
}
}
Connect-AzureAD @Parms
} Else {
If (!(Get-Module -Name MSOnline)) {
Import-Module -Name MSOnline -ErrorAction SilentlyContinue
}
If (Get-Module -Name MSOnline) {
If (!($script:myOffice365Services['Office365Credentials'])) {
Get-Office365Credentials
}
Write-Host ('Connecting to Azure Active Directory using {0} ..' -f $script:myOffice365Services['Office365Credentials'].username)
Connect-MsolService -Credential $script:myOffice365Services['Office365Credentials'] -AzureEnvironment $script:myOffice365Services['AzureEnvironment']
} Else {
Write-Error -Message 'Cannot connect to Azure Active Directory - problem loading module.'
}
}
}
Function script:Connect-AIP {
If (!(Get-Module -Name AIPService)) {Import-Module -Name AIPService -ErrorAction SilentlyContinue}
If (Get-Module -Name AIPService) {
If (!($script:myOffice365Services['Office365Credentials'])) {
Get-Office365Credentials
}
Write-Host ('Connecting to Azure Information Protection using {0}' -f $script:myOffice365Services['Office365Credentials'].username)
Connect-AipService -Credential $script:myOffice365Services['Office365Credentials']
} Else {
Write-Error -Message 'Cannot connect to Azure Information Protection - problem loading module.'
}
}
Function script:Connect-SharePointOnline {
If (!(Get-Module -Name Microsoft.Online.Sharepoint.PowerShell)) {
Import-Module -Name Microsoft.Online.Sharepoint.PowerShell -ErrorAction SilentlyContinue
}
If (Get-Module -Name Microsoft.Online.Sharepoint.PowerShell) {
If (!($script:myOffice365Services['Office365Credentials'])) {
Get-Office365Credentials
}
If (($script:myOffice365Services['Office365Credentials']).username -like '*.onmicrosoft.com') {
$script:myOffice365Services['Office365Tenant'] = ($script:myOffice365Services['Office365Credentials']).username.Substring(($script:myOffice365Services['Office365Credentials']).username.IndexOf('@') + 1).Replace('.onmicrosoft.com', '')
} Else {
If (!($script:myOffice365Services['Office365Tenant'])) {
Get-Office365Tenant
}
}
If ($script:myOffice365Services['Office365CredentialsMFA']) {
Write-Host 'Connecting to SharePoint Online with Modern Authentication ..'
$Parms = @{
url = 'https://{0}-admin.sharepoint.com' -f $($script:myOffice365Services['Office365Tenant'])
region = $script:myOffice365Services['SharePointRegion']
}
} Else {
Write-Host "Connecting to SharePoint Online using $($script:myOffice365Services['Office365Credentials'].username) .."
$Parms = @{
url = 'https://{0}-admin.sharepoint.com' -f $script:myOffice365Services['Office365Tenant']
credential = $script:myOffice365Services['Office365Credentials']
region = $script:myOffice365Services['SharePointRegion']
}
}
Connect-SPOService @Parms
} Else {
Write-Error -Message 'Cannot connect to SharePoint Online - problem loading module.'
}
}
Function script:Connect-PowerApps {
If (!(Get-Module -Name Microsoft.PowerApps.PowerShell)) {
Import-Module -Name Microsoft.PowerApps.PowerShell -ErrorAction SilentlyContinue
}
If (!(Get-Module -Name Microsoft.PowerApps.Administration.PowerShell)) {
Import-Module -Name Microsoft.PowerApps.Administration.PowerShell -ErrorAction SilentlyContinue
}
If (Get-Module -Name Microsoft.PowerApps.PowerShell) {
If (!($script:myOffice365Services['Office365Credentials'])) {
Get-Office365Credentials
}
Write-Host "Connecting to PowerApps using $($script:myOffice365Services['Office365Credentials'].username) .."
If ($script:myOffice365Services['Office365CredentialsMFA']) {
$Parms = @{
'Username' = $script:myOffice365Services['Office365Credentials'].UserName
}
} Else {
$Parms = @{
'Username' = $script:myOffice365Services['Office365Credentials'].UserName
'Password' = $script:myOffice365Services['Office365Credentials'].Password
}
}
Add-PowerAppsAccount @Parms
}
Else {
Write-Error -Message 'Cannot connect to SharePoint Online - problem loading module.'
}
}
Function script:Get-Office365Credentials {
$script:myOffice365Services['Office365Credentials'] = $host.ui.PromptForCredential('Office 365 Credentials', 'Please enter your Office 365 credentials', $script:myOffice365Services['Office365Credentials'].UserName, '')
$local:MFAenabledModulePresence = $true
$script:myOffice365Services['Office365CredentialsMFA'] = Get-MultiFactorAuthenticationUsage
Get-TenantID
Set-WindowTitle
}
Function script:Get-AllowPrereleaseModule {
If ($script:myOffice365Services['AllowPrerelease']) {
# Already asked
} Else {
$Answer = Read-host -Prompt 'Would you like to check for pre-release modules? (y/N) '
Switch ($Answer.ToUpper()) {
'Y' {$rval = $true}
Default {$rval = $false}
}
$script:myOffice365Services['AllowPrerelease']= $rval
}
}
Function script:Get-OnPremisesCredentials {
$script:myOffice365Services['OnPremisesCredentials'] = $host.ui.PromptForCredential('On-Premises Credentials', 'Please Enter Your On-Premises Credentials', '', '')
}
Function script:Get-Office365Tenant {
$script:myOffice365Services['Office365Tenant'] = Read-Host -Prompt 'Enter tenant ID, e.g. contoso for contoso.onmicrosoft.com'
}
Function script:Get-ModuleScope {
[CmdletBinding()]
Param ($Module)
If ($Module.ModuleBase -ilike ('{0}*' -f (Join-Path -Path $ENV:HOMEDRIVE -ChildPath $ENV:HOMEPATH))) {
'CurrentUser'
} Else {
'AllUsers'
}
}
Function script:Get-ModuleVersionInfo {
[CmdletBinding()]
Param ($Module)
$ModuleManifestPath = $Module.Path
$isModuleManifestPathValid = Test-Path -Path $ModuleManifestPath
If(!($isModuleManifestPathValid)) {
# Module manifest path invalid, skipping extracting prerelease info
$ModuleVersion= $Module.Version.ToString()
} Else {
$ModuleManifestContent = Get-Content -Path $ModuleManifestPath
$preReleaseInfo = $ModuleManifestContent -match "Prerelease = '(.*)'"
If ($preReleaseInfo) {
$preReleaseVersion = $preReleaseInfo[0].Split('=')[1].Trim().Trim("'")
If ($preReleaseVersion) {
$ModuleVersion = ('{0}-{1}' -f $Module.Version.ToString(), $preReleaseVersion)
} Else {
$ModuleVersion = $Module.Version.ToString()
}
} Else {
$ModuleVersion = $Module.Version.ToString()
}
}
$ModuleVersion
}
Function script:Update-Office365Modules {
Get-AllowPrereleaseModule
$local:Functions= Get-Office365ModuleInfo
$local:IsAdmin = [Security.principal.windowsprincipal]::new([Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
If ($local:IsAdmin) {
If ((Get-Process -Name powershell, pwsh -ErrorAction SilentlyContinue | Measure-Object).Count -gt 1) {
Write-Warning -Message ('Running multiple PowerShell sessions, successful updating might be problematic.')
}
ForEach ($local:Function in $local:Functions) {
$local:Item = ($local:Function).split('|')
If ($local:Item[3]) {
$local:Module = Get-Module -Name ('{0}' -f $local:Item[3]) -ListAvailable | Sort-Object -Property Version -Descending
$local:CheckThisModule = $false
If (([uri]($local:Module | Select-Object -First 1).RepositorySourceLocation).Authority -eq (([uri]$local:Item[5])).Authority) {
$local:CheckThisModule = $true
}
If ($local:CheckThisModule) {
If ($local:Item[5]) {
$local:Module = $local:Module | Where-Object {([uri]($_.RepositorySourceLocation)).Authority -ieq ([uri]($local:Item[5])).Authority } | Select-Object -First 1
} Else {
$local:Module = $local:Module | Select-Object -First 1
}
If (($local:Module).RepositorySourceLocation) {
$local:Version = Get-ModuleVersionInfo -Module $local:Module
Write-Host ('Checking {0}' -f $local:Item[4]) -NoNewLine
$local:NewerAvailable= $false
If ($local:Item[5]) {
$local:Repo = $local:Repos | Where-Object {([uri]($_.SourceLocation)).Authority -eq (([uri]$local:Item[5])).Authority}
}
If([string]::IsNullOrEmpty($local:Repo)) {$local:Repo = 'PSGallery'} Else {$local:Repo = ($local:Repo).Name}
$OnlineModule = Find-Module -Name $local:Item[3] -Repository $local:Repo -AllowPrerelease:$script:myOffice365Services['AllowPrerelease'] -ErrorAction SilentlyContinue
If ($OnlineModule) {
Write-Host (': Local:{0}, Online:{1}' -f $local:Version, $OnlineModule.version)
If ((Compare-TextVersionNumber -Version $local:Version -CompareTo $OnlineModule.version) -eq 1) {
$local:NewerAvailable = $true
} #Else {# Local module up to date or newer}
} Else {
# Not installed from online or cannot determine
Write-Host ('Local:{0} Online:N/A' -f $local:Version)
}
If ($local:NewerAvailable) {
$local:UpdateSuccess = $false
Try {
$Parm = @{
AllowPrerelease = $script:myOffice365Services['AllowPrerelease']
Force = $True
Confirm = $False
Scope = Get-ModuleScope -Module $local:Module
AllowClobber = $True
}
# Pass AcceptLicense if current version of UpdateModule supports it
If ((Get-Command -name Update-Module).Parameters['AcceptLicense']) {
$Parm.AcceptLicense = $True
}
If (Get-Command -Name Install-Package -ErrorAction SilentlyContinue) {
If ((Get-Command -name Install-Package).Parameters['SkipPublisherCheck']) {
$Parm.SkipPublisherCheck = $True
}
Install-Package -Name $local:Item[3] -Source $local:Repo @Parm | Out-Null
} Else {
Update-Module -Name $local:Item[3] @Parm
}
$local:UpdateSuccess= $true
} Catch {
Write-Error -Message ('Problem updating module {0}:{1}' -f $local:Item[3], $Error[0].Message)
}
If ($local:UpdateSuccess) {
If (Get-Command -Name Get-InstalledModule -ErrorAction SilentlyContinue) {
$local:ModuleVersions = Get-InstalledModule -Name $local:Item[3] -AllVersions
} Else {
$local:ModuleVersions = Get-Module -Name $local:Item[3] -ListAvailable -All
}
$local:Module = $local:ModuleVersions | Sort-Object -Property @{e={ [version]($_.Version -replace '[^\d\.]','')}} -Descending | Select-Object -First 1
$local:LatestVersion = ($local:Module).Version
Write-Host ('Updated {0} to version {1}' -f $local:Item[4], $local:LatestVersion) -ForegroundColor Green
# Uninstall all old versions of dependencies
If ($OnlineModule) {
ForEach ($DependencyModule in $OnlineModule.Dependencies) {
# Unload
Remove-Module -Name $DependencyModule.Name -Force -Confirm:$False -ErrorAction SilentlyContinue
$local:DepModuleVersions = Get-Module -Name $DependencyModule.Name -ListAvailable
$local:DepModule = $local:DepModuleVersions | Sort-Object -Property @{e={[version]($_.Version -replace '[^\d\.]','')}} -Descending | Select-Object -First 1
$local:DepLatestVersion = ($local:DepModule).Version
$local:OldDepModules = $local:DepModuleVersions | Where-Object {$_.Version -ne $local:DepLatestVersion}
ForEach ($DepModule in $local:OldDepModules) {
Write-Host ('Uninstalling dependency module {0} version {1}' -f $DepModule.Name, $DepModule.Version)
Try {
$DepModule | Uninstall-Module -Confirm:$false -Force
} Catch {
Write-Error -Message ('Problem uninstalling module {0} version {1}' -f $DepModule.Name, $DepModule.Version)
}
}
}
}
# Uninstall all old versions of the module
$local:OldModules = $local:ModuleVersions | Where-Object {$_.Version -ne $local:LatestVersion}
If ($local:OldModules) {
# Unload module when currently loaded
Remove-Module -Name $local:Item[3] -Force -Confirm:$False -ErrorAction SilentlyContinue
ForEach ($OldModule in $local:OldModules) {
Write-Host ('Uninstalling {0} version {1}' -f $local:Item[4], $OldModule.Version) -ForegroundColor White
Try {
$OldModule | Uninstall-Module -Confirm:$false -Force
} Catch {
Write-Error -Message ('Problem uninstalling module {0} version {1}' -f $OldModule.Name, $OldModule.Version)
}
}
}
} #Else {# Problem during update}
} #Else {# No update available}
} Else {
Write-Host ('Skipping {0}: Not installed using PowerShellGet/Install-Module' -f $local:Item[4]) -ForegroundColor Yellow
}
}
}
}
} Else {
Write-Host ('Script not running with elevated privileges; cannot update modules') -ForegroundColor Yellow
}
}
# Compare-TextVersionNumber to handle (rich) version comparison, similar to [System.Version]'s CompareTo method
# 1=CompareTo is newer, 0 = Equal, -1 = Version is Newer
Function script:Compare-TextVersionNumber {
[CmdletBinding()]
Param (
[String]$Version,
[String]$CompareTo
)
$res = 0
$null = $Version -match '^(?<version>[\d\.]+)(\-)?([a-zA-Z]*(?<preview>[\d]*))?$'
$VersionVer = [version]($matches.Version)
If ($matches.Preview) {
# Suffix .0 to satisfy SystemVersion as '#' won't initialize
$VersionPreviewVer = [version]('{0}.0' -f $matches.Preview)
} Else {
$VersionPreviewVer = [version]'99999.99999'
}
$null = $CompareTo -match '^(?<version>[\d\.]+)(\-)?([a-zA-Z]*(?<preview>[\d]*))?$'
$CompareToVer = [version]($matches.Version)
If ($matches.Preview) {
$CompareToPreviewVer = [version]('{0}.0' -f $matches.Preview)
} Else {
$CompareToPreviewVer = [version]'99999.99999'
}
If ($VersionVer -gt $CompareToVer) {
$res = -1
} Else {
If ($VersionVer -lt $CompareToVer) {
$res = 1
} Else {
# Equal - Check Preview Tag
If ($VersionPreviewVer -gt $CompareToPreviewVer) {
$res = -1
} Else {
If ($VersionPreviewVer -lt $CompareToPreviewVer) {
$res = 1
} Else {
# Really Equal
$res = 0
}
}
}
}
$res
}
Function script:Report-Office365Modules {
Get-AllowPrereleaseModule
$local:Functions = Get-Office365ModuleInfo
$local:Repos = Get-PSRepository
ForEach ($local:Function in $local:Functions) {
$local:Item = ($local:Function).split('|')
If ($local:Item[3]) {
$local:Module = Get-Module -Name ('{0}' -f $local:Item[3]) -ListAvailable | Sort-Object -Property Version -Descending
# Use specific or default repository
If ($local:Item[5]) {
$local:Repo= $local:Repos | Where-Object {([uri]($_.SourceLocation)).Authority -eq (([uri]$local:Item[5])).Authority}
}
If ([string]::IsNullOrEmpty($local:Repo)) {
$local:Repo = 'PSGallery'
} Else {
$local:Repo = ($local:Repo).Name
}
If ($local:Item[5]) {
$local:Module = $local:Module | Where-Object {([uri]($_.RepositorySourceLocation)).Authority -ieq ([uri]($local:Item[5])).Authority } | Select-Object -First 1
} Else {
$local:Module = $local:Module | Select-Object -First 1
}
If ($local:Module) {
$local:Version = Get-ModuleVersionInfo -Module $local:Module
Write-Host ('Module {0}: Local v{1}' -f $local:Item[4], $Local:Version) -NoNewline
$OnlineModule = Find-Module -Name $local:Item[3] -Repository $local:Repo -AllowPrerelease:$script:myOffice365Services['AllowPrerelease'] -ErrorAction SilentlyContinue
If ($OnlineModule) {
Write-Host (', Online v{0}' -f $OnlineModule.version) -NoNewline
} Else {
Write-Host (', Online N/A') -NoNewline
}
Write-Host (', Scope:{0} Status:' -f (Get-ModuleScope -Module $local:Module)) -NoNewline
If ([string]::IsNullOrEmpty( $local:Version) -or [string]::IsNullOrEmpty( $OnlineModule.version)) {
Write-Host ('Unknown')
} Else {
If ((Compare-TextVersionNumber -Version $local:Version -CompareTo $OnlineModule.version) -eq 1) {
Write-Host ('Outdated') -ForegroundColor Red
} Else {
Write-Host ('OK') -ForegroundColor Green
}
}
} Else {
Write-Host ('{0} module not found ({1})' -f $local:Item[4], $local:Item[5])
}
}
}
}
Function script:Connect-Office365 {
Connect-AzureActiveDirectory
Connect-AzureRMS
Connect-ExchangeOnline
Connect-MSTeams
Connect-SkypeOnline
Connect-EOP
Connect-ComplianceCenter
Connect-SharePointOnline
}
$PSGetModule = Get-Module -Name PowerShellGet -ListAvailable -ErrorAction SilentlyContinue | Sort-Object -Property Version -Descending | Select-Object -First 1
If (!$PSGetModule) {
$PSGetVer = 'N/A'
} Else {
$PSGetVer = $PSGetModule.Version
}
$PackageManagementModule = Get-Module -Name PackageManagement -ListAvailable -ErrorAction SilentlyContinue | Sort-Object -Property Version -Descending | Select-Object -First 1
If (!$PackageManagementModule) {
$PMMVer = 'N/A'
} Else {
$PMMVer = $PackageManagementModule.Version
}
Write-Host ('*' * 78)
Write-Host ('Connect-Office365Services v{0}' -f $local:ScriptVersion)
# See if the Administator built-in role is part of your role
$local:IsAdmin = [Security.principal.windowsprincipal]::new([Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
$local:CreateISEMenu = $psISE -and -not [Windows.Input.Keyboard]::IsKeyDown([Windows.Input.Key]::LeftShift)
If ($local:CreateISEMenu) {
Write-Host 'ISE detected, adding ISE menu options'
}
# Initialize global state variable when needed
If (-not (Get-Variable -Name myOffice365Services -ErrorAction SilentlyContinue)) {
$script:myOffice365Services = @{}
}
# Local Exchange session options
$script:myOffice365Services['SessionExchangeOptions'] = New-PSSessionOption -ProxyAccessType None
# Initialize environment & endpoints
Set-Office365Environment -AzureEnvironment 'Default'
Write-Host ('Environment:{0}, Administrator:{1}' -f $global:myOffice365Services['AzureEnvironment'], $local:IsAdmin)
Write-Host ('Architecture:{0}, PS:{1}, PSGet:{2}, PackageManagement:{3}' -f ($ENV:PROCESSOR_ARCHITECTURE), ($PSVersionTable).PSVersion, $PSGetVer, $PMMVer)
Write-Host ('*' * 78)
$local:Functions = Get-Office365ModuleInfo
$local:Repos = Get-PSRepository
Write-Host ('Collecting Module information ..')
If (Get-Module -Name 'SkypeOnlineConnector' -ListAvailable) {
Write-Warning -Message 'Notice: The Skype for Business Online Connector PowerShell module functionality has moved to the Microsoft Teams module. Module retired February 15th, 2021.'
}
If (Get-Module -Name 'Microsoft.Exchange.Management.ExoPowershellModule' -ListAvailable) {
Write-Warning -Message 'Notice: The Exchange Online PowerShell module has been replaced by the Exchange Online Management module.'
}
ForEach ($local:Function in $local:Functions) {
$local:Item = ($local:Function).split('|')
If ($local:Item[3]) {
$local:Module = Get-Module -Name ('{0}' -f $local:Item[3]) -ListAvailable | Sort-Object -Property Version -Descending
$local:ModuleMatch = ([uri]($local:Module | Select-Object -First 1).RepositorySourceLocation).Authority -eq ([uri]$local:Item[5]).Authority
If ($local:ModuleMatch) {
If ($local:CreateISEMenu) {
$local:MenuObj = $psISE.CurrentPowerShellTab.AddOnsMenu.SubMenus | Where-Object -FilterScript { $_.DisplayName -eq $local:Item[0] }
If (!($local:MenuObj)) {
Try {
$local:MenuObj = $psISE.CurrentPowerShellTab.AddOnsMenu.SubMenus.Add( $local:Item[0], $null, $null)
} Catch {
Write-Warning -Message $_
}
}
Try {
$local:RemoveItems = $local:MenuObj.Submenus | Where-Object -FilterScript {$_.DisplayName -eq $local:Item[1] -or $_.Action -eq $local:Item[2]}
$null = $local:RemoveItems | ForEach-Object -Process {
$local:MenuObj.Submenus.Remove($_)
}
$null = $local:MenuObj.SubMenus.Add( $local:Item[1], [ScriptBlock]::Create( $local:Item[2]), $null)
} Catch {
Write-Warning -Message $_
}
}
If ($local:Item[3]) {
$local:Module = $local:Module | Sort-Object -Property @{e={[version]($_.Version -replace '[^\d\.]','')}} -Descending
If ($local:Item[5]) {
$local:Module = $local:Module | Where-Object {([uri]($_.RepositorySourceLocation)).Authority -ieq ([uri]($local:Item[5])).Authority } | Select-Object -First 1
} Else {
$local:Module = $local:Module | Select-Object -First 1
}
$local:Version = Get-ModuleVersionInfo -Module $local:Module
Write-Host ('Found {0} module (v{1})' -f $local:Item[4], $local:Version) -ForegroundColor Green
}
} #Else {# Module not found}
}
}
}
#endregion Updates/Misc
#region Computer
Function Get-BLPass {
<#
.SYNOPSIS
Get BitLocker Recovery Password.
.DESCRIPTION
Get Bitlocker recovery key from AD.
.PARAMETER PC
Name of Computer.
.PARAMETER credential
PreSet credentials for yur SA.
.EXAMPLE
Get-BLPass -PC <ComputerName>
.INPUTS
String
.OUTPUTS
Array
#>
Param (
[Parameter(
Mandatory = $True,
HelpMessage = 'Name of PC'
)]
[String]$PC,
[System.Management.Automation.Credential()]
[pscredential]$Credential = $Credential
)
$session = New-PSSession -ComputerName $PDC -Credential $credential
$BO = Invoke-Command -Session $session -ScriptBlock {
Param ([Parameter(Mandatory = $True, HelpMessage = 'Name of PC')][String]$cpu)
$C = Get-ADComputer -Identity $cpu -Server $script:PDC
$ADP = @{
Filter = "objectclass -eq 'msFVE-RecoveryInformation'"
SearchBase = $C.DistinguishedName
Properties = 'msFVE-RecoveryPassword'
Server = $script:PDC
}
$B = Get-ADObject @ADP
return $B
} -ArgumentList $PC
return $BO.'msFVE-RecoveryPassword'
}
Function Get-DirectoryTreeSize {
<#
.SYNOPSIS
This is used to get the file count, subdirectory count and folder size for the path specified. The output will show the current folder stats unless you specify the "AllItemsAndAllFolders" property.
Since this uses Get-ChildItem as the underlying structure, this supports local paths, network UNC paths and mapped drives.
.NOTES
Name: Get-DirectoryTreeSize
Author: theSysadminChannel
Version: 1.0
DateCreated: 2020-Feb-11
.LINK
https://thesysadminchannel.com/get-directory-tree-size-using-powershell -
.PARAMETER Recurse
Using this parameter will drill down to the end of the folder structure and output the filecount, foldercount and size of each folder respectively.
.PARAMETER AllItemsAndAllFolders
Using this parameter will get the total file count, total directory count and total folder size in MB for everything under that directory recursively.
.EXAMPLE
Get-DirectoryTreeSize "C:\Some\Folder"
Path FileCount DirectoryCount FolderSizeInMB
---- --------- -------------- --------------
C:\Some\folder 3 3 0.002
.EXAMPLE
Get-DirectoryTreeSize "\\MyServer\Folder" -Recurse
Path FileCount DirectoryCount FolderSizeInMB
---- --------- -------------- --------------
\\MyServer\Folder 2 1 40.082
.\Subfolder 1 0 26.555
.EXAMPLE
Get-DirectoryTreeSize "\\MyServer\Folder" -Folder
Path FileCount DirectoryCount FolderSizeInMB
---- --------- -------------- --------------
\\MyServer\Folder 2 1 40.082
.\Subfolder 1 0 26.555
Shows size of Top-Level-Folders within target
.EXAMPLE
Get-DirectoryTreeSize "Z:\MyMapped\folder" -AllItemsAndAllFolders
Path TotalFileCount TotalDirectoryCount TotalFolderSizeInMB
---- -------------- ------------------- -------------------
Z:\MyMapped\folder 3 1 68.492
.INPUTS
String
.OUTPUTS
Array
#>
[Alias('du')]
[CmdletBinding(DefaultParameterSetName = 'Default')]
Param(
[Parameter(
Mandatory = $True,
Position = 0,
HelpMessage = 'Path to check'
)]
[String]$Path,
[Parameter(
ParameterSetName='ShowRecursive'
)]
[Switch]$Recurse,
[Alias('All')]
[Parameter(
ParameterSetName = 'ShowTopFolderAllItemsAndAllFolders'
)]
[switch]$AllItemsAndAllFolders,
[Alias('Folders')]
[Parameter(
ParameterSetName='TopLevelFolders'
)]
[Switch]$TopLevelFolders
)
Begin {
#$ErrorActionPreference = "SilentlyContinue"
#Adding a trailing slash at the end of $path to make it consistent.
If (-not $Path.EndsWith('\')) {
$Path = "$Path\"
}
Function Get-HumanReadable {
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'Value to evaluate required'
)]
$size
)
Switch ($size){
{$_ -lt 1kb} {
$size = ([math]::Round($size / 1kb,2))
$type = 'B'
$index = 0
Break
}
{$_ -lt 1mb} {
$size = ([math]::Round($size / 1kb,2))
$type = 'KB'
$index = 1
Break
}
{$_ -lt 1gb} {
$size = ([math]::Round($size / 1mb,2))
$type = 'MB'
$index = 2
Break
}
{$_ -lt 1tb} {
$size = ([math]::Round($size / 1gb,2))
$type = 'GB'
$index = 3
Break
}
{$_ -lt 1pb} {
$size = ([math]::Round($size / 1tb,2))
$type = 'TB'
$index = 4
Break
}
}
$obj = New-Object -TypeName PsObject -Property @{
size = $size
Type = $type
Index = $index
}
Return $obj
}
#>
$returnList = New-Object -TypeName System.Collections.Generic.List[PsObject]
}
Process {
Try {
If (
-not $PSBoundParameters.ContainsKey('AllItemsAndAllFolders') -and
-not $PSBoundParameters.ContainsKey('Recurse')
) {
$FileStats = Get-ChildItem -Path $Path -File -ErrorAction Stop | Measure-Object -Property Length -Sum
$FileCount = $FileStats.Count
$DirectoryCount = Get-ChildItem -Path $Path -Directory | Measure-Object | Select-Object -ExpandProperty Count
$size = Get-HumanReadable -size $FileStats.Sum
$properties = [Ordered]@{
Path = $Path
FileCount = $FileCount
DirectoryCount = $DirectoryCount
Size = $size.size
Type = $size.type
Index = $size.index
}
$obj = New-Object -TypeName PsObject -Property $properties
$returnList.Add($obj)
}
If ($PSBoundParameters.ContainsKey('AllItemsAndAllFolders')) {
$FileStats = Get-ChildItem -Path $Path -File -Recurse -ErrorAction Stop | Measure-Object -Property Length -Sum
$FileCount = $FileStats.Count
$DirectoryCount = Get-ChildItem -Path $Path -Directory -Recurse | Measure-Object | Select-Object -ExpandProperty Count
$size = Get-HumanReadable -size $FileStats.Sum
$properties = [Ordered]@{
Path = $Path
TotalFileCount = $FileCount
TotalDirectoryCount = $DirectoryCount
Size = $size.size
Type = $size.type
Index = $size.index
}
$obj = New-Object -TypeName PsObject -Property $properties
$returnList.Add($obj)
}
If ($PSBoundParameters.ContainsKey('Recurse')) {
Get-DirectoryTreeSize -Path $Path
$FolderList = Get-ChildItem -Path $Path -Directory -Recurse | Select-Object -ExpandProperty FullName
If ($FolderList) {
ForEach ($Folder in $FolderList) {
$FileStats = Get-ChildItem -Path $Folder -File | Measure-Object -Property Length -Sum
$FileCount = $FileStats.Count
$DirectoryCount = Get-ChildItem -Path $Folder -Directory | Measure-Object | Select-Object -ExpandProperty Count
$size = Get-HumanReadable -size $FileStats.Sum
$properties = [Ordered]@{
Path = $Folder#Replace($Path,'.\')
FileCount = $FileCount
DirectoryCount = $DirectoryCount
Size = $size.Size
Type = $size.Type
Index = $size.Index
}
$obj = New-Object -TypeName PsObject -Property $properties
$returnList.Add($obj)
$FileStats,$FileCount,$DirectoryCount,$Size = $null #clearing variables
}
}
}
If ($PSBoundParameters.ContainsKey('TopLevelFolders')) {
Get-DirectoryTreeSize -Path $Path
$FolderList = Get-ChildItem -Path $Path -Directory | Select-Object -ExpandProperty FullName
If ($FolderList) {
ForEach ($Folder in $FolderList) {
$FileStats = Get-ChildItem -Path $Folder -File | Measure-Object -Property Length -Sum
$FileCount = $FileStats.Count
$DirectoryCount = Get-ChildItem -Path $Folder -Directory | Measure-Object | Select-Object -ExpandProperty Count
$size = Get-HumanReadable -size $FileStats.Sum
$properties = [Ordered]@{
Path = $Folder#.Replace($Path,'.\')
FileCount = $FileCount
DirectoryCount = $DirectoryCount
Size = $size.Size
Type = $size.Type
Index = $size.Index
}
$obj = New-Object -TypeName PsObject -Property $properties
$returnList.Add($obj)
$FileStats,$FileCount,$DirectoryCount,$Size = $null #clearing variables
}
}
}
} Catch {
Write-Error -Message $_.Exception.Message
}
}
End {
Return $returnList | Sort-Object -Property Index, Size -Descending | Select-Object -Property * -ExcludeProperty Index
}
}
Function Get-DiskFree {
<#
.SYNOPSIS
Script is used to display disk information similar to Linux df command
.DESCRIPTION
The df utility displays statistics about the amount of free disk space
on the specified filesystem. This PowerShell function attempts to
encapsulates the core functionality of the NIX command for the Windows platform.
The script queries the removable disk, local disk, network, CD/DVD, and
ram disk drive types and creates a custom PowerShell object for output.
.PARAMETER Computername
What system do you want to test connection to (you can also use hostname or cn)
.PARAMETER Format
Instead of having the function return separate PowerShell "disk" objects, if you
prefer to have the collection output in a structured table format with human-readable
numbers. This would be similar to the *nix df command output with the -h option.
Note: The -Format option should only be enabled when no further numeric operations
will need to be performed on the Available, Size, and Used properties. The option
converts these values to the string data type.
.PARAMETER Credential
If you're querying a remote computer you may need to specify a remote credential
such as 'example\administrator' (This is aliased as runas as well)
.EXAMPLE
PS> Get-DiskFree
FileSystem : NTFS
Type : Local Fixed Disk
Used : 13246943232
Volume : C:
Available : 29595770880
Computer : DC01
Size : 42842714112
FileSystem : CDFS
Type : CD-ROM Disc
Used : 623890432
Volume : D:
Available : 0
Computer : DC01
Size : 623890432
FileSystem : NTFS
Type : Network Connection
Used : 16416772096
Volume : Z:
Available : 26425942016
Computer : DC01
Size : 42842714112
.EXAMPLE
PS> $cred = Get-Credential -Credential 'example\administrator'
PS> 'db01','sp01' | Get-DiskFree -Credential $cred -Format | ft -GroupBy Name -auto
Name: DB01
Name Vol Size Used Avail Use% FS Type
---- --- ---- ---- ----- ---- -- ----
DB01 C: 39.9G 15.6G 24.3G 39 NTFS Local Fixed Disk
DB01 D: 4.1G 4.1G 0B 100 CDFS CD-ROM Disc
Name: SP01
Name Vol Size Used Avail Use% FS Type
---- --- ---- ---- ----- ---- -- ----
SP01 C: 39.9G 20G 19.9G 50 NTFS Local Fixed Disk
SP01 D: 722.8M 722.8M 0B 100 UDF CD-ROM Disc
This example is to query multiple Computers, as specific user, and make human readable
.EXAMPLE
PS> Import-Module ActiveDirectory
PS> $servers = Get-ADComputer -Filter { OperatingSystem -like '*win*server*' } | Select-Object -ExpandProperty Name
PS> Get-DiskFree -cn $servers | Where-Object { ($_.Volume -eq 'C:') -and ($_.Available / $_.Size) -lt .20 } | Select-Object Computer
Computer
--------
FS01
FS03
This example gets a list of windows servers in AD that have disk space below 20% on c:
.Example
PS> $cred = Get-Credential 'example\administrator'
PS> $servers = 'dc01','db01','exch01','sp01'
PS> Get-DiskFree -Credential $cred -cn $servers -Format | ? { $_.Type -like '*fixed*' } | select * -ExcludeProperty Type | Out-GridView -Title 'Windows Servers Storage Statistics'
Get local harddrive info of four select servers and output to grid
.EXAMPLE
PS> $cred = Get-Credential 'example\administrator'
PS> $servers = 'dc01','db01','exch01','sp01'
PS> Get-DiskFree -Credential $cred -cn $servers -Format | ? { $_.Type -like '*fixed*' } | sort 'Use%' -Descending | select -Property Name,Vol,Size,'Use%' | Export-Csv -Path $HOME\Documents\windows_servers_storage_stats.csv -NoTypeInformation
Output CSV with previous example
.INPUTS
None. You cannot pipe objects to Get-DiskFree through normal usage
.OUTPUTS
System.Array. Get-DiskFree returns an array with the following properties
Available uint64 (-Format = Avail)
Computer string
FileSystem string (-Format = FS)
Size uint64
Type string
Used uint64
Volume string (-Format = Vol)
.NOTES
Version: 1.0
.LINK
https://binarynature.blogspot.com/2010/04/powershell-version-of-df-command.html
#>
[Alias('DF')]
Param (
[Parameter(
Position = 0,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true
)]
[Alias('hostname')]
[Alias('cn')]
[String[]]$ComputerName = $env:COMPUTERNAME,
[Parameter(Position=1)]
[Alias('runas')]
[System.Management.Automation.Credential()]
[PSCredential]$Credential = $Credential,
[Parameter(Position=2)]
[Switch]$Format
)
Begin {
$wmiq = 'SELECT * FROM Win32_LogicalDisk WHERE Size != Null AND DriveType >= 2'
}
Process {
ForEach ($computer in $ComputerName) {
Try {
If ($computer -eq $env:COMPUTERNAME) {
$disks = Get-WmiObject -Query $wmiq -ComputerName $computer -ErrorAction Stop
} Else {
$disks = Get-WmiObject -Query $wmiq -ComputerName $computer -Credential $Credential -ErrorAction Stop
}
If ($Format) {
# Create array for $disk objects and then populate
$diskarray = @()
$disks | ForEach-Object {$diskarray += $_}
$diskarray | Select-Object -Property @{n='Name';e={$_.SystemName}},
@{n='Vol';e={$_.DeviceID}},
VolumeName,
@{n='Size';e={Optimize-HumanReadable -size $_.Size}},
@{n='Used';e={Optimize-HumanReadable -size (($_.Size)-($_.FreeSpace))}},
@{n='Avail';e={Optimize-HumanReadable -size $_.FreeSpace}},
@{n='Use%';e={"$([int](((($_.Size)-($_.FreeSpace))/($_.Size) * 100)))%"}},
@{n='FS';e={$_.FileSystem}},
@{n='Type';e={$_.Description}}
} Else {
Foreach ($disk in $disks) {
$diskprops = [Ordered]@{
'Computer' = $disk.SystemName
'Volume' = $disk.DeviceID
'VolumeName' = $disk.VolumeName
'Size' = $disk.Size
'Used' = ($disk.Size - $disk.FreeSpace)
'Available' = $disk.FreeSpace
'Use%' = "$([int](((($disk.Size)-($disk.FreeSpace))/($disk.Size) * 100)))%"
'FileSystem' = $disk.FileSystem
'Type' = $disk.Description
}
# Create custom PS object and apply type
$diskobj = New-Object -TypeName PSObject -Property $diskprops
$diskobj.PSObject.TypeNames.Insert(0,'BinaryNature.DiskFree')
Write-Output -InputObject $diskobj
}
}
} Catch {
# Check for common DCOM errors and display "friendly" output
Switch ($_) {
{$_.Exception.ErrorCode -eq 0x800706ba} {
$err = 'Unavailable (Host Offline or Firewall)'
Break
}
{$_.CategoryInfo.Reason -eq 'UnauthorizedAccessException'}{
$err = 'Access denied (Check User Permissions)'
Break
}
default {$err = $_.Exception.Message}
}
Write-Warning -Message "$computer - $err"
}
}
}
#END {}
}
Function Get-DiskUsage {
<#
.SYNOPSIS
similar to du function
.DESCRIPTION
Gets size of objects in a path
.EXAMPLE
Get-DiskUsage C:\down | Format-Table -Property Name,Size,ActualSize -Autosize
.INPUTS
String
.OUTPUTS
Array
#>
[Alias('DiskUsage')]
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'Target folder required'
)]
[String]$dufolder
)
$results = @()
$target = Get-ChildItem -Path $dufolder
ForEach ($item in $target) {
$f = $item
$found = Get-ChildItem -Recurse -Path $f.FullName |
Measure-Object -Property length -Sum |
Select-Object -Property @{n='Name';e={$_.Name}},
@{n='Size';e={Optimize-HumanReadable -size $_.Sum}},
@{n='ActualSize';e={'{0:n0}' -f ($_.Sum) + ' Bytes'}},
Sum
$results += $found
}
$results = $results | Sort-Object -Property Sum -Descending
Return $results
}
Function Get-FileSystem {
<#
.SYNOPSIS
Get file system details from remote computer.
.DESCRIPTION
Get remote file system details inclding free space, total size, file system type.
By design it ignores Removable drives (Floppy/CD/DVD).
.PARAMETER srv
Computer Name.
.EXAMPLE
Get-FileSystem -srv TestComputername
PSComputerName Name DriveType DriveFormat IsReady FreeSpace UsedSpace TotalSize Root VolumeLabel
-------------- ---- --------- ----------- ------- --------- --------- --------- ---- -----------
TestComputername C:\ Fixed NTFS True 35.8 GB 163.7 GB 199.4 GB C:\
TestComputername E:\ Fixed ReFS True 346.1 GB 403.7 GB 749.8 GB E:\ Data
TestComputername F:\ Fixed ReFS True 595 GB 154.8 GB 749.8 GB F:\ Log
TestComputername G:\ Fixed ReFS True 395.9 GB 116 GB 511.8 GB G:\ Backups
.INPUTS
String
.OUTPUTS
Array
#>
Param (
[Parameter(Position=0)]
[String]$srv = $env:COMPUTERNAME
)
Begin {
$Sel = @{
Property = 'PSComputerName',
'Name',
'DriveType',
'DriveFormat',
'IsReady',
@{n='FreeSpace';e={Optimize-HumanReadable -size $_.AvailableFreeSpace}},
@{n='UsedSpace';e={Optimize-HumanReadable -size $($_.TotalSize - $_.AvailableFreeSpace)}},
@{n='TotalSize';e={Optimize-HumanReadable -size $_.TotalSize}},
@{n='Root';e={$_.RootDirectory}},
'VolumeLabel'
}
}
Process {
$res = Invoke-Command -ComputerName $srv -ScriptBlock {[IO.DriveInfo]::getdrives()}
}
End {
### Remove Floppy/CD/DVD drives
### IgnoreFields: RunspaceId, TotalFreeSpace, (Calculate UsedSpace)
$return = $res | Where-Object {$_.DriveType -notmatch 'CD|Removable'} | Select-Object @Sel
return $return
}
}
Function Get-OutDatedOS {
<#
.SYNOPSIS
Find Computers with EOL Operating Systems on domain.
.DESCRIPTION
Search AD Computer objects running an OS that's End of Life.
.EXAMPLE
Get-OutDatedOS | Format-Table -Auto
Returns all EOL OS's registered with domain (Name, OS, IP, LastLogonDate, Container) | table format results returned.
.INPUTS
None
.OUTPUTS
Array
#>
$ODParams = @{
Filter = 'Enabled -eq $true -and OperatingSystem -like "*Windows*"'
Properties = 'OperatingSystem', 'IPv4Address', 'CanonicalName', 'LastLogonDate'
Server = $script:PDC
}
$odsParams = @{
Property = 'Name',
'OperatingSystem',
@{
n = 'IP'
e = {
If ($null -eq $_.IPv4Address){
'Off'
} Else {
$_.IPv4Address
}
}
},
'LastLogonDate',
@{n='Container';e={Get-Container -can $_.CanonicalName}}
}
$OutDatedOSs = Get-ADComputer @ODParams |
Where-Object {$_.OperatingSystem -match '2000|2003|2008|xp|7|8.1'} |
Select-Object @odsParams |
Sort-Object -Property OperatingSystem, Container, Name
Return $OutDatedOSs
}
Function Get-PrinterInfo {
<#
.SYNOPSIS
Script to create a Excel spreadsheet with detailed information about
the printers installed on the server
.DESCRIPTION
Script was designed to give you a good description of how your print
server(s) are installed and configured.
* Requires Microsoft Excel be installed on the workstation you are running
the script from.
.PARAMETER PrintServers
Name of the server you wish to run the script again. Can also be an
array of servers.
.OUTPUTS
Excel spreadsheet
.EXAMPLE
.\Get-PrinterInfo.ps1 -PrintServers "MyPrintServer"
.EXAMPLE
.\Get-PrinterInfo.ps1 -PrintServers (Get-Content c:\scripts\myprintserverlist.txt)
.INPUTS
String
.OUTPUTS
Excel
#>
[CmdletBinding()]
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'Name of print Server'
)]
[String]$PrintServers,
[System.Management.Automation.Credential()]
[PSCredential]$Credential = $Credential
)
# Create new Excel workbook
Write-Verbose -Message "$(Get-Date): Script begins!"
Write-Verbose -Message "$(Get-Date): Opening Excel..."
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $True
$Excel = $Excel.Workbooks.Add()
$Sheet = $Excel.Worksheets.Item(1)
$Sheet.Name = 'Printer Inventory'
#======================================================
$Sheet.Cells.Item(1,1) = 'Print Server'
$Sheet.Cells.Item(1,2) = 'Printer Name'
$Sheet.Cells.Item(1,3) = 'Location'
$Sheet.Cells.Item(1,4) = 'Comment'
$Sheet.Cells.Item(1,5) = 'IP Address'
$Sheet.Cells.Item(1,6) = 'Driver Name'
$Sheet.Cells.Item(1,7) = 'Driver Version'
$Sheet.Cells.Item(1,8) = 'Driver'
$Sheet.Cells.Item(1,9) = 'Shared'
$Sheet.Cells.Item(1,10) = 'Share Name'
#=======================================================
$intRow = 2
$WorkBook = $Sheet.UsedRange
$WorkBook.Interior.ColorIndex = 40
$WorkBook.Font.ColorIndex = 11
$WorkBook.Font.Bold = $True
#=======================================================
# Get printer information
ForEach ($PrintServer in $PrintServers) {
Write-Verbose -Message "$(Get-Date): Working on $PrintServer..."
$Printers = Get-WmiObject -Class Win32_Printer -ComputerName $PrintServer -Credential $credential
ForEach ($Printer in $Printers) {
$session = New-PSSession -ComputerName $PrintServer -Credential $credential
If ($Printer.Name -notlike 'Microsoft XPS*') {
$Sheet.Cells.Item($intRow, 1) = $PrintServer
$Sheet.Cells.Item($intRow, 2) = $Printer.Name
$Sheet.Cells.Item($intRow, 3) = $Printer.Location
$Sheet.Cells.Item($intRow, 4) = $Printer.Comment
If ($Printer.PortName -notlike '*\*') {
$Ports = Get-WmiObject -Class Win32_TcpIpPrinterPort -Filter "name = '$($Printer.Portname)'" -ComputerName $Printserver -Credential $credential
ForEach ($Port in $Ports) {
$Sheet.Cells.Item($intRow, 5) = $Port.HostAddress
}
}
####################
$Drivers = Get-WmiObject -Class Win32_PrinterDriver -Filter "__path like '%$($Printer.DriverName)%'" -ComputerName $Printserver -Credential $credential
ForEach ($Driver in $Drivers) {
$Drive = $Driver.DriverPath.Substring(0,1)
$Sheet.Cells.Item($intRow,7) = (Get-ItemProperty -Path ($Driver.DriverPath.Replace("$Drive`:","\\$PrintServer\$Drive`$"))).VersionInfo.ProductVersion
$Sheet.Cells.Item($intRow,8) = Split-Path -Path $Driver.DriverPath -Leaf
}
####################
$Sheet.Cells.Item($intRow, 6) = $Printer.DriverName
$Sheet.Cells.Item($intRow, 9) = $Printer.Shared
$Sheet.Cells.Item($intRow, 10) = $Printer.ShareName
$intRow ++
}
Remove-PSSession -Id $session
}
$WorkBook.EntireColumn.AutoFit() | Out-Null
}
$intRow ++
$Sheet.Cells.Item($intRow,1) = 'Printer inventory completed'
$Sheet.Cells.Item($intRow,1).Font.Bold = $True
$Sheet.Cells.Item($intRow,1).Interior.ColorIndex = 40
$Sheet.Cells.Item($intRow,2).Interior.ColorIndex = 40
Write-Verbose -Message "$(Get-Date): Completed!"
}
Function Get-remComputerInfo {
<#
.SYNOPSIS
Get basic info from a computer.
.DESCRIPTION
Get Model, Serial Number, and BIOS Version from a computer.
.PARAMETER <Computer>
Define the target computer
.EXAMPLE
Get-ComputerInfo testmachine01
.INPUTS
String
.OUTPUTS
Array
#>
Param (
[Parameter(
Mandatory = $True,
HelpMessage = 'Computer'
)]
[String]$c
)
$CInfoResult = @()
WH -t 'Checking: ' -n
WH -t $c -f Cy
#$ping = Get-WmiObject -Class win32_pingstatus -Filter "address='$c'"
$ping = Test-Connection -ComputerName $c -Count 1 -Quiet
If ($ping -eq $true) {
$ping | ForEach-Object {
$CInfoResult += [PSCustomObject][Ordered]@{
Name = $c
Status = 'Online'
Model = (Get-WmiObject -Class Win32_ComputerSystem -ComputerName $c).model
Serial = (Get-WmiObject -Class Win32_Bios -ComputerName $c).SerialNumber
Version = (Get-WmiObject -Class Win32_Bios -ComputerName $c).Version
}
}
} Else {
$CInfoResult += [PSCustomobject][Ordered]@{
Name = $c
Status = 'Offline'
Model = 'NA'
Serial = 'NA'
Version = 'NA'
}
}
Return $CInfoResult
}
Function Search-ADComputer {
<#
.SYNOPSIS
Find any/all computers that match.
.DESCRIPTION
Finds any/all computers that even resemble your search criteria
.PARAMETER
Define the target to look for
.EXAMPLE
Search-ADComputer comply
.INPUTS
String
.OUTPUTS
Array
#>
[CmdletBinding()]
[Alias('fputer')]
Param (
[Parameter(Mandatory=$true,HelpMessage='Search String')]
[ValidateNotNullorEmpty()]
[String]$SearchString,
[Switch]$full
)
$adParam = @{
Filter = "samaccountname -like '*$($SearchString)*' -or name -like '*$($SearchString)*'"
Properties = 'IPv4Address',
'DNSHostName',
'Enabled',
'Name',
'ObjectClass',
'Description',
'CanonicalName',
'OperatingSystem',
'LastLogonDate',
'PasswordLastSet',
'whenCreated'
Server = $script:PDC
}
$mSel = @{
Property = 'DistinguishedName',
'SamAccountName',
'Name',
'IPv4Address',
'DNSHostName',
'Enabled',
'ObjectClass',
'ObjectGUID',
'SID',
'Description',
'OperatingSystem',
'LastLogonDate',
'PasswordLastSet',
'whenCreated',
@{
n = 'Container'
e = {Get-Container -can $_.CanonicalName}
}
}
$Match = Get-ADComputer @adParam | Select-Object @mSel
If ($full) {
$Match = $Match
} Else {
$maSel = @{
Property = 'DistinguishedName',
'SamAccountName',
'Name',
'IPv4Address',
'DNSHostName',
'Enabled',
'Description',
'OperatingSystem',
'LastLogonDate',
'PasswordLastSet',
'whenCreated',
'Container'
}
$Match = $Match | Select-Object @maSel
}
If ($null -eq $Match) {
WH -t 'No matching accounts were found.'
} Else {
$Match
}
}
Function Get-Serials {
<#
.SYNOPSIS
Gets serial numbers of PCs and their attached monitors
.DESCRIPTION
creates a list of PCs to query based on -EXAMPLE and retrieves PC serials
and the serials of monitors that are attached, as long as the PC is reachable.
.PARAMETER similar
add -similar to the end of your command to find all computers that resemble the pc name
.EXAMPLE
Get-Serials testpc01
Usage: to look up information on a single machine
Gets Name/Manufacturer/Model/SerialNumber/Status and all Monitor details (size/manufacturer/serial)
.EXAMPLE
Get-Serials testpc -similar
Usage: to look up information on all computers that match testpc's name
Gets Name/Manufacturer/Model/SerialNumber/Status and all Monitor details (size/manufacturer/serial)
.INPUTS
String
.OUTPUTS
Array
#>
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'Computer name required'
)]
[String]$example,
[Switch]$similar
)
Function Format-TableColored {
Param(
[Parameter(
Mandatory,
HelpMessage = 'Object to color required',
ValueFromPipeline,
Position = 0
)]
[PSObject]$InputObject,
[Switch]$AutoSize,
[Switch]$HideTableHeaders,
[String]$ColorProperty = '__Color'
)
Begin {
$Output = @()
$Colors = @()
$FirstValue = ''
$StartColor = $False
$ColorArgs = @{}
}
Process {
If (!$FirstValue){
$FirstValue = [regex]::Escape(($InputObject.PsObject.Properties | Select-Object -ExpandProperty Value -First 1))
}
$Colors += If ($InputObject.$ColorProperty -is [hashtable]) {
$InputObject.$ColorProperty
} Else {
@{}
}
$Output += $InputObject | Select-Object -Property * -ExcludeProperty $ColorProperty
}
End {
$null = $PSBoundParameters.Remove('ColorProperty')
$PSBoundParameters['InputObject'] = $Output
$i = 0
(Format-Table @PSBoundParameters | Out-String) -split "`r`n" |
ForEach-Object {
If (!$StartColor -and ($_ -match "\s*$($FirstValue)")){$StartColor = $True}
If ($StartColor -and $_){$ColorArgs = $Colors[$i++]} Else {$ColorArgs.Clear()}
Write-Host -Object $_ @ColorArgs
}
}
}
$Computers = @()
Switch ($similar) {
$true {
Search-ADComputer -SearchString $example | Foreach-Object {$Computers += $_.DNSHostName}
}
$false {
Search-ADComputer -SearchString $example -single | Foreach-Object {$Computers += $_.DNSHostName}
}
default {
Search-ADComputer -SearchString $example -single | Foreach-Object {$Computers += $_.DNSHostName}
}
}
If ($Computers) {
$computerlist = @()
# Loop through the computers
$computers | ForEach-Object {
If (($Computers | Select-Object -First 1) -eq 'NA') {
WH -t 'No matching Computers found.'
Break
}
$tempObj = New-Object -TypeName System.Management.Automation.PSObject
$tempObj | Add-Member -MemberType NoteProperty -Name 'ComputerName' -Value $_.split('.')[0].trim()
$tempCompName = $_
If (Test-Connection -ComputerName $_ -Quiet -Count 1) {
$computerHold = Get-WmiObject -ComputerName $_ -Class Win32_ComputerSystem -ErrorAction SilentlyContinue
$computerMan = $computerHold.Manufacturer
$computerMod = $computerHold.Model
### PC Serial Number
$computerSerial = (Get-WmiObject -ComputerName $_ -Class Win32_Bios -ErrorAction SilentlyContinue).SerialNumber
If ($computerSerial -match 'VMware') {$computerSerial = 'VMware'}
If ($computerSerial -notmatch 'VMware') {
### Get Monitor WMI Objects
$monitorWmi = Get-WmiObject -ComputerName $_ -Class WMIMonitorID -Namespace 'root\wmi' -ErrorAction SilentlyContinue
$monitorSerials = @()
$monitorWmi | ForEach-Object {
$Man = ($_.ManufacturerName -notmatch 0 | ForEach-Object {[char]$_}) -join ''
$Nam = Try {
($_.UserFriendlyName -notmatch 0 | ForEach-Object {[char]$_}) -join ''
} Catch {
($_.UserFriendlyName -notMatch 0 | ForEach-Object {$_}) -join ''
}
$Ser = ($_.SerialNumberID -notmatch 0 | ForEach-Object {[char]$_}) -join ''
$MonInst = $_.InstanceName.split('\')[-1]
### $MonInfHold will fetch and store computername, monitor instance id, horizontal size, vertical size, do the match for overall size, and aspect ratio
$miParam = @{
Property = @{
n = 'Computer'
e = {$_.__SERVER}
},
'InstanceName',
@{
n = 'Horizontal'
e = {[Math]::Round(($_.MaxHorizontalImageSize/2.54), 2)}
},
@{
n = 'Vertical'
e = {[Math]::Round(($_.MaxVerticalImageSize/2.54), 2)}
},
@{
n = 'Size'
e = {[Math]::Round(([Math]::Sqrt([Math]::Pow($_.MaxHorizontalImageSize, 2) + [Math]::Pow($_.MaxVerticalImageSize, 2))/2.54),2)}
},
@{
n = 'Ratio'
e = {[Math]::Round(($_.MaxHorizontalImageSize)/($_.MaxVerticalImageSize),2)}
}
}
$MonInfHold = Get-WmiObject -ComputerName $tempCompName -Namespace root\wmi -Class WmiMonitorBasicDisplayParams | Select-Object @miParam
$MonSize = $MonInfHold | Where-Object {$_.InstanceName.split('\')[-1] -match $MonInst} | Select-Object -ExpandProperty Size
$monitorSerials += "$Man,$Nam,$Ser,$MonSize inch"
$monitorSerialOutput = '{' + $($monitorSerials -join ' / ') + '}' # Convert $monitor serials to string
}
} Else {
$monitorSerialOutput = '{NA}'
}
# Add the details to our $tempObj
$tempObj | Add-Member -MemberType NoteProperty -Name 'CompManufacturer' -Value $computerMan
$tempObj | Add-Member -MemberType NoteProperty -Name 'ComputerModel' -Value $computerMod
$tempObj | Add-Member -MemberType NoteProperty -Name 'ComputerSerialNumber' -Value $computerSerial
$tempObj | Add-Member -MemberType NoteProperty -Name 'MonitorSerialNumbers' -Value $monitorSerialOutput
$tempObj | Add-Member -MemberType NoteProperty -Name 'Status' -Value 'ONLINE'
} Else {
# If the computer is off, set the status property to offline so we can easily sort and filter them
$tempObj | Add-Member -MemberType NoteProperty -Name 'Status' -Value 'UNREACHABLE'
}
$computerList += $tempObj # Add the $temoObj to the $computerList array
}
# Output the $computerList
#$computerList
If ($similar){
$cParam = @{
Property = 'ComputerName',
'CompManufacturer',
'ComputerModel',
'ComputerSerialNumber',
'MonitorSerialNumbers',
'Status',
@{
n = '__Color'
e = {
If ($_.Status -eq 'UNREACHABLE') {
@{Fore = 'Red'}
}
}
}
}
$computerList | Select-Object @cParam | Sort-Object -Property Status, ComputerName | Format-TableColored
} Else {
$cParam = @{
Property = 'ComputerName',
'CompManufacturer',
'ComputerModel',
'ComputerSerialNumber',
'MonitorSerialNumbers',
'Status'
}
$computerlist = $computerList | Select-Object @cParam | Sort-Object -Property Status,ComputerName
Return $computerlist
}
} Else {
WH -t 'No matching Computers found.'
}
}
Function Get-StalePCs {
<#
.SYNOPSIS
Get a list of stale PCs 200 days old and deletes or disables those chosen
.DESCRIPTION
Get a list of PCs that haven't logged on to domain in 200 days and either deletes or disables what you choose
.PARAMETER Disable
Disable alters Description, sets to disabled and moves to Disabled Computer OU
.PARAMETER Delete
Delete is self-explanatory
.EXAMPLE
Get-StalePCs -Delete
Show a list of machines that haven't checked in in 200 days, then gives a grid to select from (multiple).
Those chosen are deleted.
.INPUTS
String
.OUTPUTS
Out-GridView
#>
[CmdletBinding()]
Param (
[Switch]$Disable,
[Switch]$Delete
)
#$ErrorActionPreference = 'SilentlyContinue'
If ($Disable.IsPresent -or $Delete.IsPresent) {
$Act = $true
} Else {
$Act = $false
}
If ($Disable.IsPresent -and $Delete.IsPresent) {
Write-Information -MessageData 'Choose one - Disable or Delete' -Tags Choice
return
}
### Get Stale PCs to choose from
Write-Information -MessageData 'Getting Computers...' -Tags Progress
$term = '200'
$StalePCs = Show-DeadPCs -term $term | Where-Object {$null -eq $_.IPv4Address} | Sort-Object -Property Container,LastLogonAge
If ($Act -eq $true) {
### Choose machines to disable/delete
$mSel = @{Property = 'Name', 'OS', 'PWDLastSetAge', 'LastLogonDate', 'LastLogonDateDays', 'LastLogonAge', 'Container', 'Description'}
$Marked = $StalePCs | Select-Object @mSel | Out-GridView -Title "Computers that haven't logged on in $($term) days" -Passthru
If ($Marked) {
ForEach ($Mark in $Marked) {
If ($Disable.IsPresent) {
Write-Information -MessageData 'Beginning Disabling' -Tags Action,Disable
### Disabling
$PreDesc = '[Scripted - Disabled for inactivity]' ### to Disable
$MarkedTarget = $Desc = $null
$hadDesc = $false
$MarkedTarget = Get-ADComputer -Identity $Mark.Name
$TargetDesc = Get-ADComputer -Identity $Mark.Name -Properties Description
If ($TargetDesc.Description){
If (($null -ne $TargetDesc.Description) -or ($TargetDesc.Description -like '*')) {
$hadDesc = $true
$Desc = $("$($PreDesc) Disabled on $(Get-Date -Format yyyy-MMM-dd) $(($TargetDesc.Description).trim()) by $($env:USERNAME)").trim()
} Else {
$hadDesc = $false
$Desc = $("[Scripted - Disabled for inactivity] Disabled on $(Get-Date -Format yyyy-MMM-dd) by $($env:USERNAME)").trim()
}
} Else {
$hadDesc = $false
$Desc = $("[Scripted - Disabled for inactivity] Disabled on $(Get-Date -Format yyyy-MMM-dd) by $($env:USERNAME)").trim()
#Set-ADComputer -Identity $MarkedTarget -Add @{"description"="$($Desc)"}
}
### Change Description
If ($hadDesc -eq $true) {
#Set-ADComputer -Identity $MarkedTarget -replace @{"description"="$($Desc)"}
Write-Information -MessageData "Set-ADComputer -Identity $($MarkedTarget.Name) -replace @{`"description`"=`"$($Desc)`"}" -Tags Action,Disable,Description
} Else {
#Set-ADComputer -Identity $MarkedTarget -Add @{"description"="$($Desc)"}
Write-Information -MessageData "Set-ADComputer -Identity $($MarkedTarget.Name) -Add @{`"description`"=`"$($Desc)`"}" -Tags Action,Disable,Description
}
### Disable
#Set-ADComputer -Identity $MarkedTarget -Enabled $false
Write-Information -MessageData "Set-ADComputer -Identity $($MarkedTarget.Name) -Enabled `$false" -Tags Action,Disable
### Move to Terminated OU
#Move-ADObject -Identity $MarkedTarget -targetpath "OU=Terminated,OU=Computers,OU=SI,DC=shermco,DC=ind"
Write-Information -MessageData "Move-ADObject -Identity $($MarkedTarget.Name) -targetpath `"OU=Terminated,OU=Computers,OU=SI,DC=shermco,DC=ind`"" -Tags Action,Disable
### Verify Object
#Get-ADComputer -Identity $Mark.DistinguishedName -Properties Description,Enabled
}
If ($Delete.IsPresent) {
Write-Information -MessageData 'Beginning Deleting' -Tags Action,Delete
### Deleting
Get-ADComputer -Identity $Mark.DistinguishedName | Remove-ADObject -Recursive -Confirm:$false
#WH -t "Get-ADComputer $($Mark.Name) | Remove-ADObject -Recursive -Confirm:`$false"
Write-Information -MessageData "$($Mark.Name) - Deleted" -Tags Action,Delete
}
}
} Else {
Write-Information -MessageData 'Nothing Selected' -Tags NoAction
}
} Else {
$StalePCs | Out-GridView -Title "Computers that haven't logged on in $($term) days"
}
}
Function Get-SystemInfo {
<#
.SYNOPSIS
Get raw sysinfo from one or more machines.
.DESCRIPTION
Gets raw sysinfo directly off of a single machine or group of machines.
.PARAMETER Scope
Add -Scope to specify a scope of machines as outlined below.
-Scope ServerOnly: Run sysinfo on all servers.
-Scope DCOnly: Run sysinfo on all DCs.
-Scope LocalHost: Run sysinfo on local computer.
-Scope ClientOnly: Run sysinfo on all machines that are not a server.
-Scope AllComputer: Sysinfo on every machine
.PARAMETER ComputerName
Use -ComputerName to specify a single computer.
.EXAMPLE
Get-SystemInfo serveronly | ogv
Gets sysinfo for all servers and output to gridview
.NOTES
Suggested to specify a field you're looking for
or use ' | ft -auto' or ' | ogv' to make the results more readable.
.OUTPUTS
Fields returned:
'Host Name','OS','Version','Manufacturer','Configuration','Build Type',
'Registered Owner','Registered Organization','Product ID','Install Date',
'Boot Time','System Manufacturer','Model','Type','Processor','Bios',
'Windows Directory','System Directory','Boot Device','Language','Keyboard',
'Time Zone','Total Physical Memory','Available Physical Memory','Virtual Memory',
'Virtual Memory Available','Virtual Memory in Use','Page File','Domain',
'Logon Server','Hotfix','Network Card','Hyper-V'
.INPUTS
String
.OUTPUTS
String
#>
[CmdletBinding()]
Param (
[ValidateSet('LocalHost', 'ServerOnly', 'DCOnly', 'ClientOnly', 'AllComputer')]
[String]$Scope,
[String]$ComputerName
)
#$sysinforeturn = @()
$E = @{ErrorAction = 'SilentlyContinue'}
$header = 'Host Name',
'OS',
'Version',
'Manufacturer',
'Configuration',
'Build Type',
'Registered Owner',
'Registered Organization',
'Product ID',
'Install Date',
'Boot Time',
'System Manufacturer',
'Model',
'Type',
'Processor',
'Bios',
'Windows Directory',
'System Directory',
'Boot Device',
'Language',
'Keyboard',
'Time Zone',
'Total Physical Memory',
'Available Physical Memory',
'Virtual Memory',
'Virtual Memory Available',
'Virtual Memory in Use',
'Page File',
'Domain',
'Logon Server',
'Hotfix',
'Network Card',
'Hyper-V'
Switch ($Scope) {
'ServerOnly' {
Invoke-Command -ComputerName (Get-ADComputer -Filter {OperatingSystem -like '*server*'}).Name -ScriptBlock {
& "$env:windir\system32\systeminfo.exe" /FO CSV | Select-Object -Skip 1
} @E | ConvertFrom-Csv -Header $header
}
'DCOnly' {
Invoke-Command -ComputerName (Get-ADDomainController -Filter *).Name -ScriptBlock {
& "$env:windir\system32\systeminfo.exe" /FO CSV | Select-Object -Skip 1
} @E | ConvertFrom-Csv -Header $header
}
'LocalHost' {
& "$env:windir\system32\systeminfo.exe" /FO CSV | Select-Object -Skip 1 | ConvertFrom-Csv -Header $header
}
'ClientOnly' {
Invoke-Command -ComputerName (Get-ADComputer -Filter {OperatingSystem -notlike '*server*'}).Name -ScriptBlock {
& "$env:windir\system32\systeminfo.exe" /FO CSV | Select-Object -Skip 1
} @E | ConvertFrom-Csv -Header $header
}
'AllComputer' {
Invoke-Command -ComputerName (Get-ADComputer -Filter *).Name -ScriptBlock {
& "$env:windir\system32\systeminfo.exe" /FO CSV | Select-Object -Skip 1
} @E | ConvertFrom-Csv -Header $header
}
}
If ($ComputerName) {
If (Test-Connection -ComputerName $ComputerName -Count 1 -Quiet){
Invoke-Command -ComputerName $ComputerName -ScriptBlock {
& "$env:windir\system32\systeminfo.exe" /FO CSV | Select-Object -Skip 1
} @E | ConvertFrom-Csv -Header $header
} Else {
Write-Error -Message "$($ComputerName) unreachable"
}
}
}
Function Get-TrustStatus {
<#
.SYNOPSIS
Directly queries a computer to test if its trusted on domain
.DESCRIPTION
Inquires with the domain about the trust status of a given computer
.PARAMETER Computer
-Computer: Specify which computer you're targeting.
.EXAMPLE
Get-TrustStatus -Computer samplecomputername
.INPUTS
String
.OUTPUTS
String
#>
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'Computer Name'
)]
[String]$Computer
)
$TrustResult = New-Object -TypeName System.Collections.Arraylist
$Computer | Foreach-Object {
If (-not (Test-Connection -ComputerName $_ -Quiet -Count 1)) {
$trustStatus = 'Offline'
} Else {
$trustStatus = Invoke-Command -ComputerName $_ -ScriptBlock {Test-ComputerSecureChannel -Verbose}
}
$null = $TrustResult.add((New-Object -TypeName PSObject -Property @{
ComputerName = $_
Status = If ($trustStatus -eq $true) {'Trusted'} Else {'False'}
}))
}
$TrustResult
}
Function Show-DeadPCs {
<#
.SYNOPSIS
Get a list of stale PCs based on number of days
.DESCRIPTION
Get a list of PCs that haven't logged on to domain in a specified number of days
.EXAMPLE
Show-DeadPCs 365
Show a list of machines that haven't checked in in 365 days
.INPUTS
Int
.OUTPUTS
Array
.NOTES
Author : James Stephens
Created On : 12/16/2022
Organization : Shermco
Filename : Show-DeadPCs.ps1
Version : 1.0 - Initial release.
1.1 - Refining.
.FUNCTIONALITY
Get-ADComputer
#>
[Alias('stalepcs')]
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'Amount of days to use for what dead means'
)]
[Alias('term')]
[String]${Enter how many days old}
)
If (-not ($term)) {$term = ${Enter how many days old}}
#Requires -Module ActiveDirectory
$ErrorActionPreference = 'SilentlyContinue'
$Days = (Get-Date).addDays(-$term)
$StalePCParams = @{
Filter = '(LastLogonDate -NotLike "*") -OR (LastLogondate -ge $Days) -OR (PasswordLastSet -le $Days)'
Properties = 'PasswordLastSet',
'LastLogonDate',
'LastLogonTimeStamp',
'CanonicalName',
'OperatingSystem',
'Description',
'IPv4Address',
'Enabled'
Server = $script:PDC
}
$oParam = @{
Property = 'Name',
'Enabled',
@{n='OS';e={$_.OperatingSystem}},
@{n='PWDLastSetAge';e={((Get-Date) - $_.PasswordLastSet).Days}},
'LastLogonDate',
@{n='LastLogonDateDays';e={((Get-Date) - $_.LastLogonDate).days}},
@{n='LastLogonAge';e={((Get-Date) - [DateTime]::FromFileTime($_.LastLogonTimeStamp)).Days}},
@{n='Container';e={$_.CanonicalName -ireplace '\/[^\/]+$',''}},
'Description',
'IPv4Address',
'DistinguishedName'
}
$output = Get-ADComputer @StalePCParams |
Select-Object @oParam |
Where-Object {$_.LastLogonAge -gt $term} |
Where-Object {$_.Name -notmatch 'NTNX'} |
Sort-Object -Property Container, LastLogonAge
Return $output
}
Function Show-SysInfos {
<#
.SYNOPSIS
Leverages cmdlet Get-SystemInfo to return sysinfo
.DESCRIPTION
Shows in depth system info for computer you specify.
.Example
Show-SysInfos testcomputer
-------------------------------------------
Buildtype : Multiprocessor Free
OSName : Microsoft Windows 10 Enterprise LTSC
NetworkAdapter : 2 NIC(s) Installed.
[01]: Intel(R) Dual Band Wireless-AC 8260
Connection Name: Wi-Fi
Status: Media disconnected
[02]: Realtek PCIe GBE Family Controller
Connection Name: Ethernet
DHCP Enabled: Yes
DHCP Server: 192.168.1.12
IP address(es)
[01]: 192.168.1.254
[02]: fe80::6ce8:a18c:c64d:68b
RegisteredOwner : admini
UsedVirtualMemory : 1,722 MB
RegisteredOrganization :
PagingFile : C:\pagefile.sys
StartTime : 1/27/2020, 7:59:50 AM
AvailableVirtualMemory : 7,652 MB
LogonServer : N/A
Language : en-us;English (United States)
OSManufacturer : Microsoft Corporation
OSVersion : 10.0.17763 N/A Build 17763
Hotfix : 7 Hotfix(s) Installed.
[01]: KB4533013
[02]: KB4465065
[03]: KB4470788
[04]: KB4487038
[05]: KB4516115
[06]: KB4523204
[07]: KB4530715
Model : OptiPlex 3240 AIO
Type : x64-based PC
Manufacturer : Dell Inc.
Hostname : testcomputer
BIOSVersion : Dell Inc. 1.5.10, 11/14/2016
OSConfig : Member Workstation
Domain : shermco.ind
ProductID : 00424-90090-18465-AA859
PhysicalMemory : 8,094 MB
Processor : 1 Processor(s) Installed.,[01]: Intel64 Family 6 Model 94 Stepping 3 GenuineIntel ~3192 Mhz
AvailablePhysicalMemory : 6,153 MB
Keyboard : en-us;English (United States)
SystemFolder : C:\Windows\system32
WindowsFolder : C:\Windows
MaxVirtualMemory : 9,374 MB
InstallDate : 12/16/2019, 10:25:53 AM
TimeZone : (UTC-06:00) Central Time (US & Canada)
StartDevice : \Device\HarddiskVolume2
HyperV : VM Monitor Mode Extensions: Yes
Virtualization Enabled In Firmware: Yes
Second Level Address Translation: Yes
Data Execution Prevention Available: Yes
.INPUTS
String
.OUTPUTS
Array
.OUTPUTS
Hostname,OSName,OSVersion,OSManufacturer,OSConfig,Buildtype,RegisteredOwner
RegisteredOrganization,ProductID,InstallDate,StartTime,Manufacturer,Model
Type,Processor,BIOSVersion,WindowsFolder,SystemFolder,StartDevice,Language
Keyboard,TimeZone,PhysicalMemory,AvailablePhysicalMemory,MaxVirtualMemory
AvailableVirtualMemory,UsedVirtualMemory,PagingFile,Domain,LogonServer,Hotfix
NetworkAdapter,HyperV
#>
Param (
[Parameter(Mandatory,HelpMessage='ComputerName')]
[String]$ComputerName
)
$sysinfoarray = @()
$sysinfo = $null
$Selection = @{
Property = 'Host name',
'OS',
'Version',
'Manufacturer',
'Configuration',
'Build Type',
'Registered Owner',
'Registered Organization',
'Product ID',
'Install Date',
'Boot Time',
'System Manufacturer',
'Model',
'Type',
'Processor',
'BIOS',
'Windows Directory',
'System Directory',
'Boot Device',
'Language',
'Keyboard',
'Time Zone',
'Total Physical Memory',
'Available Physical Memory',
'Virtual Memory',
'Virtual Memory Available',
'Virtual Memory in Use',
'Page File',
'Domain',
'Logon Server',
@{n="'Hotfix'";e={$_.Hotfix.replace(',',"`n")}},
@{n="'NetworkCard'";e={$_.'Network Card'.replace(',',"`n")}},
@{n="'HyperV'";e={$_.'Hyper-V'.replace(',',"`n")}}
}
If ($ComputerName){
$sysinfo = Get-SystemInfo -Computername $ComputerName | Select-Object @Selection
} Else {
$sysinfo = Get-SystemInfo -Scope localhost | Select-Object @Selection
}
ForEach ($line in $sysinfo){
$sysinfoarray += [pscustomobject][ordered]@{
Hostname = $line.'Host name'
OSName = $line.'OS'
OSVersion = $line.'Version'
OSManufacturer = $line.'Manufacturer'
OSConfig = $line.'Configuration'
Buildtype = $line.'Build Type'
RegisteredOwner = $line.'Registered Owner'
RegisteredOrganization = $line.'Registered Organization'
ProductID = $line.'Product ID'
InstallDate = $line.'Install Date'
StartTime = $line.'Boot Time'
Manufacturer = $line.'System Manufacturer'
Model = $line.'Model'
Type = $line.'Type'
Processor = $line.'Processor'
BIOSVersion = $line.'BIOS'
WindowsFolder = $line.'Windows Directory'
SystemFolder = $line.'System Directory'
StartDevice = $line.'Boot Device'
Language = $line.'Language'
Keyboard = $line.'Keyboard'
TimeZone = $line.'Time Zone'
PhysicalMemory = $line.'Total Physical Memory'
AvailablePhysicalMemory = $line.'Available Physical Memory'
MaxVirtualMemory = $line.'Virtual Memory'
AvailableVirtualMemory = $line.'Virtual Memory Available'
UsedVirtualMemory = $line.'Virtual Memory in Use'
PagingFile = $line.'Page File'
Domain = $line.'Domain'
LogonServer = $line.'Logon Server'
Hotfix = $line.'Hotfix'
NetworkAdapter = $line.'NetworkCard'
HyperV = $line.'HyperV'
}
}
Return $sysinfoarray
}
#endregion Computer
#region User
Function Get-Mgr {
<#
.SYNOPSIS
Gets manager from AD if one is listed
.DESCRIPTION
Gets user manager from AD if one is listed.
.PARAMETER user
AD samaccountname
.EXAMPLE
get-mgr username
.INPUTS
String
.OUTPUTS
Array
#>
[Alias('gmgr')]
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'AD Username'
)]
[String]$user
)
$sel = @{Property = @{n='MgrName';e={(Get-ADUser -Identity $_.Manager -Server $script:PDC).Name}}}
If ($user.SamAccountName){
$Props = @{
Identity = $user.SamAccountName
Properties = 'Manager'
Server = $script:PDC
}
} Else {
$Props = @{
Identity = $user
Properties = 'Manager'
Server = $script:PDC
}
}
$mgr = (Get-ADUser @Props | Select-Object @sel).MgrName
Return $mgr
}
Function Get-Phones {
<#
.SYNOPSIS
Gets Users all user phone numbers possible from AD
.DESCRIPTION
Makes a best effort to get and parse all user phone numbers into a readable format
.PARAMETER find
you can specify a name you're looking for
.PARAMETER grid
you can send results to gridview
.EXAMPLE
Get-Phones -grid
Returns all user phone numbers into gridview
.EXAMPLE
Get-Phones <samaccountname>
Returns all phone numbers for <samaccountname>
.EXAMPLE
Get-Phones -grid
Returns all phone numbers in gridview
.INPUTS
String
.OUTPUTS
Array
#>
[CmdletBinding()]
Param (
[String[]]$find,
[Switch]$grid
)
$ErrorActionPreference = 'SilentlyContinue'
#region Poll AD for all phone number fields
$ADPhoneParams = @{
Properties = 'Fax',
'IpPhone',
'mobile',
'OfficePhone',
'otherTelephone'
}
$ADSelParm = @{
Property = 'Name',
'Fax',
'IpPhone',
'mobile',
'OfficePhone',
@{n='otherTelephone';e={($_.OtherTelephone -join ', ').TrimEnd(', ')}}
}
If ($find){
$users = @()
ForEach ($u in $find) {
$ADPhoneParams.Identity = $u
$users += Get-ADUser @ADPhoneParams | Select-Object @ADSelParm
}
} Else {
$ADPhoneParams.Filter = '(enabled -eq $true) -and (Fax -like "*" -or IpPhone -like "*" -or mobile -like "*" -or OfficePhone -like "*" -or OtherTelephone -like "*")'
$users = Get-ADUser @ADPhoneParams | Select-Object @ADSelParm
}
#endregion Poll AD
#region Build Array
$phones = @()
$users | ForEach-Object {
### Fax
If ($null -ne $_.Fax) {$Fax = Convert-StandardPhone -NumtoConv $_.Fax} Else {$Fax = $null}
### IPPhone
If ($null -ne $_.IpPhone) {$IpPhone = Convert-StandardPhone -NumtoConv $_.IpPhone} Else {$IpPhone = $null}
### Mobile
If ($null -ne $_.Mobile) {$Mobile = Convert-StandardPhone -NumtoConv $_.mobile} Else {$Mobile = $null}
### OfficePhone
If ($_.OfficePhone -match '^*[0-9]') { ### If the contents of OfficePhone attrib is a number
$OfficePhone = @()
$oTemp = $_.OfficePhone
### If the current object contains 'ext' -- replace 'ext' with a comma
If ($oTemp -match 'ext') {$oTemp = $oTemp.replace('ext',',')}
If ($oTemp -notmatch ',') {
### Deal with non-commas
### remove all characters that aren't numbers
$item = $oTemp -replace '\D'
$itemNum = ($item | Measure-Object -Character).Characters
### If the current object is 1-6 characters long, add 'x' just before it to indicate extension -- else -- If the current object is greater than 6 characters, format to phonenumber
If (($itemNum -ge 1) -and ($itemNum -le 6)){$OfficePhone = "x$($item)"} Else {$OfficePhone = Convert-StandardPhone -NumtoConv $item}
} Else {
### Deal with commas
$OfficePhoneTemp = @()
$oTemp.split(',') | ForEach-Object { ### split the current object on commas -- then deal with each item
If ($_ -like '*') { ### if the item is not null
$item = $_ -replace '\D' ### select only numbers
$itemNum = ($item | Measure-Object -Character).Characters
If (($itemNum -ge 1) -and ($itemNum -le 6)) {$OfficePhoneTemp += "x$($item)"} Else {$OfficePhoneTemp += Convert-StandardPhone -NumtoConv $item}
}
}
$OfficePhone = ($OfficePhoneTemp | Select-Object -Unique) -join ' ' ### Select unique items -> Convert to string -> Store in $OfficePhone
}
} Else { ### If the contents of OfficePhone attrib isn't a number or is empty, set $OfficePhone value to $null
$OfficePhone = $null
}
### otherTelephone
If ($_.OtherTelephone -match '^*[0-9]') {
$OtherPhone = @()
$OtTemp = $_.OtherTelephone
If ($OtTemp -notmatch ','){
$item = $OtTemp -replace '\D'
$itemNum = ($item | Measure-Object -Character).Characters
If (($itemNum -ge 1) -and ($itemNum -le 6)) {$OtherPhone = "x$($item)"} Else {$OtherPhone = Convert-StandardPhone -NumtoConv $OtTemp}
} Else {
$OtherPhoneTemp = @()
$OtTemp.split(',') | ForEach-Object {
If ($_ -like '*'){
$item = $_ -replace '\D'
$itemNum = ($item | Measure-Object -Character).Characters
If (($itemNum -ge 1) -and ($itemNum -le 6)) {$OtherPhoneTemp += "x$($item)"} Else {$OtherPhoneTemp += Convert-StandardPhone -NumtoConv $item}
}
}
$OtherPhone = ($OtherPhoneTemp | Select-Object -Unique) -join ' '
}
} Else {
$OtherPhone = $null
}
$phones += [PSCustomObject][Ordered]@{
Name = $_.Name
#HomePhone = $HomePhone
Fax = $Fax
IpPhone = $IpPhone
Mobile = $Mobile
OfficePhone = $OfficePhone
OtherPhone = $OtherPhone
Ext = If ($OfficePhone -match 'x'){$OfficePhone.split('x')[-1]} ElseIf (($IpPhone) -and (($IpPhone | Measure-Object -Character).Characters -le 6)){$IpPhone}
}
}
#endregion Build Array
### If a name was specified to search for
### Try to match the name in the phone array --> $match
If ($grid.IsPresent){ ### If grid was specified
$phones | Out-GridView ### Output results to grid
} Else { ### No grid specified
Return $phones ### return full phone array results
}
}
Function Get-Exts {
<#
.SYNOPSIS
Gets Users Phone extensions
.DESCRIPTION
Gets users phone extensions populated by the telep[honeNumber field in AD
.EXAMPLE
Get-Exts james
Returns any extensions where the name on it is "james"
.EXAMPLE
Get-Exts
Returns all extensions
.INPUTS
String
.OUTPUTS
Array
#>
[Alias('GetExts')]
[CmdletBinding()]
Param ([String]$person)
$EXTUsers = Get-Phones
$ExtList = @()
If ($person) {
$ExtList += $ExtUsers | Where-Object {$_.Name -match $person} | Select-Object -Property Name, IpPhone, OfficePhone, Ext
} Else {
$ExtList = $ExtUsers | Select-Object -Property Name, IpPhone, OfficePhone, Ext
}
Return $ExtList
}
Function Search-ADUser {
<#
.SYNOPSIS
Find any/all users that match.
.DESCRIPTION
Finds any/all users that even resemble your search criteria
.PARAMETER
Define the target to look for
.EXAMPLE
Search-ADUser jess
.INPUTS
String
.OUTPUTS
Array
#>
[Alias('fUser')]
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'partial name/username'
)]
[ValidateNotNullorEmpty()]
[String]$SearchString,
[Switch]$full
)
$AdParam = @{
Filter = "samaccountname -like '*$($SearchString)*' -or name -like '*$($SearchString)*' -or givenname -like '*$($SearchString)*' -or surname -like '*$($SearchString)*' -or userprincipalname -like '*$($SearchString)*'"
Properties = 'Company',
'CanonicalName',
'Department',
'Description',
'EmployeeID',
'Enabled',
'GivenName',
'Manager',
'Surname',
'Title',
'extensionAttribute7',
'Fax',
'mobile',
'OfficePhone',
'otherTelephone',
'PasswordLastSet',
'whenCreated',
'whenChanged'
Server = $script:PDC
}
$mSel = @{
Property = 'DistinguishedName',
'SamAccountname',
'Name',
'Enabled',
'EmployeeID',
@{n='exAt7';e={$_.extensionAttribute7}},
'GivenName',
'Surname',
'Company',
'Title',
'Description',
'Department',
@{n='Manager';e={Get-Mgr -user $_.Manager}},
@{n='Container';e={Get-Container -can $_.CanonicalName}},
'ObjectClass',
'ObjectGUID',
'SID',
'UserPrincipalName',
@{n='MobilePhone';e={Convert-StandardPhone -NumtoConv $_.mobile}},
@{n='OfficePhone';e={Convert-StandardPhone -NumtoConv $_.OfficePhone}},
@{n='otherTelephone';e={(@($_.otherTelephone | ForEach-Object {Convert-StandardPhone -NumtoConv $_}) -join ', ').TrimEnd(', ')}},
@{n='Fax';e={Convert-StandardPhone -NumtoConv $_.Fax}},
'PasswordLastSet',
'whenCreated',
'whenChanged'
}
$Match = Get-ADUser @AdParam | Select-Object @mSel
If ($full){
$Match = $Match
} Else {
$maSel = @{
Property = 'DistinguishedName',
'SamAccountname',
'Name',
'Enabled',
'EmployeeID',
'exAt7',
'Company',
'Title',
'Description',
'Department',
'Manager',
'Container',
'UserPrincipalName',
'MobilePhone',
'OfficePhone',
'otherTelephone',
'Fax',
'PasswordLastSet',
'whenCreated',
'whenChanged'
}
$Match = $Match | Select-Object @maSel
}
If ($null -eq $Match){
WH -t 'No matching accounts were found.'
} Else {
$Match
}
}
Function Search-EmpID {
<#
.SYNOPSIS
EmployeeID finder
.DESCRIPTION
Searches AD for the string you're trying to find in the Description field
.EXAMPLE
Search-EmpID 0123
.INPUTS
String
.OUTPUTS
Array
#>
[CmdletBinding()]
[Alias('flid')]
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'Search String'
)]
[ValidateNotNullorEmpty()]
[String]$SearchString,
[Switch]$full
)
$AdParam = @{
Filter = "EmployeeID -like '*$($SearchString)*'"
Properties = 'Company',
'CanonicalName',
'Department',
'Description',
'EmployeeID',
'Enabled',
'GivenName',
'Manager',
'Surname',
'Title'
Server = $script:PDC
}
$mSel = @{
Property = 'DistinguishedName',
'SamAccountname',
'Name',
'Enabled',
'EmployeeID',
'GivenName',
'Surname',
'Company',
'Title',
'Description',
'Department',
@{n='Manager';e={Get-Mgr -user $_.Manager}},
@{n='Container';e={Get-Container -can $_.CanonicalName}},
'ObjectClass',
'ObjectGUID',
'SID',
'UserPrincipalName'
}
$Match = Get-ADUser @AdParam | Select-Object @mSel
If ($full){
$Match = $Match
} Else {
$maSel = @{
Property = 'DistinguishedName',
'SamAccountname',
'Name',
'Enabled',
'EmployeeID',
'Company',
'Title',
'Description',
'Department',
'Manager',
'Container',
'UserPrincipalName'
}
$Match = $Match | Select-Object @maSel
}
If ($null -eq $Match) {
WH -t 'No matching accounts were found.'
} Else {
Return $Match
}
}
Function Search-Position {
<#
.SYNOPSIS
Department / Title finder
.DESCRIPTION
Searches AD for the string you're trying to find in the Description and Title field
.EXAMPLE
Search-Position 8080
.EXAMPLE
Search-Position Supervisor
.INPUTS
String
.OUTPUTS
Array
#>
[CmdletBinding()]
[Alias('FindDPT')]
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'Search String'
)]
[String]$DeptString,
[Switch]$full
)
$AdParam = @{
Filter = "Department -like '*$($DeptString)*' -or departmentNumber -like '*$($DeptString)*' -or Title -like '*$($DeptString)*'"
Properties = 'Company',
'CanonicalName',
'Department',
'Description',
'EmployeeID',
'Enabled',
'GivenName',
'Manager',
'Surname',
'Title',
'Fax',
'mobile',
'OfficePhone',
'otherTelephone'
Server = $script:PDC
}
$mSel = @{
Property = 'DistinguishedName',
'SamAccountname',
'Name',
'Enabled',
'EmployeeID',
'GivenName',
'Surname',
'Company',
'Title',
'Description',
'Department',
@{n='Manager';e={Get-Mgr -user $_.Manager}},
@{n='Container';e={Get-Container -can $_.CanonicalName}},
'ObjectClass',
'ObjectGUID',
'SID',
'UserPrincipalName',
@{n='MobilePhone';e={Convert-StandardPhone -NumtoConv $_.mobile}},
@{n='OfficePhone';e={Convert-StandardPhone -NumtoConv $_.OfficePhone}},
@{n='otherTelephone';e={(@($_.otherTelephone | ForEach-Object {Convert-StandardPhone -NumtoConv $_}) -join ', ').TrimEnd(', ')}},
@{n='Fax';e={Convert-StandardPhone -NumtoConv $_.Fax}}
}
$Match = Get-ADUser @AdParam | Select-Object @mSel
If ($full) {
$Match = $Match
} Else {
$maSel = @{
Property = 'DistinguishedName',
'SamAccountname',
'Name',
'Enabled',
'EmployeeID',
'Company',
'Title',
'Description',
'Department',
'Manager',
'Container',
'UserPrincipalName',
'MobilePhone',
'OfficePhone',
'otherTelephone',
'Fax'
}
$Match = $Match | Select-Object @maSel
}
If ($null -eq $Match) {
WH -t 'No matching accounts were found.'
} Else {
Return $Match
}
}
Function Find-Lockout {
<#
.SYNOPSIS
Find all users that are locked out
.DESCRIPTION
Finds all users that are locked out
.PARAMETER Unlock
Unlock all those locked out
.EXAMPLE
Find-Lockouts -Unlock
Unlocks all locked users
.EXAMPLE
Unlocker
menu to unlock users
.INPUTS
String
.OUTPUTS
Array
#>
[CmdletBinding()]
[Alias('Unlocker')]
[Alias('WhosLocked')]
Param([Switch]$Unlock)
Add-Type -AssemblyName PresentationFramework
$LockedOutUsers = Search-ADAccount -LockedOut
If (-not $Credential) {
If (Show-SavedCredential | Where-Object {$_.Keys -eq 'MySAUser'}) {
$Credential = Get-SavedCredential -Id 'MySAUser'
} Else {
Get-Credential
Register-SACreds
}
$Credential = Get-Credential
} Else {
$Credential = $Credential
}
If ($Unlock -OR ($MyInvocation.InvocationName -eq 'Unlocker')) {
If ($LockedOutUsers) {
### Function to test for a specific account lockout (verify)
Function Get-UserLockedOut {
<#
.SYNOPSIS
Find lockout status of a specific user
.DESCRIPTION
Finds current lockout status of a specific user by samaccountname
.PARAMETER CheckUser
The user to check if locked out.
.EXAMPLE
Get-UserLockedOut testuser
#>
Param (
[Parameter(
Mandatory = $true,
HelpMessage = 'Username required'
)]
[String]$CheckUser
)
$lockstatus = Get-ADUser -Identity $CheckUser -Properties LockedOut -Server $script:PDC |
Select-Object -Property Name, @{n='Username';e={$_.SamAccountName}}, LockedOut
$lockstatus = $lockstatus | Select-Object -Property Username, Name, LockedOut
Return $lockstatus
}
### 1. Get list of lockouts
$StepOne = Search-ADAccount -LockedOut |
Select-Object -Property Name, @{n='Username';e={$_.SamAccountName}} |
Sort-Object -Property Name
### 2. Put up menu (gridview) of locked accounts or tell user none were found
$StepTwo = If ($StepOne) {
#SINGLE $StepOne | Select-Object -Property Username,Name | Out-GridView -Title "User unlocker $(Get-Date)" -OutputMode Single
$StepOne |
Select-Object -Property Username, Name |
Out-GridView -Title "User unlocker $(Get-Date)" -Passthru
} Else {
[Windows.MessageBox]::Show('No locked out users found')
}
### 3. Check if a username was selected, and if-so unlock
If ($StepTwo.Username) {
### MULTIPLE
ForEach ($lockedUser in $StepTwo) {
#If ($lockedUser.Username){
#SINGLE $lockeduser = $StepTwo.Username
$lockedusername = $lockedUser.Username
$unlocks += $lockedusername
#SINGLE Get-ADUser $lockeduser | Unlock-ADAccount
Invoke-Command -ComputerName $PDC -Credential $Credential -ScriptBlock {
[CmdletBinding()]
Param ([string]$user)
Get-ADUser -Identity $user -Server $script:PDC |
Unlock-ADAccount
} -ArgumentList $lockedusername
}
} Else {
WH -t 'No user selected' -f Ye
}
### 4. Verify account was unlocked
### MULTIPLE
ForEach ($verification in $unlocks) {
#SINGLE If ($StepTwo.Username){
$v = Get-UserLockedOut -Checkuser $verification
If ($v.Lockedout -eq $false) {
#SINGLE Write-Host "Unlocked: $($lockeduser.name)" -ForegroundColor Green
WH -t ('Unlocked: {0}' -f $v.name) -f Gr
Return
} Else {
#SINGLE Write-Host "Unable to unlock $($lockeduser.name)" -ForegroundColor Red
WH -t ('Unable to unlock {0}' -f $v.name) -f Re
#SINGLE $lockedUser.DistinguishedName
$v.DistinguishedName
Return
}
}
$LockedOutUsers | Select-object -Property SamAccountName, Name
} Else {
WH -t 'Checked for user lockouts - none found.' -f Gr
}
} Else {
If ($LockedOutUsers) {
$LockedOutUsers | Select-Object -Property SamAccountName, Name
} Else {
WH -t 'No locked out users to report' -f Gr
}
}
}
Function Get-ADNestedGroupMembers {
<#
.SYNOPSIS
added displayname to the output, changed name to samaccountname in case of user objects.
.DESCRIPTION
Get nested group membership from a given group or a number of groups.
Function enumerates members of a given AD group recursively along with nesting level and parent group information.
It also displays if each user account is enabled.
When used with an -indent switch, it will display only names, but in a more user-friendly way (sort of a tree view)
.EXAMPLE
Get-ADNestedGroupMembers "MyGroup" | Export-CSV .\NedstedMembers.csv -NoTypeInformation
.EXAMPLE
Get-ADGroup -filter {Name -like "*MyGroup*"} | Get-ADNestedGroupMembers | ft -autosize
.EXAMPLE
Get-ADNestedGroupMembers "MyGroup" -indent
.INPUTS
String
.OUTPUTS
String
#>
[Alias('GetMembers')]
Param (
[Parameter(
ValuefromPipeline = $true,
HelpMessage = 'Name of the Group',
Mandatory = $true
)]
[String]$GroupName,
[Int]$nesting = -1,
[Int]$Circular = $null,
[Switch]$Indent
)
Function Format-Indent {
[CmdletBinding()]
Param ($List)
ForEach ($Line in $List) {
$Space = $null
For ($i=0;$i -lt $Line.Nesting;$i++) {$Space += ' '}
$Line.Name = "$Space" + "$($Line.Name)"
}
Return $List
}
$Table = $NestedMembers = $ADGroupName = $null
$Nesting++
$ADGroupName = Get-ADGroup -Identity $GroupName -Properties MemberOf,Members
$MemberOf = $ADGroupName | Select-Object -ExpandProperty MemberOf
Write-Verbose -Message ('Checking group: {0}' -f $ADGroupName.Name)
If ($ADGroupName) {
If ($Circular) {
$NestedMembers = Get-ADGroupMember -Identity $GroupName -Recursive
$Circular = $null
} Else {
$NestedMembers = Get-ADGroupMember -Identity $GroupName | Sort-Object -Property objectClass -Descending
If (!($NestedMembers)) {
$Unknown = $ADGroupName | Select-Object -ExpandProperty Members
If ($Unknown) {
$NestedMembers = @()
ForEach ($Member in $Unknown){$NestedMembers += Get-ADObject -Identity $Member}
}
}
}
ForEach ($NestedMember in $NestedMembers) {
$Props = @{
Type = $NestedMember.objectClass
Name = $NestedMember.Name
DisplayName = ''
ParentGroup = $ADGroupName.Name
Enabled = ''
Nesting = $Nesting
DN = $NestedMember.DistinguishedName
Comment = ''
}
If ($NestedMember.objectclass -eq 'user') {
$NestedADMember = Get-ADUser -Identity $NestedMember -Properties Enabled,DisplayName,Description
$Table = New-Object -TypeName PSObject -Property $props
$Table.Enabled = $NestedADMember.Enabled
$Table.Name = $NestedADMember.SamAccountName
$Table.DisplayName = $NestedADMember.DisplayName
$Table.Comment = $NestedADMember.Description
If ($Indent) {
Format-Indent -List $Table | Select-Object -Property @{N='Name';E={"$($_.Name) ($($_.DisplayName))"}}
} Else {
$Table | Select-Object -Property Type, Name, DisplayName, ParentGroup, Nesting, Enabled, DN, Comment
}
} ElseIf ($NestedMember.objectclass -eq 'group') {
$Table = New-Object -TypeName PSObject -Property $props
If ($MemberOf -contains $NestedMember.DistinguishedName) {
$Table.Comment = 'Circular membership'
$Circular = 1
}
If ($Indent) {
Format-Indent -List $Table | Select-Object -Property Name,Comment | Foreach-Object {
If ($_.Comment -ne ''){
WH -t ('{0} (Circular Membership)' -f $_.Name) -f Re
} Else {
WH -t ('{0}' -f $_.Name) -f Ye
}
}
} Else {
$Table | Select-Object -Property Type, Name, DisplayName, ParentGroup, Nesting, Enabled, DN, Comment
}
If ($Indent) {
Get-ADNestedGroupMembers -GroupName $NestedMember.distinguishedName -nesting $Nesting -circular $Circular -indent
} Else {
Get-ADNestedGroupMembers -GroupName $NestedMember.distinguishedName -nesting $Nesting -circular $Circular
}
} Else {
If ($NestedMember) {
$Table = New-Object -TypeName PSObject -Property $props
If ($Indent) {
Format-Indent -List $Table | Select-Object -Property Name
} Else {
$Table | Select-Object -Property Type, Name, DisplayName, ParentGroup, Nesting, Enabled, DN, Comment
}
}
}
}
}
}
Function Get-NewUsers {
<#
.SYNOPSIS
Get newly created users.
.DESCRIPTION
Get all newly created users within a specified amount of days.
If you choose -ft or -grid, you can only choose one.
If you choose both, ft is selected by default.
.PARAMETER cutoff
How many days old must a user be to be considered 'New'.
.PARAMETER app
Users created by app are put into root/Users.
.PARAMETER ft
Format output as Table
.PARAMETER grid
Format output as grid
.EXAMPLE
Get-NewUsers -cutoff 5 -app
show newly created users created in the past 5 days only in the root/Users OU.
.EXAMPLE
Get-NewUsers -cutoff 5
Shows all users everywhere created in the past 5 days.
Describe what this call does
.INPUTS
String
.OUTPUTS
Array
#>
[Alias('fnu')]
Param (
[Parameter(
Position = 0,
HelpMessage = 'Days old',
Mandatory = $true,
ValueFromPipelineByPropertyName = $true
)]
[Int]$cutoff,
[Switch]$app,
[Switch]$ft,
[Switch]$grid
)
If ($ft -and $grid) {$grid = $false}
$DateCutOff = (Get-Date).AddDays(-$cutoff)
If ($app) {
$fetchParams = @{
Filter = '*'
SearchBase = 'CN=Users,DC=shermco,DC=ind'
Properties = 'CanonicalName',
'Company',
'Department',
'Description',
'EmployeeID',
'Enabled',
'GivenName',
'Surname',
'Title',
'whenCreated',
'extensionAttribute7'
}
} Else {
$fetchParams = @{
Filter = '*'
Properties = 'CanonicalName',
'Company',
'Department',
'Description',
'EmployeeID',
'Enabled',
'GivenName',
'Surname',
'Title',
'whenCreated',
'extensionAttribute7'
}
}
$trSel = @{
Property = 'SamAccountName',
'Name',
#'UserPrincipalName',
#'GivenName',
#'Surname',
'EmployeeID',
'Enabled',
'Company',
'Title',
'Department',
@{n='exAt7';e={$_.extensionAttribute7}},
'whenCreated',
#'Description',
@{n='Container';e={Get-Container -can $_.CanonicalName}}
}
$toReturn = Get-ADUser @fetchParams |
Where-Object {$_.whenCreated -gt $datecutoff} |
Select-Object @trSel |
Sort-Object -Property 'Container', 'whenCreated'
If ($ft) {
$toReturn | Format-Table
} ElseIf ($grid) {
$toReturn | Out-GridView -Title ('{0} Users - Created in the past {1} Days' -f $toReturn.Count, $cutoff)
} Else {
Return $toReturn
}
}
Function Get-TreeStart {
<#
.SYNOPSIS
Get org chart tree start.
.DESCRIPTION
Attempt to figure out where an org chart tree begins.
.EXAMPLE
Get-TreeStart
.INPUTS
None
.OUTPUTS
Array
#>
<#
### This is where we figure out where to start our tree from (highest person listed in AD with most number of Direct Reports)
Get all users where:
* (They are enabled) AND
* (They have a title) AND
* (their name does not contain 'test') AND
* (they have someone directlyreporting to them
* and get the extended attributes of Manager/DirectReports/Title as well -- store in $treeStarter
#>
$tsParam = @{
Filter = '(Enabled -eq $true) -and (title -like "*") -and (name -notlike "*test*") -and (DirectReports -ne 0)'
Properties = 'Manager', 'DirectReports', 'Title'
}
$treeStarter = Get-ADUser @tsParam |
Where-Object {$null -eq $_.Manager} |
Select-Object -Property SamAccountName, Name, Title, @{n='DR';e={($_.directreports).count}} |
### Sort by dr then by name in descending order (if eyeball verifying, add " | formt-table" on the
### end of this line and only run from 'get-aduser' to 'format-table' to see the results)
Sort-Object -Property DR, Name -Descending |
Select-Object -First 1 |
Select-Object -ExpandProperty SamAccountName
Return $treeStarter
}
Function Get-OrgChart {
<#
.SYNOPSIS
display an on-screen org chart based on directreports in AD
.DESCRIPTION
Will utlize DirectReports attribute in AD to create and visually represent org chart tree on screen
.PARAMETER
start - give a username to start the tree from
.EXAMPLE
Get-OrgChart eWilliams
Draws an Org chart on-screen starting with Ed at the top
.EXAMPLE
Get-OrgChart
No username was specified so the script figures out who's logically the CEO and starts from there.
.INPUTS
String
.OUTPUTS
Stream
#>
Param (
[Parameter(Position=0)][String]$start,
[Switch]$noLegend
)
Begin {
### We use $count to declare how far into the tre we are. 0 is the very top
$count = 0
Function Get-DirectReports {
### $args is the argument given to the function which should be the user to start the tree building from
ForEach ($user in $args) {
### $currUser - get-aduser for the current user, include the attributes of Enabled,Title,Company,GivenName,SN (SurName),Department, and EmployeeId
$currUser = (Get-ADUser -Identity $user -Properties Enabled, Title, Company, GivenName, SN, Department, EmployeeId)
### Define what character set to use ('--') to indent as we expand through the tree...
### and how many times to use it depending on how far into the tree we are ("* $count")
$spacing = '--' * $count
### If the current user is enabled
If ($currUser.Enabled -eq $true) {
### Write what the count level we're at is (heirarchy level) -- don't line break
WH -t "$($count) " -f Ma -n
### If the current user EmployeeId field is populated, display it
### If thy don't, space over enough so the indentation aligns. -- dont line break
If ($currUser.EmployeeId) {
WH -t "$($currUser.EmployeeId) " -f Ma -n
} Else {
WH -t ' ' -n
}
### Re-create their proper name using their GivenName and SurName -- don't line break
WH -t "$($spacing) $($currUser.GivenName) $($currUser.SN) " -n
### include a separator to distinguish between the users name and everything else -- don't line break
WH -t '- ' -f DkCy -n
### Display whtever is in their Title field -- don't line break
WH -t "$($currUser.Title.trim()) " -f Cy -n
### If the user Department attribute has data...
If ($currUser.Department){
### if the data in the Department field is 6 characters long, display in green -- don't line break
If ((($currUser.Department | out-string).trim() | Measure-Object -Character).Characters -eq 6) {
WH -t "[$($currUser.Department)] " -f Gr -n
} Else {
### If the data in the Department field is not 6 characters (less/greater than) display it in Yellow -- don't line break
WH -t "[$($currUser.Department)] " -f Ye -n
}
} Else {
### If the user Department attribute is empty, display our brackets in Red -- don't line break
WH -t "[$($currUser.Department)] " -f Re -n
}
### If the Company attribute has data...
If ($currUser.Company) {
### Display the information in the Company field
WH -t "($($currUser.Company))" -f Cy
} Else {
### If the Company attribute is empty, display '(NA)' in Red
WH -t '(NA)' -f Re
}
} Else {
### If the current user is disabled, all of the code is the same with one exception
WH -t "$($count) " -f Ma -n
If ($currUser.EmployeeId) {
WH -t "$($currUser.EmployeeId) " -f Ma -n
} Else {
WH -t ' ' -n
}
WH -t "$($spacing) " -n
### Since the user is disabled, Write that in Red -- don't line break
WH -t '[Disabled] ' -f Re -n
WH -t "$($currUser.GivenName) $($currUser.SN) " -n
WH -t '- ' -f DkCy -n
WH -t "$($currUser.Title) " -f Ye -n
If ($currUser.Department) {
If ((($currUser.Department | Out-String).Trim() | Measure-Object -Character).Characters -eq 6) {
WH -t "[$($currUser.Department)] " -f Gr -n
} Else {
WH -t "[$($currUser.Department)] " -f Ye -n
}
} Else {
WH -t "[$($currUser.Department)] " -f Re -n
}
If ($currUser.Company){
WH -t "($($currUser.Company))" -f Cy
} Else {
WH -t '(NA)' -f Re
}
}
$count++
Get-ADUser -Identity $user -Properties DirectReports | Select-Object -ExpandProperty DirectReports | ForEach-Object {Get-DirectReports $_}
$count--
}
}
}
Process {
If ($start) {
$RootUser = $start
} Else {
$RootUser = Get-TreeStart
}
Get-DirectReports $RootUser
If (-not $noLegend) {
WH -t "`n --------- Legend ---------- " -f Ye
WH -t ' X E00000 : ' -f Ma -n
WH -t 'Number of levels between that person and the source along with EmployeeID (if populated).'
WH -t ' ------ : ' -n
WH -t 'Visual representation of depth'
WH -t ' Name : ' -n
WH -t 'Name ' -n
WH -t 'May be prefixed with ' -n
WH -t '[Disabled]' -f Re -n
WH -t ' to maybe revisit later.'
WH -t ' Title : ' -n
WH -t 'Cyan' -f Cy
WH -t ' Department: ' -n
WH -t 'Green = Good, ' -f Gr -n
WH -t 'Yellow = Bad, ' -f Ye -n
WH -t 'Red = empty' -f Re
WH -t ' Company : ' -n
WH -t 'Cyan' -f Cy -n
WH -t ' - If empty: ' -n
WH -t '(NA)' -f Re
}
}
}
Function Show-Everyone {
<#
.SYNOPSIS
Show everyone who is enabled
.DESCRIPTION
Will show name,company,title,description,username,license, and manager for all enabled users
.EXAMPLE
Show-Everyone
Raw output
.EXAMPLE
Show-Everyone -Grid
Out to Gridview
.INPUTS
None
.OUTPUTS
Array
#>
#Requires -Module ActiveDirectory
[CmdletBinding()]
[Alias('everyone')]
Param ([Switch]$Grid)
$pi = 0
$Progress = @{
Activity = 'Working through users . . .'
CurrentOperation = 'Loading'
PercentComplete = 0
}
### Fetch Users
$EveryoneParams = @{
Filter = 'Enabled -eq $true -and Company -like "*" -and Title -like "*" -and Department -like "*"'
Properties = 'DisplayName',
'Enabled',
'Company',
'Department',
'Title',
'Manager',
'Description',
'EmployeeID',
'EmailAddress',
'GivenName',
'sn',
'CanonicalName'
}
$ActiveUsers = Get-ADUser @EveryoneParams
### Organize users and add to array
$ActiveEveryone = @()
Foreach ($aUser in $ActiveUsers) {
$ProperName = "$($aUser.GivenName) $($aUser.sn)"
$pi++
[Int]$percentage = ($pi / $ActiveUsers.Count)*100
$Progress.CurrentOperation = "$pi of $($ActiveUsers.Count) - $($aUser.Name)"
$Progress.PercentComplete = $percentage
Write-Progress @Progress
$ActiveEveryone += [PSCustomObject][Ordered]@{
UserName = $aUser.SamAccountName
DisplayName = $aUser.DisplayName
ProperName = $ProperName
Enabled = $aUser.Enabled
Company = $aUser.Company
Department = $aUser.Department
Title = $aUser.Title
Manager = If ($aUser.Manager){($aUser.Manager).split('OU')[0].trimend(',').split('=')[1].replace('\','')};
Description = $aUser.Description
EmployeeID = $aUser.EmployeeID
EmailAddress = $aUser.EmailAddress
Container = $aUser.CanonicalName -ireplace '\/[^\/]+$',''
}
}
#region Cleanup
Write-Progress -Activity 'Working through users . . .' -Status 'Ready' -Completed
$ActiveEveryone = $ActiveEveryone | Sort-Object -Property EmployeeID
Remove-Variable -Name ActiveUsers
#endregion Cleanup
If (-not $Grid) {
Return $ActiveEveryone
} Else {
$ActiveEveryone | Out-GridView -Title "EVERYONE! $($ActiveEveryone.Count) - $(Get-Date)"
Remove-Variable -Name ActiveEveryone
}
}
#endregion User
#region Reminders
Function script:365reminder {
WH -t ' • Connect-AzureActiveDirectory ' -f Ye -n
WH -t ' Connects to Azure Active Directory' -f Cy
WH -t ' • Connect-AzureRMS ' -f Ye -n
WH -t ' Connects to Azure Rights Management' -f Cy
WH -t ' • Connect-ExchangeOnline ' -f Ye -n
WH -t ' Connects to Exchange Online' -f Cy
WH -t ' • Connect-ExchangeOnlinev2 ' -f Ye -n
WH -t ' Connects to Exchange Online using REST module' -f Cy
WH -t ' • Connect-SkypeOnline ' -f Ye -n
WH -t ' Connects to Skype for Business Online' -f Cy
WH -t ' • Connect-EOP ' -f Ye -n
WH -t ' Connects to Exchange Online Protection' -f Cy
WH -t ' • Connect-ComplianceCenter ' -f Ye -n
WH -t ' Connects to Compliance Center' -f Cy
WH -t ' • Connect-SharePointOnline ' -f Ye -n
WH -t ' Connects to SharePoint Online' -f Cy
WH -t ' • Connect-MSTeams ' -f Ye -n
WH -t ' Connects to Microsoft Teams' -f Cy
WH -t ' • Get-Office365Credentials ' -f Ye -n
WH -t ' Gets Office 365 credentials' -f Cy
WH -t ' • Connect-ExchangeOnPremises ' -f Ye -n
WH -t ' Connects to Exchange On-Premises' -f Cy
WH -t ' • Get-OnPremisesCredentials ' -f Ye -n
WH -t ' Gets On-Premises credentials' -f Cy
WH -t ' • Get-ExchangeOnPremisesFQDN ' -f Ye -n
WH -t ' Gets FQDN for Exchange On-Premises' -f Cy
WH -t ' • Get-Office365Tenant ' -f Ye -n
WH -t ' Gets Office 365 tenant name' -f Cy
WH -t ' • Set-Office365Environment ' -f Ye -n
WH -t " Configures Uri's and region to use" -f Cy
WH -t ' • Update-Office365Modules ' -f Ye -n
WH -t ' Updates supported Office 365 modules' -f Cy
WH -t ' • Report-Office365Modules ' -f Ye -n
WH -t ' Report on known vs online module versions' -f Cy
$DOTS = '••••'
WH -t $DOTS -f Re -n
WH -t ' Use ' -f Ye -n
WH -t 'load365 ' -f Cy -n
WH -t ' to load 365 connectors ' -f Ye -n
WH -t $DOTS -f Re
WH -t $DOTS -f Re -n
WH -t ' Use ' -f Ye -n
WH -t '365reminder' -f Cy -n
WH -t ' for a reminder of the 365 plugins ' -f Ye -n
WH -t $DOTS -f Re
}
Function Get-Reminder {
<#
.SYNOPSIS
Brief reminder of some commands in this module.
.DESCRIPTION
Highlight of some commands in this module.
.EXAMPLE
Get-Reminder
.INPUTS
None
.OUTPUTS
Stream
#>
WH -t "This is just a slight overview of commands:`r`n" -f Ye
WH -t ' --- Operational -------------------------------------------------------' -f Ma
### Get-Container (container)
WH -t ' • HR - Changes a disk size to human readable' -f Ye
WH -t ' • DU - Display Disk Usage on a directory' -f Ye
WH -t ' • DF - Display Drive information' -f Ye
WH -t ' • DiskUsage - Get disk usage ' -f Ye -n
WH -t '(' -f Ye -n
WH -t 'Get-DiskFree ' -f Cy -n
WH -t 'servername ' -f Re -n
WH -t '-- ' -f Ye -n
WH -t '[' -f Ma -n
WH -t 'add ' -f Gr -n
WH -t '-format ' -f Cy -n
WH -t 'to make output more readable' -f Gr -n
WH -t ']' -f Ma -n
WH -t ')' -f Ye
WH -t ' • Grep - Find a string' -f Ye
### Read-OpenFileDialog
WH -t ' • Tail - Follow a log file' -f Ye
### Get-UniquePerms
WH -t ' • Touch - Create a blank file' -f Ye
WH -t ' • SUDO - Run next command as admin' -f Ye
### Out-ExcelClip (clipXL)
WH -t ' • SU - Start new Admin session' -f Ye
WH -t ' • UpdateModules - Checks installed PS modules and updates accordingly' -f Ye
### Install-WinUpdates
### Update-JProfile
WH -t ' --- Domain ------------------------------------------------------------' -f Ma
WH -t ' • GetDCs - Get domain controller list with roles' -f Ye
WH -t ' • GetDHCPs - Get list of DHCP servers' -f Ye
WH -t ' • GetDNS - Get list of DNS Servers' -f Ye
WH -t ' • Find-PrintServers - Finds print servers on the network' -f Ye
### Get-EmptyOUs
### Get-SchemaVersion
WH -t ' --- User --------------------------------------------------------------' -f Ma
WH -t ' • WhosLocked - List all locked out users ' -f Ye -n
WH -t '(' -f Ye -n
WH -t 'whoslocked ' -f Cy -n
WH -t '| ' -n
WH -t 'ft' -f Cy -n
WH -t ')' -f Ye -n
WH -t ' -- or -- ' -f Ye -n
WH -t '(' -f Ye -n
WH -t 'unlocker' -f Cy -n
WH -t ')' -f Ye
### Get-CurrentUser
WH -t ' • Get-Phones - Get phone list' -f Ye
WH -t ' • Get-Exts - Get phone extension if listed ' -f Ye -n
WH -t '(' -f Ye -n
WH -t 'GetExts ' -f Cy -n
WH -t 'john' -f Re -n
WH -t ')' -f Ye
WH -t ' • WhosOn - Get whos on a PC' -f Ye
### Get-Mgr (gmgr)
WH -t ' • FUser - Find all users who even resemble search ' -f Ye -n
WH -t '(' -f Ye -n
WH -t 'fuser ' -f Cy -n
WH -t 'jessica ' -f Re -n
WH -t '| ' -n
WH -t 'select name ' -f Cy -n
WH -t '| ' -n
WH -t 'ft' -f Cy -n
WH -t '(' -f Ye
WH -t ' • Flid - Find an EmployeeID' -f Ye
WH -t ' • FindDPT - Find matches to Description, Dept fields, or Title' -f Ye
WH -t ' • FNU - Get New Users ' -f Ye -n
WH -t '(' -f Ye -n
WH -t 'fnu ' -f Cy -n
WH -t '5' -f Re -n
WH -t ' -- ' -f Ye -n
WH -t '[' -f Ma -n
WH -t 'add ' -f Gr -n
WH -t '-app ' -f Cy -n
WH -t 'to see App-Created Users.' -f Gr -n
WH -t ' | ' -f Ye -n
WH -t 'use ' -f Gr -n
WH -t '-ft ' -f Cy -n
WH -t 'or ' -f Gr -n
WH -t '-grid' -f Cy -n
WH -t ' to format output.' -f Gr -n
WH -t ']' -f Ma -n
WH -t ')' -f Ye
### Search-Position (FindDPT)
WH -t ' • Everyone - Show all users' -f Ye
### Get-OrgChart
WH -t ' • Get-ADFSLocks - Find all ADFS lock events for a user' -f Ye
WH -t ' • Get-UserRestore - Restore auto-Disabled user ' -f Ye -n
WH -t '(' -f Ye -n
WH -t 'restoreuser' -f Cy -n
WH -t ')' -f Ye
WH -t ' • Deny-User - Disabled user(s) ' -f Ye -n
WH -t '(' -f Ye -n
WH -t 'killuser' -f Cy -n
WH -t ')' -f Ye
WH -t ' --- Group -------------------------------------------------------------' -f Ma
WH -t ' • GetMembers - Get all users of a group even if nested' -f Ye
### Get-EmptyGroupDetails
WH -t ' --- Computer ----------------------------------------------------------' -f Ma
WH -t ' • GetVitals - Get basic info from a computer ' -f Ye -n
WH -t '(' -f Ye -n
WH -t 'Get-ComputerInfo ' -f Cy -n
WH -t 'testmachine01' -f Re -n
WH -t ')' -f Ye
WH -t ' • Get-Serials - Get serial numbers from PCs and monitors' -f Ye
### Get-SystemInfo
### Get-TrustStatus
WH -t ' • FPuter - Find all computers who even resemble search word' -f Ye
WH -t ' • StalePCs - Get list of dead computer objects' -f Ye
### Show-SysInfos
### Get-OutDatedOS
### Get-StalePCs
#WH ' • DHCPReport - Get DHCP Report' -f Ye
#WH ' • UsersOn - Runs WhosOn for a set of machines' -f Ye
$DOTS = '••••'
WH -t ' '
WH -t $DOTS -f Re -n
WH -t ' Use ' -f Ye -n
WH -t 'Get-Help <Command Name>' -f Cy -n
WH -t ' for help ' -f Ye -n
WH -t $DOTS -f Re
WH -t $DOTS -f Re -n
WH -t ' Use ' -f Ye -n
WH -t 'reminder' -f Cy -n
WH -t ' to get this reminder of functions ' -f Ye -n
WH -t $DOTS -f Re
WH -t $DOTS -f Re -n
WH -t ' Use ' -f Ye -n
WH -t 'Show-Commands' -f Cy -n
WH -t ' for a full list of cmdlets ' -f Ye -n
WH -t $DOTS -f Re
WH -t $DOTS -f Re -n
WH -t ' Use ' -f Ye -n
WH -t 'Show-Alias' -f Cy -n
WH -t ' for a full list of aliases from ' -f Ye -n
WH -t 'Show-Commands ' -f Cy -n
WH -t $DOTS -f Re
WH -t $DOTS -f Re -n
WH -t ' Use ' -f Ye -n
WH -t '365reminder' -f Cy -n
WH -t ' for a reminder of the 365 plugins ' -f Ye -n
WH -t $DOTS -f Re
WH -t ' '
}
$script:helper = (Get-Module -Name $profile -ListAvailable | Select-Object -ExpandProperty ExportedCommands).Values
Function Show-Commands {
<#
.SYNOPSIS
Show commands in this module.
.DESCRIPTION
Shows all functions within this module.
.EXAMPLE
Show-Commands
Describe what this call does
.INPUTS
None
.OUTPUTS
Array
#>
$mh = @()
$temp = $helper
Foreach ($c in $temp) {
$ho = [PsCustomObject][Ordered]@{
Name = $c.Name
Alias = (Get-Alias -Definition $c.Name -ErrorAction SilentlyContinue).Name
Description = $((Get-Help -Name $c.Name).Description).Text
}
$mh += $ho
}
$res = $mh | Sort-Object -Property Name
Return $res
}
Function Show-Alias {
<#
.SYNOPSIS
Show Aliases for Shermco-Module Functions.
.DESCRIPTION
Show Aliases for Shermco-Module Functions.
.EXAMPLE
Show-Alias
Describe what this call does
.INPUTS
None
.OUTPUTS
Array
#>
($helper | ForEach-Object {Get-Alias -Definition $_.Name -ErrorAction SilentlyContinue}) |
Select-Object -Property @{n='Function';e={$_.Definition}}, @{n='Alias';e={$_.Name}}
}
#endregion Reminders
#endregion Functions
#region Customizations
$PSDefaultParameterValues = @{
'Export-Csv:Delimiter' = ','
'Export-Csv:Encoding' = 'UTF8'
'Export-Csv:NoTypeInformation' = $true
'Convertto-Csv:Delimiter' = ','
'Convertto-Csv:NoTypeInformation' = $true
}
#endregion Customizations
#region Cosmetics
$script:Root = Test-Administrator
If ($Host.Name -eq 'ConsoleHost') {
Get-Elevation
Set-WindowTitle
Set-PSReadlineOption -HistoryNoDuplicates
Set-PSReadlineOption -HistorySearchCursorMovesToEnd
Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward
Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward
Set-PSReadlineKeyHandler -Key 'Ctrl+[' -Function CaptureScreen
Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete
Set-PSReadlineOption -ShowToolTips -BellStyle None
}
#endregion
#region Prompt
$script:PromptSettings = [PSCustomObject] @{
Separator = ' > '
OpeningBrace = '['
ClosingBrace = ']'
DefaultForegroundColor = $Host.UI.RawUI.ForegroundColor
DefaultBackgroundColor = $Host.UI.RawUI.BackgroundColor
IsAdminForeGroundColor = [ConsoleColor]::DarkGray
IsAdminBackGroundColor = [ConsoleColor]::Red
UserForegroundColor = [ConsoleColor]::Gray
UserBackgroundColor = [ConsoleColor]::DarkBlue
LocationForegroundColor = [ConsoleColor]::White
LocationBackgroundColor = [ConsoleColor]::DarkBlue
ElevatedPrompt = '#'
StandardPrompt = '$'
PromptForegroundColor = [ConsoleColor]::DarkCyan
PromptBackgroundColor = [ConsoleColor]::DarkBlue
PromptErrorForegroundColor = [ConsoleColor]::White
PromptErrorBackgroundColor = [ConsoleColor]::Red
AddedSymbol = '+'
ModifiedSymbol = '~'
DeletedSymbol = '-'
UnmergedSymbol = '?'
Delimiter = ' |'
}
Function script:Prompt {
# Locking on console, we need to remember to use the mutex name "ConsoleMtx"
$mtx = New-Object -TypeName System.Threading.Mutex -ArgumentList ($false, 'ConsoleMtx')
$null = $mtx.WaitOne()
$Script:Last = $?
$s = $script:PromptSettings
#$U = '[{0}@{1}' -f $env:USERNAME.ToLower(), $env:COMPUTERNAME.ToLower()
$U = '[{0}' -f $env:USERNAME.ToLower()
Write-Host $U -NoNewLine -ForegroundColor $s.UserForegroundColor -BackgroundColor $s.UserBackgroundColor
Write-Host $s.Separator -NoNewLine -ForegroundColor magenta -BackgroundColor $s.LocationBackgroundColor
$Location = '{0}] ' -f ((Get-Location).Path.Replace($env:USERPROFILE, '~'))
Write-Host $Location -NoNewLine -BackgroundColor $s.LocationBackgroundColor -ForegroundColor $s.LocationForegroundColor
If ($Script:Last) {
$promptForegroundColor = $s.PromptForegroundColor
$promptBackgroundColor = $s.PromptBackgroundColor
} Else {
$promptForegroundColor = $s.PromptErrorForegroundColor
$promptBackgroundColor = $s.PromptErrorBackgroundColor
}
If ($script:Root) {
$p = $s.ElevatedPrompt
} else {
$p = $s.StandardPrompt
}
Write-Host $p -NoNewLine -ForegroundColor $promptForegroundColor -BackgroundColor $promptBackgroundColor
$null = $mtx.ReleaseMutex()
$mtx.Dispose()
Return ' '
}
#endregion Prompt
#region playful quotes typed out
Function Greetings {
$strings = "How about a nice game of chess?`n",
"Would you like to play a game?`n",
"How about a nice game of Global Thermonuclear War.`n",
"The primary goal is to win the game.`n",
"The only winning move is not to play`n"
Write-Host "> Greetings Professor Falken . . .`n> " -NoNewLine -ForegroundColor Gray #-BackgroundColor Black
#$string = "Greetings Professor Falken...`nShall we play a game?`n"
Start-Sleep -milliseconds 500
$string = Get-Random -InputObject $strings
$Random = New-Object -TypeName System.Random
$string -split '' | ForEach-Object {
Write-Host $_ -nonew -ForegroundColor Gray #-BackgroundColor Black
Start-Sleep -milliseconds $(1 + $Random.Next(60))
}
}
#endregion
Get-reminder
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment