PSSharedGoods.psm1
function Add-PropertyToList { [CmdletBinding()] param( $List, [ValidateSet("Password", "MailNickName")][string] $PropertyName ) foreach ($Object in $List) { if ($PropertyName -eq 'Password') { $PropertyValue = Get-RandomPassword $Object | Add-Member -MemberType NoteProperty -Name $PropertyName -Value $PropertyValue -Force } if ($PropertyName -eq 'MailNickName') { $PropertyValue = ($Object.UserPrincipalName).Split('@')[0] #$PropertyValue = $Split[0] $Object | Add-Member -MemberType NoteProperty -Name $PropertyName -Value $PropertyValue -Force } } return $List } function Add-ToArray { [CmdletBinding()] param( [System.Collections.ArrayList] $List, [Object] $Element ) #Write-Verbose "Add-ToArray - Element: $Element" [void] $List.Add($Element) #> $null } function Add-ToArrayAdvanced { [CmdletBinding()] param( [System.Collections.ArrayList] $List, [Object] $Element, [switch] $SkipNull, [switch] $RequireUnique, [switch] $FullComparison, [switch] $Merge ) if ($SkipNull -and $Element -eq $null) { #Write-Verbose "Add-ToArrayAdvanced - SkipNull used" return } if ($RequireUnique) { if ($FullComparison) { foreach ($ListElement in $List) { if ($ListElement -eq $Element) { $TypeLeft = Get-ObjectType -Object $ListElement $TypeRight = Get-ObjectType -Object $Element if ($TypeLeft.ObjectTypeName -eq $TypeRight.ObjectTypeName) { #Write-Verbose "Add-ToArrayAdvanced - RequireUnique with full comparison used" return } } } } else { if ($List -contains $Element) { #Write-Verbose "Add-ToArrayAdvanced - RequireUnique on name used" return } } } #Write-Verbose "Add-ToArrayAdvanced - Adding ELEMENT: $Element" if ($Merge) { [void] $List.AddRange($Element) # > $null } else { [void] $List.Add($Element) # > $null } } function Add-ToHashTable($Hashtable, $Key, $Value) { if ($null -ne $Value -and $Value -ne '') { $Hashtable.Add($Key, $Value) } } <# Output of Get-ADPrincipalGroupmembership: distinguishedName : CN=Organization Management,OU=Microsoft Exchange Security Groups,DC=ad,DC=evotec,DC=xyz GroupCategory : Security GroupScope : Universal name : Organization Management objectClass : group objectGUID : 551c2400-f0d2-4aa6-8dbf-f9722ceb8675 SamAccountName : Organization Management SID : S-1-5-21-853615985-2870445339-3163598659-1117 #> function Add-WinADUserGroups { [CmdletBinding()] [alias("Add-ADUserGroups")] param( [parameter(Mandatory = $true)][Object] $User, [string[]] $Groups, [string] $FieldSearch = 'Name', [switch] $WhatIf ) $Object = @() try { $ADgroups = Get-ADPrincipalGroupMembership -Identity $User.DistinguishedName | Where-Object {$_.Name -ne "Domain Users" } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $Group.Name; Extended = $ErrorMessage } } if ($Groups) { foreach ($Group in $Groups) { if ($ADgroups.$FieldSearch -notcontains $Group) { try { if (-not $WhatIf) { Add-ADGroupMember -Identity $Group -Members $User.DistinguishedName -ErrorAction Stop } $Object += @{ Status = $true; Output = $Group; Extended = 'Added to group.' } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $Group; Extended = $ErrorMessage } } } else { # Turned off to not clutter view, may required turning back on. #$Object += @{ Status = $false; Output = $Group; Extended = 'Already exists.' } } } } return $Object } function Compare-ObjectsAdvanced { param( [object] $Object1, [object] $Object2, [alias('Property')][string] $CommonProperty = 'DistinguishedName', [string[]] $AddObjectArrayName, [object[]] $AddObjectArray, [string] $Object1Property, [string] $Object2Property, [string] $ObjectPropertySubstitute = 'SpecialValueToCompare', [switch] $RemoveSideIndicator, [switch] $KeepTemporaryProperty, [ValidateSet('Left', 'Right')][string] $Side = 'Left' # May need Both later on ) $Objects = New-GenericList if ($null -eq $Object1 -and $null -eq $Object2) { # nothing to do, doesn't event need check for distribution groups because it should be empty as well. } elseif ($null -eq $Object1) { # nothing to do } elseif ($null -eq $Object2) { foreach ($G in $Object1) { #Add-Member -InputObject $G -MemberType NoteProperty -Name 'OrganizationalUnit' -Value $OrganizationalUnit -Force # for ($a = 0; $a -lt $AddObjectArrayName.Count; $a++) { $G | Add-Member -MemberType NoteProperty -Name $AddObjectArrayName[$a] -Value $AddObjectArray[$a] -Force } $Objects.Add($G) } } else { $Terminate = New-GenericList -Type [bool] if ($Object1Property -and $Object2Property) { if ($Object1[0].PSObject.Properties.Name -notcontains $Object1Property) { Write-Warning -Message "Compare-InfrastructureObjects - Object1 property doesn't exists $Object1Property" $Terminate.Add($true) } if ($Object2[0].PSObject.Properties.Name -notcontains $Object2Property) { Write-Warning -Message "Compare-InfrastructureObjects - Object2 property doesn't exists $Object2Property" $Terminate.Add($true) } if ($Terminate -contains $true) { return } $Object1 | Add-Member -MemberType AliasProperty -Name $ObjectPropertySubstitute -Value $Object1Property -Force $Object2 | Add-Member -MemberType AliasProperty -Name $ObjectPropertySubstitute -Value $Object2Property -Force $Compare = Compare-Object -ReferenceObject $Object1 -DifferenceObject $Object2 -Property $ObjectPropertySubstitute -PassThru } else { if ($Object1[0].PSObject.Properties.Name -notcontains $CommonProperty) { Write-Warning -Message "Compare-InfrastructureObjects - Object1 property doesn't exists $CommonProperty" $Terminate.Add($true) } if ($Object2[0].PSObject.Properties.Name -notcontains $CommonProperty) { Write-Warning -Message "Compare-InfrastructureObjects - Object2 property doesn't exists $CommonProperty" $Terminate.Add($true) } if ($Terminate -contains $true) { return } $Compare = Compare-Object -ReferenceObject $Object1 -DifferenceObject $Object2 -Property $CommonProperty -PassThru } if ($Side -eq 'Left') { $Compare = $Compare | Where-Object { $_.SideIndicator -eq '<=' } } elseif ($Side -eq 'Right') { $Compare = $Compare | Where-Object { $_.SideIndicator -eq '=>' } } else { # left just in case Equal is needed $Compare = $Compare | Where-Object { $_.SideIndicator -eq '==' } } foreach ($G in $Compare) { if ($RemoveSideIndicator) { $G.PSObject.Members.Remove('SideIndicator') } if (-not $KeepTemporaryProperty) { $G.PSObject.Members.Remove($ObjectPropertySubstitute) } for ($a = 0; $a -lt $AddObjectArrayName.Count; $a++) { $G | Add-Member -MemberType NoteProperty -Name $AddObjectArrayName[$a] -Value $AddObjectArray[$a] -Force } $Objects.Add($G) } } return $Objects } Function Compare-ObjectProperties { Param( [PSObject]$ReferenceObject, [PSObject]$DifferenceObject ) $objprops = $ReferenceObject | Get-Member -MemberType Property, NoteProperty | % Name $objprops += $DifferenceObject | Get-Member -MemberType Property, NoteProperty | % Name $objprops = $objprops | Sort | Select -Unique $diffs = @() foreach ($objprop in $objprops) { $diff = Compare-Object $ReferenceObject $DifferenceObject -Property $objprop if ($diff) { $diffprops = @{ PropertyName = $objprop RefValue = ($diff | ? {$_.SideIndicator -eq '<='} | % $($objprop)) DiffValue = ($diff | ? {$_.SideIndicator -eq '=>'} | % $($objprop)) } $diffs += New-Object PSObject -Property $diffprops } } if ($diffs) {return ($diffs | Select PropertyName, RefValue, DiffValue)} } <# https://blogs.technet.microsoft.com/janesays/2017/04/25/compare-all-properties-of-two-objects-in-windows-powershell/ $ad1 = Get-ADUser amelia.mitchell -Properties * $ad2 = Get-ADUser carolyn.quinn -Properties * Compare-ObjectProperties $ad1 $ad2 #> function Connect-WinAzure { [CmdletBinding()] param( [string] $SessionName = 'Azure MSOL', [string] $Username, [string] $Password, [switch] $AsSecure, [switch] $FromFile, [switch] $Output ) $Credentials = Request-Credentials -UserName $Username ` -Password $Password ` -AsSecure:$AsSecure ` -FromFile:$FromFile ` -Service $SessionName ` -Output if ($Credentials -isnot [PSCredential]) { if ($Output) { return $Credentials } else { return } } try { Connect-MsolService -Credential $Credentials -ErrorAction Stop $Connected = $true } catch { $Connected = $false $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " if ($Output) { return @{ Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" } } else { Write-Warning "Connect-WinAzure - Failed with error message: $ErrorMessage" return } } if ($Connected -eq $false) { if ($Output) { return @{ Status = $false; Output = $SessionName; Extended = 'Connection Failed.' } } else { return } } else { if ($Output) { return @{ Status = $true; Output = $SessionName; Extended = 'Connection Established.' } } else { return } } } function Connect-WinAzureAD { [CmdletBinding()] param( [string] $SessionName = 'Azure AD', [string] $Username, [string] $Password, [switch] $AsSecure, [switch] $FromFile, [switch] $Output ) $Credentials = Request-Credentials -UserName $Username ` -Password $Password ` -AsSecure:$AsSecure ` -FromFile:$FromFile ` -Service $SessionName ` -Output if ($Credentials -isnot [PSCredential]) { if ($Output) { return $Credentials } else { return } } try { $Session = Connect-AzureAD -Credential $Credentials -ErrorAction Stop } catch { $Session = $null $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " if ($Output) { return @{ Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" } } else { Write-Warning "Connect-WinAzureAD - Failed with error message: $ErrorMessage" return } } if (-not $Session) { if ($Output) { return @{ Status = $false; Output = $SessionName; Extended = 'Connection Failed.' } } else { return } } if ($Output) { return @{ Status = $true; Output = $SessionName; Extended = 'Connection Established.' } } } function Connect-WinExchange { [CmdletBinding()] param( [string] $SessionName = 'Exchange', [string] $ConnectionURI = 'http://ex2013x3.ad.evotec.xyz/Powershell', # https://outlook.office365.com/powershell-liveid/ [ValidateSet("Basic", "Kerberos")][String] $Authentication = 'Kerberos', [string] $Username, [string] $Password, [switch] $AsSecure, [switch] $FromFile, [string] $Prefix, [switch] $Output ) $Object = @() if ($Authentication -ne 'Kerberos') { $Credentials = Request-Credentials -UserName $Username ` -Password $Password ` -AsSecure:$AsSecure ` -FromFile:$FromFile ` -Service $SessionName ` -Output if ($Credentials -isnot [PSCredential]) { if ($Output) { return $Credentials } else { return } } } else { # Credentials should be null for Kerberos - Current user will run it $Credentials = $null } $ExistingSession = Get-PSSession -Name $SessionName -ErrorAction SilentlyContinue if ($ExistingSession.Availability -contains 'Available') { foreach ($Session in $ExistingSession) { if ($Session.Availability -eq 'Available') { if ($Output) { $Object += @{ Status = $true; Output = $SessionName; Extended = "Will reuse established session to $($Session.ComputerName)" } } else { Write-Verbose "Connect-WinExchange - reusing session $($Session.ComputerName)" } } } } else { Write-Verbose "Connect-WinExchange - Creating Session to URI: $ConnectionURI" $SessionOption = New-PSSessionOption -SkipRevocationCheck -SkipCACheck -SkipCNCheck -Verbose:$false try { if ($Credentials) { Write-Verbose 'Connect-WinExchange - Creating new session using Credentials' $Session = New-PSSession -Credential $Credentials -ConfigurationName Microsoft.Exchange -ConnectionUri $ConnectionURI -Authentication $Authentication -SessionOption $sessionOption -Name $SessionName -AllowRedirection -ErrorAction Stop -Verbose:$false } else { Write-Verbose 'Connect-WinExchange - Creating new session without Credentials' $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ConnectionURI -Authentication $Authentication -SessionOption $sessionOption -Name $SessionName -AllowRedirection -Verbose:$false -ErrorAction Stop } } catch { $Session = $null $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " if ($Output) { $Object += @{ Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" } return $Object } else { Write-Warning "Connect-WinExchange - Failed with error message: $ErrorMessage" return } } } # Failed connecting to session if (-not $Session) { if ($Output) { $Object += @{ Status = $false; Output = $SessionName; Extended = 'Connection failed.' } return $Object } else { return } } $CurrentVerbosePreference = $VerbosePreference; $VerbosePreference = 'SilentlyContinue' # weird but -Verbose:$false doesn't do anything $CurrentWarningPreference = $WarningPreference; $WarningPreference = 'SilentlyContinue' # weird but -Verbose:$false doesn't do anything if ($Prefix) { #Write-Verbose "Prefix used $Prefix" Import-Module (Import-PSSession -Session $Session -AllowClobber -DisableNameChecking -Prefix $Prefix -Verbose:$false) -Global -Prefix $Prefix } else { #Write-Verbose "Prefix used - None" Import-Module (Import-PSSession -Session $Session -AllowClobber -DisableNameChecking -Verbose:$false) -Global } $VerbosePreference = $CurrentVerbosePreference $WarningPreference = $CurrentWarningPreference ## Verify Connectivity #$CheckAvailabilityCommands = Test-AvailabilityCommands -Commands "Get-$($Service.Prefix)ExchangeServer", "Get-$($Service.Prefix)MailboxDatabase", "Get-$($Service.Prefix)PublicFolderDatabase" $CheckAvailabilityCommands = Test-AvailabilityCommands -Commands "Get-$($Prefix)MailContact", "Get-$($Prefix)Mailbox" if ($CheckAvailabilityCommands -contains $false) { if ($Output) { $Object += @{ Status = $false; Output = $SessionName; Extended = 'Commands unavailable.' } return $Object } else { return } } if ($Output) { if ($Prefix) { $Object += @{ Status = $true; Output = $SessionName; Extended = "Connection established $($Session.ComputerName) - prefix: $Prefix" } } else { $Object += @{ Status = $true; Output = $SessionName; Extended = "Connection established $($Session.ComputerName) - prefix: n/a" } } return $Object } return $Object } function Connect-WinService { [CmdletBinding()] param ( [Object] $Credentials, [Object] $Service, [string] $Type, [switch] $Output ) $Object = @() if ($Service.Use) { switch ($Type) { 'ActiveDirectory' { # Prepare Data AD $CheckAvailabilityCommandsAD = Test-AvailabilityCommands -Commands 'Get-ADForest', 'Get-ADDomain', 'Get-ADRootDSE', 'Get-ADGroup', 'Get-ADUser', 'Get-ADComputer' if ($CheckAvailabilityCommandsAD -contains $false) { if ($Output) { $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'Commands unavailable.' } return $Object } else { Write-Warning "Active Directory documentation can't be started as commands are unavailable. Check if you have Active Directory module available (part of RSAT) and try again." return } } else { #if ($Output) { # $Object += @{ Status = $true; Output = 'ActiveDirectory'; Extended = 'Commands available.' } #} } if (-not (Test-ForestConnectivity)) { if ($Output) { $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'No connectivity to forest/domain.' } return $Object } else { Write-Warning 'Active Directory - No connectivity to forest/domain.' return } } else { #if ($Output) { #$Object += @{ Status = $true; Output = 'ActiveDirectory'; Extended = 'Connectivity to forest/domain available.' } #} } if ($Output) { $Object += @{ Status = $true; Output = $Service.SessionName; Extended = 'Connection Established.' } return $Object } } 'Azure' { # Check Credentials $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials if ($CheckCredentials.Status -contains $false) { if ($Output) { $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' } return $Object } else { return } } $OutputCommand = Connect-WinAzure -SessionName $Service.SessionName ` -Username $Credentials.Username ` -Password $Credentials.Password ` -AsSecure:$Credentials.PasswordAsSecure ` -FromFile:$Credentials.PasswordFromFile ` -Output return $OutputCommand } 'AzureAD' { # Check Credentials $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials if ($CheckCredentials.Status -contains $false) { if ($Output) { $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' } return $Object } else { return } } $OutputCommand = Connect-WinAzureAD -SessionName $Service.SessionName ` -Username $Credentials.Username ` -Password $Credentials.Password ` -AsSecure:$Credentials.PasswordAsSecure ` -FromFile:$Credentials.PasswordFromFile ` -Output return $OutputCommand } 'Exchange' { $CheckCredentials = Test-ConfigurationCredentials -Configuration $Document.DocumentExchange.Configuration -AllowEmptyKeys 'Username', 'Password' if ($CheckCredentials.Status -contains $false) { if ($Output) { $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' } return $Object } else { return } } $OutputCommand = Connect-WinExchange -SessionName $Service.SessionName ` -ConnectionURI $Service.ConnectionURI ` -Authentication $Service.Authentication ` -Username $Credentials.Username ` -Password $Credentials.Password ` -AsSecure:$Credentials.PasswordAsSecure ` -FromFile:$Credentials.PasswordFromFile ` -Prefix $Service.Prefix ` -Output return $OutputCommand } 'ExchangeOnline' { $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials if ($CheckCredentials.Status -contains $false) { if ($Output) { $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' } return $Object } else { return } } # Build Session $OutputCommand = Connect-WinExchange -SessionName $Service.SessionName ` -ConnectionURI $Service.ConnectionURI ` -Authentication $Service.Authentication ` -Username $Credentials.Username ` -Password $Credentials.Password ` -AsSecure:$Credentials.PasswordAsSecure ` -FromFile:$Credentials.PasswordFromFile ` -Prefix $Service.Prefix ` -Output return $OutputCommand } 'MicrosoftTeams' { # Check Credentials $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials if ($CheckCredentials.Status -contains $false) { if ($Output) { $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' } return $Object } else { return } } $OutputCommand = Connect-WinTeams -SessionName $Service.SessionName ` -Username $Credentials.Username ` -Password $Credentials.Password ` -AsSecure:$Credentials.PasswordAsSecure ` -FromFile:$Credentials.PasswordFromFile ` -Output return $OutputCommand } } } } function Connect-WinTeams { [CmdletBinding()] param( [string] $SessionName = 'Microsoft Teams', [string] $Username, [string] $Password, [switch] $AsSecure, [switch] $FromFile, [switch] $Output ) $Credentials = Request-Credentials -UserName $Username ` -Password $Password ` -AsSecure:$AsSecure ` -FromFile:$FromFile ` -Service $SessionName ` -Output if ($Credentials -isnot [PSCredential]) { if ($Output) { return $Credentials } else { return } } try { $Session = Connect-MicrosoftTeams -Credential $Credentials -ErrorAction Stop } catch { $Session = $null $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " if ($Output) { return @{ Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" } } else { Write-Warning "Connect-WinTeams - Failed with error message: $ErrorMessage" return } } if (-not $Session) { if ($Output) { return @{ Status = $false; Output = $SessionName; Extended = 'Connection Failed.' } } else { return } } if ($Output) { return @{ Status = $true; Output = $SessionName; Extended = 'Connection Established.' } } } function Convert-BinaryToHex { param( [alias('Bin')] [Parameter(Position = 0, Mandatory = $false, ValueFromPipeline = $true)] [Byte[]]$Binary ) if ($null -eq $Binary) { return } # assume pipeline input if we don't have an array (surely there must be a better way) if ($Binary.Length -eq 1) { $Binary = @($input) } $Return = -join ($Binary | foreach { "{0:X2}" -f $_ }) Write-Output $Return } function Convert-BinaryToString { param( [alias('Bin')] [Parameter(Position = 0, Mandatory = $false, ValueFromPipeline = $true)] [Byte[]]$Binary ) if ($null -ne $Binary) { return [System.Text.Encoding]::Unicode.GetString($Binary) } } <# $Emails = @() $Emails += 'SIP:test@email.com' $Emails += 'SMTP:elo@maiu.com' $Emails += 'SIP:elo@maiu.com' Convert-ExchangeEmail -Emails $Emails -RemovePrefix -RemoveDuplicates -AddSeparator #> function Convert-ExchangeEmail { [cmdletbinding()] param( [string[]] $Emails, [string] $Separator = ', ', [switch] $RemoveDuplicates, [switch] $RemovePrefix, [switch] $AddSeparator ) if ($RemovePrefix) { $Emails = $Emails.Replace('SMTP:', '').Replace('SIP:', '') } if ($RemoveDuplicates) { $Emails = $Emails | Sort-Object -Unique } if ($AddSeparator) { $Emails = $Emails -join $Separator } return $Emails } function Convert-ExchangeItems { [cmdletbinding()] param( $Count, [string] $Default = 'N/A' ) if ($null -eq $Count) { return $Default } else { return $Count } } <# Convert-ExchangeSize -To MB -Size '49 GB (52,613,349,376 bytes)' Convert-ExchangeSize -To GB -Size '49 GB (52,613,349,376 bytes)' #> function Convert-ExchangeSize { [cmdletbinding()] param( [validateset("Bytes", "KB", "MB", "GB", "TB")][string]$To = 'MB', [string]$Size, [int]$Precision = 4, [switch]$Display, [string]$Default = 'N/A' ) if ([string]::IsNullOrWhiteSpace($Size)) { return $Default } $Pattern = [Regex]::new('(?<=\()([0-9]*[,.].*[0-9])') # (?<=\()([0-9]*.*[0-9]) works too $Value = ($Size | Select-String $Pattern -AllMatches).Matches.Value Write-Verbose "Convert-ExchangeSize - Value Before: $Value" if ($null -ne $Value) { $Value = $Value.Replace(',', '').Replace('.', '') } switch ($To) { "Bytes" {return $value} "KB" {$Value = $Value / 1KB} "MB" {$Value = $Value / 1MB} "GB" {$Value = $Value / 1GB} "TB" {$Value = $Value / 1TB} } Write-Verbose "Convert-ExchangeSize - Value After: $Value" if ($Display) { return "$([Math]::Round($value,$Precision,[MidPointRounding]::AwayFromZero)) $To" } else { return [Math]::Round($value, $Precision, [MidPointRounding]::AwayFromZero) } } # # https://community.idera.com/database-tools/powershell/powertips/b/tips/posts/making-error-records-more-readable function ConvertFrom-ErrorRecord { param ( # we receive either a legit error record... [Management.Automation.ErrorRecord[]] [Parameter( Mandatory,ValueFromPipeline, ParameterSetName='ErrorRecord')] $ErrorRecord, # ...or a special stop exception which is raised by # cmdlets with -ErrorAction Stop [Management.Automation.ActionPreferenceStopException[]] [Parameter( Mandatory,ValueFromPipeline, ParameterSetName='StopException')] $Exception ) process { # if we received a stop exception in $Exception, # the error record is to be found inside of it # in all other cases, $ErrorRecord was received # directly if ($PSCmdlet.ParameterSetName -eq 'StopException') { $ErrorRecord = $Exception.ErrorRecord } # compose a new object out of the interesting properties # found in the error record object $ErrorRecord | ForEach-Object { [PSCustomObject]@{ Exception = $_.Exception.Message Reason = $_.CategoryInfo.Reason Target = $_.CategoryInfo.TargetName Script = $_.InvocationInfo.ScriptName Line = $_.InvocationInfo.ScriptLineNumber Column = $_.InvocationInfo.OffsetInLine } } } } Function ConvertFrom-OperationType { param ( [string] $OperationType ) $Known = @{ '%%14674' = 'Value Added' '%%14675' = 'Value Deleted' '%%14676' = 'Unknown' } foreach ($id in $OperationType) { if ($name = $Known[$id]) { return $name } } return $OperationType } function ConvertFrom-SID ($Sid) { $KnownSIDs = @{ 'S-1-0' = 'Null Authority' 'S-1-0-0' = 'Nobody' 'S-1-1' = 'World Authority' 'S-1-1-0' = 'Everyone' 'S-1-2' = 'Local Authority' 'S-1-2-0' = 'Local' 'S-1-2-1' = 'Console Logon' 'S-1-3' = 'Creator Authority' 'S-1-3-0' = 'Creator Owner' 'S-1-3-1' = 'Creator Group' 'S-1-3-2' = 'Creator Owner Server' 'S-1-3-3' = 'Creator Group Server' 'S-1-3-4' = 'Owner Rights' 'S-1-5-80-0' = 'All Services' 'S-1-4' = 'Non-unique Authority' 'S-1-5' = 'NT Authority' 'S-1-5-1' = 'Dialup' 'S-1-5-2' = 'Network' 'S-1-5-3' = 'Batch' 'S-1-5-4' = 'Interactive' 'S-1-5-6' = 'Service' 'S-1-5-7' = 'Anonymous' 'S-1-5-8' = 'Proxy' 'S-1-5-9' = 'Enterprise Domain Controllers' 'S-1-5-10' = 'Principal Self' 'S-1-5-11' = 'Authenticated Users' 'S-1-5-12' = 'Restricted Code' 'S-1-5-13' = 'Terminal Server Users' 'S-1-5-14' = 'Remote Interactive Logon' 'S-1-5-15' = 'This Organization' 'S-1-5-17' = 'This Organization' 'S-1-5-18' = 'Local System' 'S-1-5-19' = 'NT Authority' 'S-1-5-20' = 'NT Authority' 'S-1-5-32-544' = 'Administrators' 'S-1-5-32-545' = 'Users' 'S-1-5-32-546' = 'Guests' 'S-1-5-32-547' = 'Power Users' 'S-1-5-32-548' = 'Account Operators' 'S-1-5-32-549' = 'Server Operators' 'S-1-5-32-550' = 'Print Operators' 'S-1-5-32-551' = 'Backup Operators' 'S-1-5-32-552' = 'Replicators' 'S-1-5-64-10' = 'NTLM Authentication' 'S-1-5-64-14' = 'SChannel Authentication' 'S-1-5-64-21' = 'Digest Authority' 'S-1-5-80' = 'NT Service' 'S-1-5-83-0' = 'NT VIRTUAL MACHINE\Virtual Machines' 'S-1-16-0' = 'Untrusted Mandatory Level' 'S-1-16-4096' = 'Low Mandatory Level' 'S-1-16-8192' = 'Medium Mandatory Level' 'S-1-16-8448' = 'Medium Plus Mandatory Level' 'S-1-16-12288' = 'High Mandatory Level' 'S-1-16-16384' = 'System Mandatory Level' 'S-1-16-20480' = 'Protected Process Mandatory Level' 'S-1-16-28672' = 'Secure Process Mandatory Level' 'S-1-5-32-554' = 'BUILTIN\Pre-Windows 2000 Compatible Access' 'S-1-5-32-555' = 'BUILTIN\Remote Desktop Users' 'S-1-5-32-556' = 'BUILTIN\Network Configuration Operators' 'S-1-5-32-557' = 'BUILTIN\Incoming Forest Trust Builders' 'S-1-5-32-558' = 'BUILTIN\Performance Monitor Users' 'S-1-5-32-559' = 'BUILTIN\Performance Log Users' 'S-1-5-32-560' = 'BUILTIN\Windows Authorization Access Group' 'S-1-5-32-561' = 'BUILTIN\Terminal Server License Servers' 'S-1-5-32-562' = 'BUILTIN\Distributed COM Users' 'S-1-5-32-569' = 'BUILTIN\Cryptographic Operators' 'S-1-5-32-573' = 'BUILTIN\Event Log Readers' 'S-1-5-32-574' = 'BUILTIN\Certificate Service DCOM Access' 'S-1-5-32-575' = 'BUILTIN\RDS Remote Access Servers' 'S-1-5-32-576' = 'BUILTIN\RDS Endpoint Servers' 'S-1-5-32-577' = 'BUILTIN\RDS Management Servers' 'S-1-5-32-578' = 'BUILTIN\Hyper-V Administrators' 'S-1-5-32-579' = 'BUILTIN\Access Control Assistance Operators' 'S-1-5-32-580' = 'BUILTIN\Remote Management Users' } foreach ($id in $sid) { if ($name = $KnownSIDs[$id]) { } else { #Try to translate the SID to an account Try { $objSID = New-Object System.Security.Principal.SecurityIdentifier($id) $name = ( $objSID.Translate([System.Security.Principal.NTAccount]) ).Value } Catch { $name = $sid # returns sid if unable to name } } return @{ SID = $id Name = $name } } } function Convert-HexToBinary { param( [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)] [string] $Hex ) $return = @() for ($i = 0; $i -lt $Hex.Length ; $i += 2) { $return += [Byte]::Parse($Hex.Substring($i, 2), [System.Globalization.NumberStyles]::HexNumber) } Write-Output $return } function Convert-KeyToKeyValue { [CmdletBinding()] param ( [object] $Object ) $NewHash = [ordered] @{} foreach ($O in $Object.Keys) { $KeyName = "$O ($($Object.$O))" $KeyValue = $Object.$O $NewHash.$KeyName = $KeyValue } return $NewHash } function Convert-Size { # Original - https://techibee.com/powershell/convert-from-any-to-any-bytes-kb-mb-gb-tb-using-powershell/2376 # # Changelog - Modified 30.03.2018 - przemyslaw.klys at evotec.pl # - Added $Display Switch [cmdletbinding()] param( [validateset("Bytes", "KB", "MB", "GB", "TB")] [string]$From, [validateset("Bytes", "KB", "MB", "GB", "TB")] [string]$To, [Parameter(Mandatory = $true)] [double]$Value, [int]$Precision = 4, [switch]$Display ) switch ($From) { "Bytes" {$value = $Value } "KB" {$value = $Value * 1024 } "MB" {$value = $Value * 1024 * 1024} "GB" {$value = $Value * 1024 * 1024 * 1024} "TB" {$value = $Value * 1024 * 1024 * 1024 * 1024} } switch ($To) { "Bytes" {return $value} "KB" {$Value = $Value / 1KB} "MB" {$Value = $Value / 1MB} "GB" {$Value = $Value / 1GB} "TB" {$Value = $Value / 1TB} } if ($Display) { return "$([Math]::Round($value,$Precision,[MidPointRounding]::AwayFromZero)) $To" } else { return [Math]::Round($value, $Precision, [MidPointRounding]::AwayFromZero) } } function Convert-TimeToDays { [CmdletBinding()] param ( $StartTime, $EndTime, #[nullable[DateTime]] $StartTime, #[nullable[DateTime]] $EndTime, [string] $Ignore = '*1601*' ) <# Due to some use of string as StartTime in some code temporary using this version. Get-ObjectType -Object $StartTime -VerboseOnly -Verbose Get-ObjectType -Object $EndTime -VerboseOnly -Verbose #> if ($StartTime -and $EndTime) { try { if ($StartTime -notlike $Ignore -and $EndTime -notlike $Ignore) { $Days = (NEW-TIMESPAN -Start (GET-DATE) -End ($EndTime)).Days } else { $Days = $null } } catch { $Days = $null } } return $Days } function Convert-ToDateTime { [CmdletBinding()] param ( [string] $Timestring, [string] $Ignore = '*1601*' ) Try { $DateTime = ([datetime]::FromFileTime($Timestring)) } catch { $DateTime = $null } #Write-Verbose "Convert-ToDateTime: $DateTime" if ($null -eq $DateTime -or $DateTime -like $Ignore) { return $null } else { return $DateTime } } function ConvertTo-ImmutableID { [CmdletBinding()] param( [Parameter(Mandatory = $false, ParameterSetName = 'User')] [alias('ADuser')] [Microsoft.ActiveDirectory.Management.ADAccount] $User, [Parameter(Mandatory = $false, ParameterSetName = 'Guid')] [alias('GUID')] [string] $ObjectGUID ) if ($User) { if ($User.ObjectGUID) { $ObjectGUID = $User.ObjectGuid } } if ($ObjectGUID) { $ImmutableID = [System.Convert]::ToBase64String(($User.ObjectGUID).ToByteArray()) return $ImmutableID } return } function Convert-ToTimeSpan { [CmdletBinding()] param ( [DateTime] $StartTime = (Get-Date), [DateTime] $EndTime ) if ($StartTime -and $EndTime) { try { $TimeSpan = (NEW-TIMESPAN -Start $StartTime -End $EndTime) } catch { $TimeSpan = $null } } if ($null -ne $TimeSpan) { return $TimeSpan } else { return $null } } ## This methods converts 2 Arrays into 1 Array ## Administrators + 0 = Administrators (0) function Convert-TwoArraysIntoOne { [CmdletBinding()] param ( $Object, $ObjectToAdd ) $Value = @() for ($i = 0; $i -lt $Object.Count; $i++) { $Value += "$($Object[$i]) ($($ObjectToAdd[$i]))" } return $Value } Function Convert-UAC { <# .SYNOPSIS Converts values from Events into proper format .DESCRIPTION Converts values from Events into proper format .PARAMETER UAC Parameter description .EXAMPLE Convert-UAC -UAC '%%1793' Convert-UAC -UAC '1793' Output: TEMP_DUPLICATE_ACCOUNT, NORMAL_ACCOUNT, RESERVED Convert-UAC -UAC '1793', '1794' Convert-UAC -UAC '121793' Output: PASSWD_CANT_CHANGE, ENCRYPTED_TEXT_PWD_ALLOWED, TEMP_DUPLICATE_ACCOUNT, NORMAL_ACCOUNT, INTERDOMAIN_TRUST_ACCOUNT, WORKSTATION_TRUST_ACCOUNT, RESERVED, RESERVED, DONT_EXPIRE_PASSWORD Convert-UAC -UAC 'C:\Onet33' Output: Same input as output Convert-UAC -UAC '121793' -OutputPerLine Output: One entry per line PASSWD_CANT_CHANGE ENCRYPTED_TEXT_PWD_ALLOWED TEMP_DUPLICATE_ACCOUNT NORMAL_ACCOUNT INTERDOMAIN_TRUST_ACCOUNT WORKSTATION_TRUST_ACCOUNT RESERVED RESERVED DONT_EXPIRE_PASSWORD .NOTES General notes #> [CmdletBinding()] param( [string[]] $UAC, [string] $Separator ) $Output = foreach ($String in $UAC) { $NumberAsString = $String.Replace('%', '') -as [int] if ($null -eq $NumberAsString) { return $UAC } $PropertyFlags = @( "SCRIPT", "ACCOUNTDISABLE", "RESERVED", "HOMEDIR_REQUIRED", "LOCKOUT", "PASSWD_NOTREQD", "PASSWD_CANT_CHANGE", "ENCRYPTED_TEXT_PWD_ALLOWED", "TEMP_DUPLICATE_ACCOUNT", "NORMAL_ACCOUNT", "RESERVED", "INTERDOMAIN_TRUST_ACCOUNT", "WORKSTATION_TRUST_ACCOUNT", "SERVER_TRUST_ACCOUNT", "RESERVED", "RESERVED", "DONT_EXPIRE_PASSWORD", "MNS_LOGON_ACCOUNT", "SMARTCARD_REQUIRED", "TRUSTED_FOR_DELEGATION", "NOT_DELEGATED", "USE_DES_KEY_ONLY", "DONT_REQ_PREAUTH", "PASSWORD_EXPIRED", "TRUSTED_TO_AUTH_FOR_DELEGATION", "RESERVED", "PARTIAL_SECRETS_ACCOUNT" "RESERVED" "RESERVED" "RESERVED" "RESERVED" "RESERVED" ) 1..($PropertyFlags.Length) | Where-Object { $NumberAsString -bAnd [math]::Pow(2, $_)} | ForEach-Object {$PropertyFlags[$_]} } if ($Separator -eq '') { $Output } else { $Output -join $Separator } } function Disconnect-WinAzure { [CmdletBinding()] param( [string] $SessionName = 'Azure MSOL', [switch] $Output, [switch] $Force ) $Object = @() if (-not $Force) { if ($Output) { $Object += @{ Status = $true; Output = $SessionName; Extended = "No way to do this. Kill PowerShell session manually." } return $Object } else { Write-Warning "Disconnect-WinAzure - There is no other way to disconnect from $Session then killing PowerShell session. Do this manually!" return } } else { Exit } } function Disconnect-WinAzureAD { [CmdletBinding()] param( [string] $SessionName = 'Azure AD', [switch] $Output ) $Object = @() try { Disconnect-AzureAD -ErrorAction Stop } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " if ($ErrorMessage -like "*Object reference not set to an instance of an object.*") { $Object += @{ Status = $false; Output = $SessionName; Extended = "Disconnection failed. No connection exists." } } else { $Object += @{ Status = $false; Output = $SessionName; Extended = "Disconnection failed. Error: $ErrorMessage" } } if ($Output) { return $Object } else { Write-Warning "Disconnect-WinAzureAD - Failed with error message: $ErrorMessage" return } } if ($Output) { $Object += @{ Status = $true; Output = $SessionName; Extended = "Disconnection succeeded." } return $Object } } function Disconnect-WinExchange { [CmdletBinding()] param( [string] $SessionName = "Exchange", [switch] $Output ) $Object = @() $ExistingSession = Get-PSSession -Name $SessionName -ErrorAction SilentlyContinue if ($ExistingSession) { try { Remove-PSSession -Name $SessionName -ErrorAction Stop } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " if ($Output) { $Object += @{ Status = $false; Output = $SessionName; Extended = "Disconnection failed. Error: $ErrorMessage" } return $Object } else { Write-Warning "Disconnect-WinExchange - Failed with error message: $ErrorMessage" return } } if ($Output) { $Object += @{ Status = $true; Output = $SessionName; Extended = "Disconnection succeeded." } return $Object } } else { if ($Output) { $Object += @{ Status = $false; Output = $SessionName; Extended = "Disconnection failed. No connection exists." } return $Object } } } function Disconnect-WinTeams { [CmdletBinding()] param( [string] $SessionName = 'Microsoft Teams', [switch] $Output ) $Object = @() try { Disconnect-MicrosoftTeams -ErrorAction Stop } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " if ($ErrorMessage -like "*Object reference not set to an instance of an object.*") { $Object += @{ Status = $false; Output = $SessionName; Extended = "Disconnection failed. No connection exists." } } else { $Object += @{ Status = $false; Output = $SessionName; Extended = "Disconnection failed. Error: $ErrorMessage" } } if ($Output) { return $Object } else { Write-Warning "Disconnect-MicrosoftTeams - Failed with error message: $ErrorMessage" return } } if ($Output) { $Object += @{ Status = $true; Output = $SessionName; Extended = "Disconnection succeeded." } return $Object } } function Find-DatesCurrentDayMinusDayX ($days) { $DateTodayStart = (Get-Date -Hour 0 -Minute 0 -Second 0 -Millisecond 0).AddDays( - $Days) $DateTodayEnd = (Get-Date -Hour 0 -Minute 0 -Second 0 -Millisecond 0).AddDays(1).AddDays( - $Days).AddMilliseconds(-1) $DateParameters = @{ DateFrom = $DateTodayStart DateTo = $DateTodayEnd } return $DateParameters } function Find-DatesCurrentDayMinuxDaysX ($days) { $DateTodayStart = (Get-Date -Hour 0 -Minute 0 -Second 0 -Millisecond 0).AddDays( - $Days) $DateTodayEnd = (Get-Date -Hour 0 -Minute 0 -Second 0 -Millisecond 0).AddDays(1).AddMilliseconds(-1) $DateParameters = @{ DateFrom = $DateTodayStart DateTo = $DateTodayEnd } return $DateParameters } function Find-DatesCurrentHour () { $DateTodayStart = (Get-Date -Minute 0 -Second 0 -Millisecond 0) $DateTodayEnd = $DateTodayStart.AddHours(1) $DateParameters = @{ DateFrom = $DateTodayStart DateTo = $DateTodayEnd } return $DateParameters } function Find-DatesDayPrevious () { $DateToday = (GET-DATE).Date $DateYesterday = $DateToday.AddDays(-1) $DateParameters = @{ DateFrom = $DateYesterday DateTo = $dateToday } return $DateParameters } function Find-DatesDayToday () { $DateToday = (GET-DATE).Date $DateTodayEnd = $DateToday.AddDays(1).AddSeconds(-1) $DateParameters = @{ DateFrom = $DateToday DateTo = $DateTodayEnd } return $DateParameters } function Find-DatesMonthCurrent () { $DateMonthFirstDay = (GET-DATE -Day 1).Date $DateMonthLastDay = GET-DATE $DateMonthFirstDay.AddMonths(1).AddSeconds(-1) $DateParameters = @{ DateFrom = $DateMonthFirstDay DateTo = $DateMonthLastDay } return $DateParameters } function Find-DatesMonthPast ([bool] $Force) { $DateToday = (Get-Date).Date $DateMonthFirstDay = (GET-DATE -Day 1).Date $DateMonthPreviousFirstDay = $DateMonthFirstDay.AddMonths(-1) if ($Force -eq $true -or $DateToday -eq $DateMonthFirstDay) { $DateParameters = @{ DateFrom = $DateMonthPreviousFirstDay DateTo = $DateMonthFirstDay } return $DateParameters } else { return $null } } function Find-DatesPastHour () { $DateTodayEnd = Get-Date -Minute 0 -Second 0 -Millisecond 0 $DateTodayStart = $DateTodayEnd.AddHours(-1) $DateParameters = @{ DateFrom = $DateTodayStart DateTo = $DateTodayEnd } return $DateParameters } function Find-DatesPastWeek($DayName) { $DateTodayStart = Get-Date -Hour 0 -Minute 0 -Second 0 -Millisecond 0 if ($DateTodayStart.DayOfWeek -ne $DayName) { return $null } $DateTodayEnd = (Get-Date -Hour 0 -Minute 0 -Second 0 -Millisecond 0).AddDays(-7) $DateParameters = @{ DateFrom = $DateTodayEnd DateTo = $DateTodayStart } return $DateParameters } function Find-DatesQuarterCurrent ([bool] $Force) { $Today = (Get-Date) $Quarter = [Math]::Ceiling($Today.Month / 3) $LastDay = [DateTime]::DaysInMonth([Int]$Today.Year.ToString(), [Int]($Quarter * 3)) $StartDate = (get-date -Year $Today.Year -Month ($Quarter * 3 - 2) -Day 1).Date $EndDate = (get-date -Year $Today.Year -Month ($Quarter * 3) -Day $LastDay).Date.AddDays(1).AddTicks(-1) $DateParameters = @{ DateFrom = $StartDate DateTo = $EndDate } return $DateParameters } function Find-DatesQuarterLast ([bool] $Force) { #https://blogs.technet.microsoft.com/dsheehan/2017/09/21/use-powershell-to-determine-the-first-day-of-the-current-calendar-quarter/ $Today = (Get-Date).AddDays(-90) $Yesterday = ((Get-Date).AddDays(-1)) $Quarter = [Math]::Ceiling($Today.Month / 3) $LastDay = [DateTime]::DaysInMonth([Int]$Today.Year.ToString(), [Int]($Quarter * 3)) $StartDate = (get-date -Year $Today.Year -Month ($Quarter * 3 - 2) -Day 1).Date $EndDate = (get-date -Year $Today.Year -Month ($Quarter * 3) -Day $LastDay).Date.AddDays(1).AddTicks(-1) if ($Force -eq $true -or $Yesterday.Date -eq $EndDate.Date) { $DateParameters = @{ DateFrom = $StartDate DateTo = $EndDate } return $DateParameters } else { return $null } } function Find-MyProgramData { [CmdletBinding()] param ( $Data, $FindText ) foreach ($Sub in $Data) { if ($Sub -like $FindText) { $Split = $Sub.Split(' ') return $Split[1] } } return '' } function Find-TypesNeeded { [CmdletBinding()] param ( $TypesRequired, $TypesNeeded ) $AllTypes = @() foreach ($Type in $TypesNeeded) { if ($TypesRequired -contains $Type) { $AllTypes += $True } else { $AllTypes += $False } } if ($AllTypes -contains $True) { return $True } else { return $False } } function Format-AddSpaceToSentence { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER Text Parameter description .EXAMPLE $test = @( 'OnceUponATime', 'OnceUponATime1', 'Money@Risk', 'OnceUponATime123', 'AHappyMan2014' 'OnceUponATime_123' ) Format-AddSpaceToSentence -Text $Test $Test | Format-AddSpaceToSentence -ToLowerCase .NOTES General notes #> [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)][string[]] $Text, [switch] $ToLowerCase ) Begin {} Process { $Value = foreach ($T in $Text) { ($T -creplace '([A-Z\W_]|\d+)(?<![a-z])', ' $&').trim() } if ($ToLowerCase) { $Value.ToLower() } else { $Value } } End { } } function Format-FirstXChars { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER Text Parameter description .PARAMETER NumberChars Parameter description .EXAMPLE Format-FirstChars -Text "VERBOSE: Loading module from path 'C:\Users\pklys\.vscode\extensions\ms-vs" -NumberChars 15 .NOTES General notes #> param( [string] $Text, [int] $NumberChars ) return ($Text.ToCharArray() | Select-Object -First $NumberChars) -join '' } function Format-PSTable { [CmdletBinding()] param ( [parameter(ValueFromPipelineByPropertyName, ValueFromPipeline)] $Object, [switch] $SkipTitle, [string[]] $Property, [string[]] $ExcludeProperty, [switch] $NoAliasOrScriptProperties, [switch] $DisplayPropertySet, [Object] $OverwriteHeaders, [switch] $PreScanHeaders, [ref] $StringLenghts ) $Type = Get-ObjectType -Object $Object -Verbose:$false if ($Type.ObjectTypeName -eq 'Object[]' -or $Type.ObjectTypeName -eq 'Object' -or $Type.ObjectTypeName -eq 'PSCustomObject' -or $Type.ObjectTypeName -eq 'Collection`1') { if ($Type.ObjectTypeInsiderName -match 'string|bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ulong|ushort') { return $Object #return Format-PSTableConvertType1 -Object $Object -SkipTitle:$SkipTitle -ExcludeProperty $ExcludeProperty -NoAliasOrScriptProperties:$NoAliasOrScriptProperties -DisplayPropertySet:$DisplayPropertySet -OverwriteHeaders $OverwriteHeaders } elseif ($Type.ObjectTypeInsiderName -eq 'Object' -or $Type.ObjectTypeInsiderName -eq 'PSCustomObject') { # Write-Verbose 'Level 1-1' return Format-PSTableConvertType2 -Object $Object ` -SkipTitle:$SkipTitle ` -ExcludeProperty $ExcludeProperty ` -NoAliasOrScriptProperties:$NoAliasOrScriptProperties ` -DisplayPropertySet:$DisplayPropertySet ` -OverwriteHeaders $OverwriteHeaders ` -PreScanHeaders:$PreScanHeaders ` -Property $Property } elseif ($Type.ObjectTypeInsiderName -eq 'HashTable' -or $Type.ObjectTypeInsiderName -eq 'OrderedDictionary' ) { return Format-PSTableConvertType3 -Object $Object ` -SkipTitle:$SkipTitle ` -ExcludeProperty $ExcludeProperty ` -NoAliasOrScriptProperties:$NoAliasOrScriptProperties ` -DisplayPropertySet:$DisplayPropertySet ` -OverwriteHeaders $OverwriteHeaders ` -Property $Property } else { # Covers ADDriveInfo and other types of objects return Format-PSTableConvertType2 -Object $Object ` -SkipTitle:$SkipTitle ` -ExcludeProperty $ExcludeProperty ` -NoAliasOrScriptProperties:$NoAliasOrScriptProperties ` -DisplayPropertySet:$DisplayPropertySet ` -OverwriteHeaders $OverwriteHeaders ` -PreScanHeaders:$PreScanHeaders ` -Property $Property } } elseif ($Type.ObjectTypeName -eq 'HashTable' -or $Type.ObjectTypeName -eq 'OrderedDictionary' ) { return Format-PSTableConvertType3 -Object $Object ` -SkipTitle:$SkipTitle ` -ExcludeProperty $ExcludeProperty ` -NoAliasOrScriptProperties:$NoAliasOrScriptProperties ` -DisplayPropertySet:$DisplayPropertySet ` -OverwriteHeaders $OverwriteHeaders ` -Property $Property } elseif ($Type.ObjectTypeName -match 'bool|byte|char|datetime|decimal|double|ExcelHyperLink|float|int|long|sbyte|short|string|timespan|uint|ulong|URI|ushort') { return $Object } else { # Covers ADDriveInfo and other types of objects return Format-PSTableConvertType2 -Object $Object ` -SkipTitle:$SkipTitle ` -ExcludeProperty $ExcludeProperty ` -NoAliasOrScriptProperties:$NoAliasOrScriptProperties ` -DisplayPropertySet:$DisplayPropertySet ` -OverwriteHeaders $OverwriteHeaders ` -PreScanHeaders:$PreScanHeaders ` -Property $Property } throw 'Not supported? Weird' } function Format-PSTableConvertType1 { [CmdletBinding()] param ( [Object] $Object, [switch] $SkipTitles, [string[]] $Property, [string[]] $ExcludeProperty, [switch] $NoAliasOrScriptProperties, [switch] $DisplayPropertySet, [Object] $OverwriteHeaders # not used - requires fix ) #Write-Verbose 'Format-PSTableConvertType1 - Option 1' $Array = New-ArrayList ### Add Titles if (-not $SkipTitles) { $Titles = New-ArrayList Add-ToArray -List $Titles -Element 'Name' Add-ToArray -List $Titles -Element 'Value' Add-ToArray -List $Array -Element $Titles } ### Add Data foreach ($Name in $Object.Keys) { Write-Verbose "$Name" Write-Verbose "$Object.$Name" $ArrayValues = New-ArrayList if ($Property) { if ($Property -contains $Name) { Add-ToArray -List $ArrayValues -Element $Name Add-ToArray -List $ArrayValues -Element $Object.$Name Add-ToArray -List $Array -Element $ArrayValues } } else { if (-not $ExcludeProperty -notcontains $Name) { Add-ToArray -List $ArrayValues -Element $Name Add-ToArray -List $ArrayValues -Element $Object.$Name Add-ToArray -List $Array -Element $ArrayValues } } } return , $Array } function Format-PSTableConvertType2 { [CmdletBinding()] param( [Object] $Object, [switch] $SkipTitles, [string[]] $Property, [string[]] $ExcludeProperty, [switch] $NoAliasOrScriptProperties, [switch] $DisplayPropertySet, [Object] $OverwriteHeaders, [switch] $PreScanHeaders ) if ($Property) { $Object = $Object | Select-Object $Property } $Array = New-ArrayList $Titles = New-ArrayList if ($NoAliasOrScriptProperties) {$PropertyType = 'AliasProperty', 'ScriptProperty' } else {$PropertyType = ''} #Write-Verbose "Format-PSTableConvertType2 - Option 2 - NoAliasOrScriptProperties: $NoAliasOrScriptProperties" # Get Titles first (to make sure order is correct for all rows) if ($PreScanHeaders) { $ObjectProperties = Get-ObjectProperties -Object $Object foreach ($Name in $ObjectProperties) { Add-ToArray -List $Titles -Element $Name } } else { if ($OverwriteHeaders) { $Titles = $OverwriteHeaders } else { foreach ($O in $Object) { if ($DisplayPropertySet -and $O.psStandardmembers.DefaultDisplayPropertySet.ReferencedPropertyNames) { $ObjectProperties = $O.psStandardmembers.DefaultDisplayPropertySet.ReferencedPropertyNames.Where( { $ExcludeProperty -notcontains $_ } ) #.Name } else { $ObjectProperties = $O.PSObject.Properties.Where( { $PropertyType -notcontains $_.MemberType -and $ExcludeProperty -notcontains $_.Name } ).Name } foreach ($Name in $ObjectProperties) { Add-ToArray -List $Titles -Element $Name } break } # Add Titles to Array (if not -SkipTitles) } } if (-not $SkipTitle) { Add-ToArray -List $Array -Element $Titles } # Extract data (based on Title) foreach ($O in $Object) { $ArrayValues = New-ArrayList foreach ($Name in $Titles) { Add-ToArray -List $ArrayValues -Element $O.$Name } Add-ToArray -List $Array -Element $ArrayValues } return , $Array } function Format-PSTableConvertType3 { [CmdletBinding()] param ( [Object] $Object, [switch] $SkipTitles, [string[]] $Property, [string[]] $ExcludeProperty, [switch] $NoAliasOrScriptProperties, [switch] $DisplayPropertySet, [Object] $OverwriteHeaders ) #Write-Verbose 'Format-PSTableConvertType3 - Option 3' $Array = New-ArrayList ### Add Titles if (-not $SkipTitles) { $Titles = New-ArrayList Add-ToArray -List $Titles -Element 'Name' Add-ToArray -List $Titles -Element 'Value' Add-ToArray -List $Array -Element $Titles } ### Add Data foreach ($O in $Object) { foreach ($Name in $O.Keys) { # Write-Verbose "Test2 - $Key - $($O[$Key])" $ArrayValues = New-ArrayList if ($Property) { if ($Property -contains $Name) { Add-ToArray -List $ArrayValues -Element $Name Add-ToArray -List $ArrayValues -Element $Object.$Name Add-ToArray -List $Array -Element $ArrayValues } } else { if ($ExcludeProperty -notcontains $Name) { Add-ToArray -List $ArrayValues -Element $Name Add-ToArray -List $ArrayValues -Element $O[$Name] Add-ToArray -List $Array -Element $ArrayValues } } } } return , $Array } function Format-Stream { [alias('fs','Format-TableStream','Format-ListStream')] ##[alias('ftv','ftd','fto','fth','fti','flv','fld','flo','flh','fli','Format-TableVerbose', 'Format-TableDebug', 'Format-TableInformation', 'Format-TableWarning')] [CmdletBinding(DefaultParameterSetName = 'All')] param( [Parameter(Mandatory = $false,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true,Position = 1)] [object] $InputObject, [Parameter(Mandatory = $false,ValueFromPipeline = $false,Position = 0,ParameterSetName = 'Property')] [Object[]] $Property, [Parameter(Mandatory = $false,ValueFromPipeline = $false,Position = 2,ParameterSetName = 'ExcludeProperty')] [Object[]] $ExcludeProperty, [Parameter(Mandatory = $false,ValueFromPipeline = $false,Position = 3)] [switch] $HideTableHeaders, [Parameter(Mandatory = $false,ValueFromPipeline = $false,Position = 4)] [int] $ColumnHeaderSize, [Parameter(Mandatory = $false,ValueFromPipeline = $false,Position = 5)] [switch] $AlignRight, [Parameter(Mandatory = $false,ValueFromPipeline = $false,Position = 6)] [validateset('Output', 'Host', 'Warning', 'Verbose', 'Debug', 'Information')] [string] $Stream = 'Verbose', [Parameter(Mandatory = $false,ValueFromPipeline = $false,Position = 7)] [alias('AsList')][switch] $List, [Parameter(Mandatory = $false,ValueFromPipeline = $false,Position = 8)] [alias('Rotate', 'RotateData', 'TransposeColumnsRows', 'TransposeData')] [switch] $Transpose, [Parameter(Mandatory = $false,ValueFromPipeline = $false,Position = 9)] [ValidateSet("ASC", "DESC", "NONE")] [string] $TransposeSort = 'NONE', [alias('Color')] [System.ConsoleColor[]] $ForegroundColor, [alias('ColorRow')] [int[]] $ForegroundColorRow ) Begin { $IsVerbosePresent = $PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent if ($Stream -eq 'Output') { # } elseif ($Stream -eq 'Host') { # } elseif ($Stream -eq 'Warning') { [System.Management.Automation.ActionPreference] $WarningCurrent = $WarningPreference $WarningPreference = 'continue' } elseif ($Stream -eq 'Verbose') { [System.Management.Automation.ActionPreference] $VerboseCurrent = $VerbosePreference $VerbosePreference = 'continue' } elseif ($Stream -eq 'Debug') { [System.Management.Automation.ActionPreference] $DebugCurrent = $DebugPreference $DebugPreference = 'continue' } elseif ($Stream -eq 'Information') { [System.Management.Automation.ActionPreference] $InformationCurrent = $InformationPreference $InformationPreference = 'continue' } [bool] $FirstRun = $True # First run for pipeline [bool] $FirstLoop = $True # First loop for data [bool] $FirstList = $True # First loop for a list [int] $ScreenWidth = $Host.UI.RawUI.WindowSize.Width - 12 # Removes 12 chars because of VERBOSE: output $ArrayList = @() } Process { if ((Get-ObjectCount -Object $InputObject) -eq 0) { break } if ($FirstRun) { $FirstRun = $false if ($Transpose) { $InputObject = Format-TransposeTable -Object $InputObject -Sort $TransposeSort } $Data = Format-PSTable -Object $InputObject -Property $Property -ExcludeProperty $ExcludeProperty -NoAliasOrScriptProperties:$NoAliasOrScriptProperties -DisplayPropertySet:$DisplayPropertySet -PreScanHeaders:$PreScanHeaders $Headers = $Data[0] if ($HideTableHeaders) { $Data.RemoveAt(0); } $ArrayList += $Data } else { if ($Transpose) { $InputObject = Format-TransposeTable -Object $InputObject -Sort $TransposeSort } $Data = Format-PSTable -Object $InputObject -Property $Property -ExcludeProperty $ExcludeProperty -NoAliasOrScriptProperties:$NoAliasOrScriptProperties -DisplayPropertySet:$DisplayPropertySet -PreScanHeaders:$PreScanHeaders -OverwriteHeaders $Headers -SkipTitle $ArrayList += $Data } } End { if (-not $ColumnHeaderSize) { $ColumnLength = [int[]]::new($Headers.Count); foreach ($Row in $ArrayList) { $i = 0 foreach ($Column in $Row) { $Length = "$Column".Length if ($Length -gt $ColumnLength[$i]) { $ColumnLength[$i] = $Length } $i++ } } if ($IsVerbosePresent) { Write-Verbose "Format-TableVerbose - ScreenWidth $ScreenWidth" Write-Verbose "Format-TableVerbose - Column Lengths $($ColumnLength -join ',')" } } # Add empty line if ($Stream -eq 'Output') { Write-Output -InputObject '' } elseif ($Stream -eq 'Host') { Write-Host -Object '' } elseif ($Stream -eq 'Warning') { Write-Warning -Message '' } elseif ($Stream -eq 'Verbose') { Write-Verbose -Message '' } elseif ($Stream -eq 'Debug') { Write-Debug -Message '' } elseif ($Stream -eq 'Information') { Write-Information -MessageData '' } if ($List) { [int] $RowCount = 1 foreach ($Row in $ArrayList ) { [string] $Output = '' [int] $ColumnNumber = 0 [int] $CurrentColumnLength = 0 if ($ColumnHeaderSize) { $PadLength = $ColumnHeaderSize # Add +1 to make sure there's space between columns } else { $PadLength = (($Headers.Length | Measure-Object -Maximum).Maximum) + 1 # Add +1 to make sure there's space between columns } # Prepare each data for row if (-not $FirstList) { $i = 0 foreach ($ColumnValue in $Row) { if (-not $HideTableHeaders) { # Using Headers for a List if ($AlignRight) { $Head = $($Headers[$i]).PadLeft($PadLength) } else { $Head = $($Headers[$i]).PadRight($PadLength) } $Output = "$Head`: $ColumnValue" } else { # Hide table headers for a List switch $Output = "$ColumnValue" } if ($Stream -eq 'Output') { Write-Output -InputObject $Output } elseif ($Stream -eq 'Host') { Write-Host -Object $Output } elseif ($Stream -eq 'Warning') { Write-Warning -Message $Output } elseif ($Stream -eq 'Verbose') { Write-Verbose -Message $Output } elseif ($Stream -eq 'Debug') { Write-Debug -Message $Output } elseif ($Stream -eq 'Information') { Write-Information -MessageData $Output } $i++ } $RowCount++ if ($RowCount -ne $ArrayList.Count) { # Add empty line per each object but only if it's not last object if ($Stream -eq 'Output') { Write-Output -InputObject '' } elseif ($Stream -eq 'Host') { Write-Host -Object '' } elseif ($Stream -eq 'Warning') { Write-Warning -Message '' } elseif ($Stream -eq 'Verbose') { Write-Verbose -Message '' } elseif ($Stream -eq 'Debug') { Write-Debug -Message '' } elseif ($Stream -eq 'Information') { Write-Information -MessageData '' } } } $FirstList = $false } } else { # Process Data [int] $RowCountColors = 1 foreach ($Row in $ArrayList ) { [string] $Output = '' [int] $ColumnNumber = 0 [int] $CurrentColumnLength = 0 # Prepare each data for row foreach ($ColumnValue in $Row) { # Set Column Header Size to static value or based on string length if ($ColumnHeaderSize) { $PadLength = $ColumnHeaderSize # Add +1 to make sure there's space between columns } else { $PadLength = $ColumnLength[$ColumnNumber] + 1 # Add +1 to make sure there's space between columns } # Makes sure to display all data on current screen size, the larger the screen, the more it fits $CurrentColumnLength += $PadLength if ($CurrentColumnLength -ge $ScreenWidth) { break } # Prepare Data if ($ColumnHeaderSize) { # if ColumnHeaderSize is defined we need to trim text and make sure there is space between for the ones being trimmed $ColumnValue = ("$ColumnValue".ToCharArray() | Select-Object -First ($PadLength - 1)) -join "" } else { $ColumnValue = ("$ColumnValue".ToCharArray() | Select-Object -First ($PadLength)) -join "" } if ($Output -eq '') { if ($AlignRight) { $Output = "$ColumnValue".PadLeft($PadLength) } else { $Output = "$ColumnValue".PadRight($PadLength) } } else { if ($AlignRight) { $Output = $Output + "$ColumnValue".PadLeft($PadLength) } else { $Output = $Output + "$ColumnValue".PadRight($PadLength) } } $ColumnNumber++ } if ($Stream -eq 'Output') { Write-Output -InputObject $Output } elseif ($Stream -eq 'Host') { if ($ForegroundColorRow -contains $RowCountColors) { [int] $Index = $ForegroundColorRow.IndexOf($RowCountColors) Write-Host -Object $Output -ForegroundColor $ForegroundColor[$Index] } else { Write-Host -Object $Output } } elseif ($Stream -eq 'Warning') { Write-Warning -Message $Output } elseif ($Stream -eq 'Verbose') { Write-Verbose -Message $Output } elseif ($Stream -eq 'Debug') { Write-Debug -Message $Output } elseif ($Stream -eq 'Information') { Write-Information -MessageData $Output } if (-not $HideTableHeaders) { # Add underline if ($FirstLoop) { $HeaderUnderline = $Output -Replace '\w', '-' #Write-Verbose -Message $HeaderUnderline if ($Stream -eq 'Output') { Write-Output -InputObject $HeaderUnderline } elseif ($Stream -eq 'Host') { if ($ForegroundColorRow -contains $RowCountColors) { [int] $Index = $ForegroundColorRow.IndexOf($RowCountColors) Write-Host -Object $HeaderUnderline -ForegroundColor $ForegroundColor[$Index] } else { Write-Host -Object $HeaderUnderline } } elseif ($Stream -eq 'Warning') { Write-Warning -Message $HeaderUnderline } elseif ($Stream -eq 'Verbose') { Write-Verbose -Message $HeaderUnderline } elseif ($Stream -eq 'Debug') { Write-Debug -Message $HeaderUnderline } elseif ($Stream -eq 'Information') { Write-Information -MessageData $HeaderUnderline } } } $FirstLoop = $false $RowCountColors++ } } # Add empty line if ($Stream -eq 'Output') { Write-Output -InputObject '' } elseif ($Stream -eq 'Host') { Write-Host -Object '' } elseif ($Stream -eq 'Warning') { Write-Warning -Message '' } elseif ($Stream -eq 'Verbose') { Write-Verbose -Message '' } elseif ($Stream -eq 'Debug') { Write-Debug -Message '' } elseif ($Stream -eq 'Information') { Write-Information -MessageData '' } # Set back to defaults if ($Stream -eq 'Output') { # } elseif ($Stream -eq 'Host') { # } elseif ($Stream -eq 'Warning') { $WarningPreference = $WarningCurrent } elseif ($Stream -eq 'Verbose') { $VerbosePreference = $VerboseCurrent } elseif ($Stream -eq 'Debug') { $DebugPreference = $DebugCurrent } elseif ($Stream -eq 'Information') { $InformationPreference = $InformationCurrent } } } <# Format-ToTitleCase 'me' 'me i feel good' | Format-ToTitleCase 'me i feel', 'not feel' |Format-ToTitleCase #> function Format-ToTitleCase { [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)][string[]] $Text ) Begin {} Process { $Conversion = foreach ($T in $Text) { (Get-Culture).TextInfo.ToTitleCase($T) } } End { return $Conversion } } function Format-TransposeTable { [CmdletBinding()] param ( [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [object[]]$Object, [ValidateSet("ASC", "DESC", "NONE")][String] $Sort = 'NONE' ) begin { $i = 0; } process { foreach ($myObject in $Object) { if ($myObject.GetType().Name -eq 'hashtable' -or $myObject.GetType().Name -eq 'OrderedDictionary') { #Write-Verbose "Format-TransposeTable - Converting HashTable/OrderedDictionary to PSCustomObject - $($myObject.GetType().Name)" $output = New-Object -TypeName PsObject; Add-Member -InputObject $output -MemberType ScriptMethod -Name AddNote -Value { Add-Member -InputObject $this -MemberType NoteProperty -Name $args[0] -Value $args[1]; }; if ($Sort -eq 'ASC') { $myObject.Keys | Sort-Object -Descending:$false | % { $output.AddNote($_, $myObject.$_); } } elseif ($Sort -eq 'DESC') { $myObject.Keys | Sort-Object -Descending:$true | % { $output.AddNote($_, $myObject.$_); } } else { $myObject.Keys | % { $output.AddNote($_, $myObject.$_); } } $output; } else { #Write-Verbose "Format-TransposeTable - Converting PSCustomObject to HashTable/OrderedDictionary - $($myObject.GetType().Name)" # Write-Warning "Index $i is not of type [hashtable]"; $output = [ordered] @{}; $myObject | Get-Member -MemberType *Property | % { $output.($_.name) = $myObject.($_.name); } $output } $i += 1; } } } function Format-Verbose { [alias('FV')] [CmdletBinding(DefaultParameterSetName = 'All')] param( [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 1)] [object] $InputObject, [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 0, ParameterSetName = 'Property')] [Object[]] $Property, [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 2, ParameterSetName = 'ExcludeProperty')] [Object[]] $ExcludeProperty, [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 3)] [switch] $HideTableHeaders, [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 6)] [validateset('Output', 'Host', 'Warning', 'Verbose', 'Debug', 'Information')] [string] $Stream = 'Verbose', [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 7)] [alias('AsList')][switch] $List, [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 8)] [alias('Rotate', 'RotateData', 'TransposeColumnsRows', 'TransposeData')] [switch] $Transpose, [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 9)] [ValidateSet("ASC", "DESC", "NONE")] [string] $TransposeSort = 'NONE' ) Begin { $IsVerbosePresent = $PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent if ($Stream -eq 'Output') { # } elseif ($Stream -eq 'Host') { # } elseif ($Stream -eq 'Warning') { [System.Management.Automation.ActionPreference] $WarningCurrent = $WarningPreference $WarningPreference = 'continue' } elseif ($Stream -eq 'Verbose') { [System.Management.Automation.ActionPreference] $VerboseCurrent = $VerbosePreference $VerbosePreference = 'continue' } elseif ($Stream -eq 'Debug') { [System.Management.Automation.ActionPreference] $DebugCurrent = $DebugPreference $DebugPreference = 'continue' } elseif ($Stream -eq 'Information') { [System.Management.Automation.ActionPreference] $InformationCurrent = $InformationPreference $InformationPreference = 'continue' } [bool] $FirstRun = $True # First run for pipeline [bool] $FirstLoop = $True # First loop for data [bool] $FirstList = $True # First loop for a list [int] $ScreenWidth = $Host.UI.RawUI.WindowSize.Width - 12 # Removes 12 chars because of VERBOSE: output $ArrayList = @() } Process { $ArrayList += $InputObject } End { $ArrayList | Format-Table | Out-String | Write-Verbose } } #Get-Process | Format-Verbose <# ReferenceOnly It's not really a function. If you use this it will display Get-CommandInfo. It's something you can use for reference if needed. #> Function Get-CommandInfo { $Command = @{} $Command.Type = "$($MyInvocation.MyCommand.CommandType)".ToLower() $Command.Name = "$($MyInvocation.MyCommand.Name)" return $Command } function Get-FileInformation { [CmdletBinding()] param( [string] $File ) if (Test-Path $File) { return get-item $File | Select-Object Name, FullName, @{N = 'Size'; E = {Get-FileSize -Bytes $_.Length}}, IsReadOnly, LastWriteTime } return } # https://docs.microsoft.com/en-us/dotnet/api/system.io.path.getfilenamewithoutextension?view=netframework-4.7.2 function Get-FileName { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER Extension Parameter description .PARAMETER Temporary Parameter description .PARAMETER TemporaryFileOnly Parameter description .EXAMPLE Get-FileName -Temporary Output: 3ymsxvav.tmp .EXAMPLE Get-FileName -Temporary Output: C:\Users\pklys\AppData\Local\Temp\tmpD74C.tmp .EXAMPLE Get-FileName -Temporary -Extension 'xlsx' Output: C:\Users\pklys\AppData\Local\Temp\tmp45B6.xlsx .NOTES General notes #> [CmdletBinding()] param( [string] $Extension = 'tmp', [switch] $Temporary, [switch] $TemporaryFileOnly ) if ($Temporary) { return "$($([System.IO.Path]::GetTempFileName()).Split('.')[0]).$Extension" } if ($TemporaryFileOnly) { # Generates 3ymsxvav.tmp return "$($([System.IO.Path]::GetRandomFileName()).Split('.')[0]).$Extension" } } function Get-FilesInFolder { [CmdletBinding()] param( [string] $Folder, [string] $Extension = '*.evtx' ) $ReturnFiles = @() $Files = Get-ChildItem -Path $Folder -Filter $Extension -Recurse foreach ($File in $Files) { $ReturnFiles += $File.FullName } return $ReturnFiles } function Get-FileSize { [CmdletBinding()] param( $Bytes ) $sizes = 'Bytes,KB,MB,GB,TB,PB,EB,ZB' -split ',' for ($i = 0; ($Bytes -ge 1kb) -and ($i -lt $sizes.Count); $i++) { $Bytes /= 1kb } $N = 2; if ($i -eq 0) { $N = 0 } return "{0:N$($N)} {1}" -f $Bytes, $sizes[$i] } function Get-HashMaxValue { [CmdletBinding()] param ( [Object] $hashTable, [switch] $Lowest ) if ($Lowest) { return ($hashTable.GetEnumerator() | Sort-Object value -Descending | Select-Object -Last 1).Value } else { return ($hashTable.GetEnumerator() | Sort-Object value -Descending | Select-Object -First 1).Value } } function Get-HTML($text) { $text = $text.Split("`r") foreach ($t in $text) { Write-Host $t } } #requires -Module PSWriteColor <# .SYNOPSIS Returns an instance of the logger object. .EXAMPLE with full log name $Logger = Get-Logger -ShowTime -LogPath 'C:\temp\test.log' $Logger.AddErrorRecord("test error") $Logger.AddInfoRecord("test info") $Logger.AddSuccessRecord("test success") $Logger.AddRecord("test record") .EXAMPLE with directory name and auto-generated log name $Logger = Get-Logger -ShowTime -LogsDir 'C:\temp' $Logger.AddErrorRecord("test error") .EXAMPLE with directory name and logo name defined separately $Logger = Get-Logger -ShowTime -Directory 'C:\temp' -Filename 'test.log' $Logger.AddErrorRecord("test error") .EXAMPLE without logfile, only console output $Logger = Get-Logger -ShowTime $Logger.AddErrorRecord("test error") #> function Get-Logger { [CmdletBinding(DefaultParameterSetName="All")] param ( [Parameter(Mandatory = $false, ParameterSetName = 'Logpath')] [string] $LogPath, [Parameter(Mandatory = $false, ParameterSetName = 'Complexpath')] [string] $LogsDir, [Parameter(Mandatory = $false, ParameterSetName = 'Complexpath')] [string] $Filename, [switch] $ShowTime, [string] $TimeFormat = 'yyyy-MM-dd HH:mm:ss' ) if ($PSCmdlet.ParameterSetName -eq 'Complexpath') { if (-not $Filename) { $CallerName = [System.IO.Path]::GetFileNameWithoutExtension((Split-Path $MyInvocation.PSCommandPath -Leaf)) $Filename = "$([DateTime]::Now.ToString($TimeFormat) -replace('[^.\-\w]', '_'))_$CallerName.log" } $LogPath = Join-Path $LogsDir $Filename } if ($LogPath) { $LogsDir = [System.IO.Path]::GetDirectoryName($LogPath) New-Item $LogsDir -ItemType Directory -Force | Out-Null New-Item $LogPath -ItemType File -Force | Out-Null } $Logger = [PSCustomObject]@{ LogPath = $LogPath ShowTime = $ShowTime TimeFormat = $TimeFormat } Add-Member -InputObject $Logger -MemberType ScriptMethod AddErrorRecord -Value { param( [Parameter(Mandatory = $true)] [string]$String ) if (-not $this.LogPath) { Write-Color -Text "[Error] ", $String -Color Red, White -ShowTime:$this.ShowTime -TimeFormat $this:TimeFormat } else { Write-Color -Text "[Error] ", $String -Color Red, White -LogFile:$this.LogPath -ShowTime:$this.ShowTime -TimeFormat $this:TimeFormat } } Add-Member -InputObject $Logger -MemberType ScriptMethod AddInfoRecord -Value { param( [Parameter(Mandatory = $true)] [string]$String ) if (-not $this.LogPath) { Write-Color -Text "[Info] ", $String -Color Yellow, White -ShowTime:$this.ShowTime -TimeFormat $this:TimeFormat } else { Write-Color -Text "[Info] ", $String -Color Yellow, White -LogFile:$this.LogPath -ShowTime:$this.ShowTime -TimeFormat $this:TimeFormat } } Add-Member -InputObject $Logger -MemberType ScriptMethod AddWarningRecord -Value { param( [Parameter(Mandatory = $true)] [string]$String ) if (-not $this.LogPath) { Write-Color -Text "[Warning] ", $String -Color Magenta, White -ShowTime:$this.ShowTime -TimeFormat $this:TimeFormat } else { Write-Color -Text "[Warning] ", $String -Color Magenta, White -LogFile:$this.LogPath -ShowTime:$this.ShowTime -TimeFormat $this:TimeFormat } } Add-Member -InputObject $Logger -MemberType ScriptMethod AddRecord -Value { param( [Parameter(Mandatory = $true)] [string]$String ) if (-not $this.LogPath) { Write-Color -Text " $String" -Color White -ShowTime:$this.ShowTime -TimeFormat $this:TimeFormat } else { Write-Color -Text " $String" -Color White -LogFile:$this.LogPath -ShowTime:$this.ShowTime -TimeFormat $this:TimeFormat } } Add-Member -InputObject $Logger -MemberType ScriptMethod AddSuccessRecord -Value { param( [Parameter(Mandatory = $true)] [string]$String ) if (-not $this.LogPath) { Write-Color -Text "[Success] ", $String -Color Green, White -ShowTime:$this.ShowTime -TimeFormat $this:TimeFormat } else { Write-Color -Text "[Success] ", $String -Color Green, White -LogFile:$this.LogPath -ShowTime:$this.ShowTime -TimeFormat $this:TimeFormat } } return $Logger } function Get-MimeType { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $FileName ) $MimeMappings = @{ '.jpeg' = 'image/jpeg' '.jpg' = 'image/jpeg' '.png' = 'image/png' } $Extension = [System.IO.Path]::GetExtension( $FileName ) $ContentType = $MimeMappings[ $Extension ] if ([string]::IsNullOrEmpty($ContentType)) { return New-Object System.Net.Mime.ContentType } else { return New-Object System.Net.Mime.ContentType($ContentType) } } Function Get-ModulesAvailability { param( [string]$Name ) if (-not(Get-Module -Name $Name)) { if (Get-Module -ListAvailable | Where-Object { $_.Name -eq $Name }) { try { Import-Module -Name $Name return $true } catch { return $false } } else { #module not available return $false } } else { return $true } #module already loaded } function Get-MyIP { [CmdletBinding()] param() $DNSParam = @{ Name = 'myip.opendns.com' Server = 'resolver1.opendns.com' DnsOnly = $true } return Resolve-DnsName @DNSParam | ForEach-Object IPAddress } function Get-ObjectCount { [CmdletBinding()] param( [parameter(ValueFromPipelineByPropertyName, ValueFromPipeline)][Object]$Object ) return $($Object | Measure-Object).Count } function Get-ObjectData { [CmdletBinding()] param( $Object, $Title, [switch] $DoNotAddTitles ) $ArrayList = New-Object System.Collections.ArrayList $Values = $Object.$Title Write-Verbose "Get-ObjectData1: Title $Title Values: $Values" if ((Get-ObjectCount $values) -eq 1 -and $DoNotAddTitles -eq $false) { $ArrayList.Add("$Title - $Values") | Out-Null } else { if ($DoNotAddTitles -eq $false) { $ArrayList.Add($Title) | Out-Null } foreach ($Value in $Values) { $ArrayList.Add("$Value") | Out-Null } } Write-Verbose "Get-ObjectData2: Title $Title Values: $(Get-ObjectCount $ArrayList)" return $ArrayList } function Get-ObjectKeys { param( [object] $Object, [string] $Ignore ) $Data = $Object.Keys | Where { $_ -notcontains $Ignore } return $Data } # This function goes thru an object such as Get-Aduser and scans every object returned getting all properties # This basically makes sure that all properties are known at run time of Export to SQL, Excel or Word <# $Test = Get-Process Get-ObjectProperties -Object $Test #> function Get-ObjectProperties { param ( [object] $Object, [string[]] $AddProperties, # provides ability to add some custom properties [switch] $Sort ) $Properties = New-ArrayList foreach ($O in $Object) { $ObjectProperties = $O.PSObject.Properties.Name foreach ($Property in $ObjectProperties) { Add-ToArrayAdvanced -List $Properties -Element $Property -SkipNull -RequireUnique } } foreach ($Property in $AddProperties) { Add-ToArrayAdvanced -List $Properties -Element $Property -SkipNull -RequireUnique } if ($Sort) { return $Properties | Sort-Object } else { return $Properties } } function Get-ObjectPropertiesAdvanced { [CmdletBinding()] param ( [object] $Object, [string[]] $AddProperties, # provides ability to add some custom properties [switch] $Sort ) $Data = @{} $Properties = New-ArrayList $HighestCount = 0 foreach ($O in $Object) { $ObjectProperties = $O.PSObject.Properties.Name $Test = $ObjectProperties -join ',' $Count = $ObjectProperties.Count if ($Count -gt $HighestCount) { $Data.HighestCount = $Count $Data.HighestObject = $O $HighestCount = $Count } foreach ($Property in $ObjectProperties) { Add-ToArrayAdvanced -List $Properties -Element $Property -SkipNull -RequireUnique } } foreach ($Property in $AddProperties) { Add-ToArrayAdvanced -List $Properties -Element $Property -SkipNull -RequireUnique } $Data.Properties = if ($Sort) { $Properties | Sort-Object } else { $Properties } #Write-Verbose "Get-ObjectPropertiesAdvanced - HighestCount: $($Data.HighestCount)" #Write-Verbose "Get-ObjectPropertiesAdvanced - Properties: $($($Data.Properties) -join ',')" # returns for example # $Data.HighestCount = 100 # $Data.HighestObject = $Object # $Data.Properties = array of strings return $Data } function Get-ObjectTitles { [CmdletBinding()] param( $Object ) $ArrayList = New-Object System.Collections.ArrayList Write-Verbose "Get-ObjectTitles - ObjectType $($Object.GetType())" foreach ($Title in $Object.PSObject.Properties) { Write-Verbose "Get-ObjectTitles - Value added to array: $($Title.Name)" $ArrayList.Add($Title.Name) | Out-Null } Write-Verbose "Get-ObjectTitles - Array size: $($ArrayList.Count)" return $ArrayList } function Get-ObjectType { [CmdletBinding()] param( [Object] $Object, [string] $ObjectName = 'Random Object Name', [switch] $VerboseOnly ) $ReturnData = [ordered] @{} $ReturnData.ObjectName = $ObjectName if ($Object -ne $null) { try { $TypeInformation = $Object.GetType() $ReturnData.ObjectTypeName = $TypeInformation.Name $ReturnData.ObjectTypeBaseName = $TypeInformation.BaseType $ReturnData.SystemType = $TypeInformation.UnderlyingSystemType } catch { $ReturnData.ObjectTypeName = '' $ReturnData.ObjectTypeBaseName = '' $ReturnData.SystemType = '' #Write-Verbose "Get-ObjectType - Outside Error: $($_.Exception.Message)" } try { $TypeInformationInsider = $Object[0].GetType() $ReturnData.ObjectTypeInsiderName = $TypeInformationInsider.Name $ReturnData.ObjectTypeInsiderBaseName = $TypeInformationInsider.BaseType $ReturnData.SystemTypeInsider = $TypeInformationInsider.UnderlyingSystemType } catch { $ReturnData.ObjectTypeInsiderName = '' $ReturnData.ObjectTypeInsiderBaseName = '' $ReturnData.SystemTypeInsider = '' #Write-Verbose "Get-ObjectType - Inside Error: $($_.Exception.Message)" } } else { $ReturnData.ObjectTypeName = '' $ReturnData.ObjectTypeBaseName = '' $ReturnData.SystemType = '' $ReturnData.ObjectTypeInsiderName = '' $ReturnData.ObjectTypeInsiderBaseName = '' $ReturnData.SystemTypeInsider = '' #Write-Verbose "Get-ObjectType - No data to process - Object is empty?" } Write-Verbose "Get-ObjectType - ObjectTypeName: $($ReturnData.ObjectTypeName)" Write-Verbose "Get-ObjectType - ObjectTypeBaseName: $($ReturnData.ObjectTypeBaseName)" Write-Verbose "Get-ObjectType - SystemType: $($ReturnData.SystemType)" Write-Verbose "Get-ObjectType - ObjectTypeInsiderName: $($ReturnData.ObjectTypeInsiderName)" Write-Verbose "Get-ObjectType - ObjectTypeInsiderBaseName: $($ReturnData.ObjectTypeInsiderBaseName)" Write-Verbose "Get-ObjectType - SystemTypeInsider: $($ReturnData.SystemTypeInsider)" if ($VerboseOnly) { return } else { return Format-TransposeTable -Object $ReturnData } } function Get-PathSeparator { param( ) return [IO.Path]::PathSeparator } function Get-PathTemporary { param( ) return [IO.path]::GetTempPath() } function Get-RandomCharacters { param( [int] $length, [string] $characters ) if ($length -ne 0 -and $characters -ne '') { $random = 1..$length | ForEach-Object { Get-Random -Maximum $characters.length } $private:ofs = "" # https://blogs.msdn.microsoft.com/powershell/2006/07/15/psmdtagfaq-what-is-ofs/ return [String]$characters[$random] } else { return } } function Get-RandomPassword { param( [int] $LettersLowerCase = 4, [int] $LettersHigherCase = 2, [int] $Numbers = 1, [int] $SpecialChars = 0, [int] $SpecialCharsLimited = 1 ) $password = Get-RandomCharacters -length $LettersLowerCase -characters 'abcdefghiklmnoprstuvwxyz' $password += Get-RandomCharacters -length $LettersHigherCase -characters 'ABCDEFGHKLMNOPRSTUVWXYZ' $password += Get-RandomCharacters -length $Numbers -characters '1234567890' $password += Get-RandomCharacters -length $SpecialChars -characters '!$%()=?{@#' $password += Get-RandomCharacters -length $SpecialCharsLimited -characters '!$#' return $password } function Get-RandomStringName { [cmdletbinding()] param( [int] $Size = 31, [switch] $ToLower ) if ($ToLower) { return (-join ((48..57) + (97..122) | Get-Random -Count $Size | ForEach-Object {[char]$_})).ToLower() } else { return -join ((48..57) + (97..122) | Get-Random -Count $Size | ForEach-Object {[char]$_}) } } function Get-SqlQueryColumnInformation { [CmdletBinding()] param ( [string] $SqlServer, [string] $SqlDatabase, [string] $Table ) $Table = $Table.Replace("dbo.", '').Replace('[', '').Replace(']', '') # removes dbo and [] from dbo.[Table] as INFORMATION_SCHEMA expects it without $Query = "SELECT * FROM $SqlDatabase.INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '$Table'" #Write-Verbose $Query $SQLReturn = Invoke-Sqlcmd2 -ServerInstance $SqlServer -Query $Query #-Verbose return $SQLReturn } function Get-TimeSynchronization { param( ) Get-Item HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters Get-Item HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config Get-Item HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer Get-Item HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpClient } <# w32tm /config /manualpeerlist:"0.uk.pool.ntp.org,0x1 1.uk.pool.ntp.org,0x1 2.uk.pool.ntp.org,0x1 3.uk.pool.ntp.org,0x1" w32tm /config /reliable:yes Restart-Service -Name 'w32time' #> <# w32tm /resync /nowait w32tm /query /configuration w32tm /query /source w32tm /query /peers w32tm /query /status w32tm /config /syncfromflags:domhier /update #> <# net stop w32time w32tm /unregister w32tm /register net start w32time #> <# $Servers = 'localhost','127.0.0.1' $w32tm = Invoke-Command -Computer $Servers -ArgumentList $Servers -Scriptblock { Param ($Servers) Foreach ($Server in $Servers) { $Check = w32tm /monitor /computers:$Server /nowarn $ICMP = (($Check | Select-String "ICMP")-Replace "ICMP: " , "").Trim() $ICMPVal = [int]($ICMP -split "ms")[0] $Source = w32tm /query /source $Name = Hostname Switch ($ICMPVal) { {$ICMPVal -le 0} {$Status = "Optimal time synchronisation"} #you probably need another value here since you'll get no status if it is between 0 and 2m {$ICMPVal -lt 100000} {$Status = "0-2 Minute time difference"} {$ICMPVal -ge 100000} {$Status = "Warning, 2 minutes time difference"} {$ICMPVal -ge 300000} {$Status = "Critical. Over 5 minutes time difference!"} } $String = $Name + " - $Status " + "- $ICMP " + " - Source: $Source" Write-Output $String } } $w32tm #> function Get-TimeZoneAdvanced { param( [string[]]$ComputerName = $Env:COMPUTERNAME, [System.Management.Automation.PSCredential] $Credential = [System.Management.Automation.PSCredential]::Empty ) foreach ($computer in $computerName) { $TimeZone = Get-WmiObject -Class win32_timezone -ComputerName $computer -Credential $Credential $LocalTime = Get-WmiObject -Class win32_localtime -ComputerName $computer -Credential $Credential $Output = @{ 'ComputerName' = $localTime.__SERVER; 'TimeZone' = $timeZone.Caption; 'CurrentTime' = (Get-Date -Day $localTime.Day -Month $localTime.Month); } $Object = New-Object -TypeName PSObject -Property $Output Write-Output $Object } } function Get-TimeZoneLegacy () { return ([System.TimeZone]::CurrentTimeZone).StandardName } Function Get-Types { [CmdletBinding()] param ( [Object] $Types ) $TypesRequired = @() foreach ($Type in $Types) { #Write-Verbose "Type: $Type" $TypesRequired += $Type.GetEnumValues() } return $TypesRequired } function Get-WinADForestControllers { [alias('Get-WinADDomainControllers')] <# .SYNOPSIS .DESCRIPTION Long description .PARAMETER TestAvailability Parameter description .EXAMPLE Get-WinADForestControllers -TestAvailability | Format-Table .EXAMPLE Get-WinADDomainControllers .NOTES General notes #> [CmdletBinding()] param( [switch] $TestAvailability ) $Forest = Get-AdForest $Servers = foreach ($D in $Forest.Domains) { try { Get-ADDomainController -Server $D -Filter * | Select Domain, HostName, Forest, IPv4Address, 'Comment' } catch { [PSCustomObject]@{ Domain = $D HostName = '' Forest = $Forest.RootDomain IPV4Address = '' Comment = 'Error connecting to domain' } } } if ($TestAvailability) { foreach ($Server in $Servers) { if ($Server.IPV4Address -ne '') { $Output = Test-Connection -Count 1 -Server $Server.IPV4Address -Quiet -ErrorAction SilentlyContinue Add-Member -InputObject $Server -MemberType NoteProperty -Name 'Pingable' -Value $Output } else { Add-Member -InputObject $Server -MemberType NoteProperty -Name 'Pingable' -Value $false } } } return $Servers } function Get-WinADOrganizationalUnitData { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER OrganizationalUnit Parameter description .EXAMPLE An example Get-WinADOrganizationalUnitData -OrganizationalUnit 'OU=Users-O365,OU=Production,DC=ad,DC=evotec,DC=xyz' .NOTES Output of function: CanonicalName : ad.evotec.xyz/Production/Users-O365 City : CN : Country : PL Created : 09.11.2018 17:38:32 Description : OU for Synchronization of Users to Office 365 DisplayName : DistinguishedName : OU=Users-O365,OU=Production,DC=ad,DC=evotec,DC=xyz LinkedGroupPolicyObjects : {cn={74D09C6F-35E9-4743-BCF7-F87D7010C60D},cn=policies,cn=system,DC=ad,DC=evotec,DC=xyz} ManagedBy : Modified : 19.11.2018 22:54:47 Name : Users-O365 PostalCode : ProtectedFromAccidentalDeletion : True State : StreetAddress : #> [CmdletBinding()] param( [string[]] $OrganizationalUnit ) $Output = foreach ($OU in $OrganizationalUnit) { $Data = Get-ADOrganizationalUnit -Identity $OU -Properties CanonicalName, City, CN, Country, Created, Description, DisplayName, DistinguishedName, ManagedBy, Modified, Name, OU, PostalCode, ProtectedFromAccidentalDeletion, State, StreetAddress [PsCustomobject][Ordered] @{ CanonicalName = $Data.CanonicalName City = $Data.City CN = $Data.CN Country = $Data.Country Created = $Data.Created Description = $Data.Description DisplayName = $Data.DisplayName DistinguishedName = $Data.DistinguishedName LinkedGroupPolicyObjects = $Data.LinkedGroupPolicyObjects ManagedBy = Get-WinADUsersByDN -DistinguishedName $U.ManagedBy Modified = $Data.Modified Name = $Data.Name PostalCode = $Data.PostalCode ProtectedFromAccidentalDeletion = $Data.ProtectedFromAccidentalDeletion State = $Data.State StreetAddress = $Data.StreetAddress } } return $Output } function Get-WinADOrganizationalUnitFromDN { <# .SYNOPSIS .DESCRIPTION Long description .PARAMETER DistinguishedName Parameter description .EXAMPLE An example $DistinguishedName = 'CN=Przemyslaw Klys,OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz' Get-WinADOrganizationalUnitFromDN -DistinguishedName $DistinguishedName .NOTES General notes #> [CmdletBinding()] param( $DistinguishedName ) return [Regex]::Match($DistinguishedName,'(?=OU)(.*\n?)(?<=.)').Value } function Get-WinADUsersByDN { param( [alias('DN')][string[]]$DistinguishedName, [string] $Field = 'DisplayName', # return field [switch] $All ) $Properties = 'DistinguishedName', 'Enabled', 'GivenName', 'Name', 'SamAccountName', 'SID', 'Surname', 'UserPrincipalName', 'EmailAddress', 'DisplayName' $Users = foreach ($DN in $DistinguishedName) { try { get-aduser -Identity $DN -Properties $Properties } catch { # returns empty, basically ignores stuff } } if ($All) { return $Users #.PSObject.Properties.Name } else { return $Users.$Field } } function Get-WinADUsersByOU { [CmdletBinding()] param ( $OrganizationalUnit ) $OU = Get-ADOrganizationalUnit $OrganizationalUnit if ($OU.ObjectClass -eq 'OrganizationalUnit') { try { $Users = Get-ADUser -SearchBase $OU -Filter * -Properties $Script:UserProperties } catch { Write-Color @Script:WriteParameters -Text '[i]', ' One or more properties are invalid - Terminating', ' Terminating' -Color Yellow, White, Red return } } return $Users } function Get-WinADUserSnapshot { [CmdletBinding()] [alias("Get-ADUserSnapshot")] param ( [parameter(Mandatory = $true)][Object] $User, [string] $XmlPath, [switch] $WhatIf ) $Object = @() try { $FullData = Get-ADUser -Identity $User.DistinguishedName -Properties * if (($XmlPath) -and (Test-Path $XmlPath)) { $FullPath = [IO.Path]::Combine($XmlPath, "$($User.SamAccountName).xml") # if (-not $WhatIf) { $FullData | Export-Clixml -Path $FullPath -ErrorAction Stop } $Object += @{ Status = $true; Output = $User.SamAccountName; Extended = "Saved to $FullPath" } } else { $Object += @{ Status = $false; Output = $User.SamAccountName; Extended = 'XmlPath Incorrect' } } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $User.SamAccountName; Extended = $ErrorMessage } } return $Object } <# $FoundUser1 = [pscustomobject] @{ 'Duplicate Group1' = 'test1' 'User2' = 'test2' } $FoundUser2 = [pscustomobject] @{ 'Duplicate Group2' = 'test3' 'User1' = 'test4' } Merge-Objects -Object1 $FoundUser1 -Object2 $FoundUser2 #> function Merge-Objects { [CmdletBinding()] param ( [Object] $Object1, [Object] $Object2 ) $Object = [ordered] @{} foreach ($Property in $Object1.PSObject.Properties) { $Object += @{$Property.Name = $Property.Value} } foreach ($Property in $Object2.PSObject.Properties) { $Object += @{$Property.Name = $Property.Value} } return [pscustomobject] $Object } function New-ArrayList { [CmdletBinding()] param() $List = New-Object System.Collections.ArrayList <# Mathias Rørbo Jessen: The pipeline will attempt to unravel the list on assignment, so you'll have to either wrap the empty arraylist in an array, like above, or call WriteObject explicitly and tell it not to, like so: $PSCmdlet.WriteObject($List,$false) #> return , $List } function New-GenericList { [CmdletBinding()] param( [Object] $Type = [System.Object] ) return New-Object "System.Collections.Generic.List[$Type]" } function New-Runspace { [cmdletbinding()] param ( [int] $minRunspaces = 1, [int] $maxRunspaces = [int]$env:NUMBER_OF_PROCESSORS + 1 ) $RunspacePool = [RunspaceFactory]::CreateRunspacePool($minRunspaces, $maxRunspaces) #ApartmentState is not available in PowerShell 6+ #$RunspacePool.ApartmentState = "MTA" $RunspacePool.Open() return $RunspacePool } function New-SqlQuery { [CmdletBinding()] param ( [Object] $SqlSettings, [Object] $Object, [Object] $TableMapping ) <# Example on how output looks like: IF NOT EXISTS ( SELECT 1 FROM dbo.[EventsLogsClearedSecurity] WHERE [RecordID] = '2434391' ) BEGIN --INSERT INTO Users (FirstName, LastName) VALUES ('John', 'Smith') INSERT INTO dbo.[EventsLogsClearedSecurity] ( [DomainController],[Action],[BackupPath],[LogType],[Who],[When],[EventID],[RecordID],[AddedWhen],[AddedWho] ) VALUES ( 'AD1.ad.evotec.xyz','Event log automatic backup','C:\Windows\System32\Winevt\Logs\Archive-Security-2018-09-25-14-12-52-658.evtx','Security','Automatic Backup','2018-09-25 16:12:53','1105','2434391','2018-09-25 20:49:02','przemyslaw.klys' ) END #> $ArraySQLQueries = New-ArrayList if ($Object -ne $null) { ## Added fields to know when event was added to SQL and by WHO (in this case TaskS Scheduler User) ## Only adding when $Object exists foreach ($O in $Object) { $ArrayMain = New-ArrayList $ArrayKeys = New-ArrayList $ArrayValues = New-ArrayList if (-not $O.AddedWhen) { Add-Member -InputObject $O -MemberType NoteProperty -Name "AddedWhen" -Value (Get-Date) } if (-not $O.AddedWho) { Add-Member -InputObject $O -MemberType NoteProperty -Name "AddedWho" -Value ($Env:USERNAME) } if ([string]::IsNullOrWhiteSpace($SqlSettings.SqlCheckBeforeInsert)) { $DuplicateColumn = '' $DuplicateValue = '' } else { # This section allows to skip INSERT if value of DuplicateColumn already exists. $DuplicateColumn = ($SqlSettings.SqlCheckBeforeInsert).Replace("[", '').Replace("]", '') # Remove [ ] for comparision $DuplicateValue = '' } foreach ($E in $O.PSObject.Properties) { $FieldName = $E.Name $FieldValue = $E.Value foreach ($MapKey in $TableMapping.Keys) { if ($FieldName -eq $MapKey) { $MapValue = $TableMapping.$MapKey $MapValueSplit = $MapValue -Split ',' if ($FieldValue -is [DateTime]) { $FieldValue = Get-Date $FieldValue -Format "yyyy-MM-dd HH:mm:ss" } if ($FieldValue -like "*'*") { $FieldValue = $FieldValue -Replace "'", "''" } Add-ToArray -List $ArrayKeys -Element "[$($MapValueSplit[0])]" if ([string]::IsNullOrWhiteSpace($FieldValue)) { Add-ToArray -List $ArrayValues -Element "NULL" } else { #if ($DuplicateColumn -eq $FieldName) { # $DuplicateValue = "'$FieldValue'" #} if ($MapValueSplit[0] -eq $DuplicateColumn) { $DuplicateColumn = "[$DuplicateColumn]" # add [] to make sure spaces are supported $DuplicateValue = "'$FieldValue'" } Add-ToArray -List $ArrayValues -Element "'$FieldValue'" } } } } if ($ArrayKeys) { if ($SqlSettings.SqlCheckBeforeInsert -ne $null -and $DuplicateColumn -ne '' -and $DuplicateValue -ne '') { Add-ToArray -List $ArrayMain -Element "IF NOT EXISTS (" Add-ToArray -List $ArrayMain -Element "SELECT 1 FROM " Add-ToArray -List $ArrayMain -Element "$($SqlSettings.SqlTable) " Add-ToArray -List $ArrayMain -Element "WHERE $DuplicateColumn = $DuplicateValue" Add-ToArray -List $ArrayMain -Element ")" } Add-ToArray -List $ArrayMain -Element "BEGIN" Add-ToArray -List $ArrayMain -Element "INSERT INTO $($SqlSettings.SqlTable) (" Add-ToArray -List $ArrayMain -Element ($ArrayKeys -join ',') Add-ToArray -List $ArrayMain -Element ') VALUES (' Add-ToArray -List $ArrayMain -Element ($ArrayValues -join ',') Add-ToArray -List $ArrayMain -Element ')' Add-ToArray -List $ArrayMain -Element "END" Add-ToArray -List $ArraySQLQueries -Element ([string] ($ArrayMain) -replace "`n", "" -replace "`r", "") } } } # Write-Verbose "SQLQuery: $SqlQuery" return $ArraySQLQueries } function New-SqlQueryAlterTable { [CmdletBinding()] param ( [Object]$SqlSettings, [Object]$TableMapping, [string[]] $ExistingColumns ) $ArraySQLQueries = New-ArrayList $ArrayMain = New-ArrayList $ArrayKeys = New-ArrayList foreach ($MapKey in $TableMapping.Keys) { #Write-Verbose "New-SqlQueryAlterTable - MapKey: $MapKey" $MapValue = $TableMapping.$MapKey $Field = $MapValue -Split ',' if ($ExistingColumns -notcontains $MapKey -and $ExistingColumns -notcontains $Field[0]) { #Write-Verbose "New-SqlQueryAlterTable - MapKey: $MapKey not found in $($ExistingColumns -join ',')" #Write-Verbose "New-SqlQueryAlterTable - MapKey: $MapKey MapValue: $MapValue" if ($Field.Count -eq 1) { Add-ToArray -List $ArrayKeys -Element "[$($Field[0])] [nvarchar](max) NULL" } elseif ($Field.Count -eq 2) { Add-ToArray -List $ArrayKeys -Element "[$($Field[0])] $($Field[1]) NULL" } elseif ($Field.Count -eq 3) { Add-ToArray -List $ArrayKeys -Element "[$($Field[0])] $($Field[1]) $($Field[2])" } } } if ($ArrayKeys) { Add-ToArray -List $ArrayMain -Element "ALTER TABLE $($SqlSettings.SqlTable) ADD" Add-ToArray -List $ArrayMain -Element ($ArrayKeys -join ',') Add-ToArray -List $ArrayMain -Element ';' Add-ToArray -List $ArraySQLQueries -Element ([string] ($ArrayMain) -replace "`n", "" -replace "`r", "") } return $ArraySQLQueries } function New-SqlQueryCreateTable { [CmdletBinding()] param ( [Object]$SqlSettings, [Object]$TableMapping ) $ArraySQLQueries = New-ArrayList $ArrayMain = New-ArrayList $ArrayKeys = New-ArrayList foreach ($MapKey in $TableMapping.Keys) { $MapValue = $TableMapping.$MapKey $Field = $MapValue -Split ',' if ($Field.Count -eq 1) { Add-ToArray -List $ArrayKeys -Element "[$($Field[0])] [nvarchar](max) NULL" } elseif ($Field.Count -eq 2) { Add-ToArray -List $ArrayKeys -Element "[$($Field[0])] $($Field[1]) NULL" } elseif ($Field.Count -eq 3) { Add-ToArray -List $ArrayKeys -Element "[$($Field[0])] $($Field[1]) $($Field[2])" } <# $MapValue = $TableMapping.$MapKey if ($FieldValue -is [DateTime]) { Add-ToArray -List $ArrayKeys -Element "[$MapValue] [DateTime] NULL" } elseif ($FieldValue -is [int] -or $FieldValue -is [Int64]) { Add-ToArray -List $ArrayKeys -Element "[$MapValue] [bigint] NULL" } elseif ($FieldValue -is [bool]) { Add-ToArray -List $ArrayKeys -Element "[$MapValue] [bit] NULL" } else { Add-ToArray -List $ArrayKeys -Element "[$MapValue] [nvarchar](max) NULL" } #> } if ($ArrayKeys) { Add-ToArray -List $ArrayMain -Element "CREATE TABLE $($SqlSettings.SqlTable) (" Add-ToArray -List $ArrayMain -Element "ID int IDENTITY(1,1) PRIMARY KEY," Add-ToArray -List $ArrayMain -Element ($ArrayKeys -join ',') Add-ToArray -List $ArrayMain -Element ')' Add-ToArray -List $ArraySQLQueries -Element ([string] ($ArrayMain) -replace "`n", "" -replace "`r", "") } return $ArraySQLQueries } function New-SqlTableMapping { [CmdletBinding()] param( [Object] $SqlTableMapping, [Object] $Object, $Properties, [switch] $BasedOnSqlTable ) if ($SqlTableMapping) { $TableMapping = $SqlTableMapping } else { $TableMapping = @{} if ($BasedOnSqlTable) { foreach ($Property in $Properties) { $FieldName = $Property $FieldNameSql = $Property $TableMapping.$FieldName = $FieldNameSQL } } else { #Write-Verbose 'New-SqlTableMapping - Starting' # Gets the highest object foreach ($O in $Properties.HighestObject) { #if (-not $O.AddedWhen) { # Add-Member -InputObject $O -MemberType NoteProperty -Name "AddedWhen" -Value (Get-Date) #} #if (-not $O.AddedWho) { # Add-Member -InputObject $O -MemberType NoteProperty -Name "AddedWho" -Value ($Env:USERNAME) #} # goes thru properties (but not properties of highest object but for all properties of all objects # if there is value it will use the ones from highest object if not it will utilize nvarchar foreach ($Property in $Properties.Properties) { #foreach ($E in $O.PSObject.Properties) { $FieldName = $Property $FieldValue = $O.$Property $FieldNameSQL = $FieldName.Replace(' ', '') #.Replace('-', '') if ($FieldValue -is [DateTime]) { $TableMapping.$FieldName = "$FieldNameSQL,[datetime],null" #Add-ToArray -List $ArrayKeys -Element "[$MapValue] [DateTime] NULL" #Write-Verbose "New-SqlTableMapping - FieldName: $FieldName FieldValue: $FieldValue FieldNameSQL: $FieldNameSQL FieldDataType: DateTime" } elseif ($FieldValue -is [int] -or $FieldValue -is [Int64]) { $TableMapping.$FieldName = "$FieldNameSQL,[bigint]" #Add-ToArray -List $ArrayKeys -Element "[$MapValue] [bigint] NULL" # Write-Verbose "New-SqlTableMapping - FieldName: $FieldName FieldValue: $FieldValue FieldNameSQL: $FieldNameSQL FieldDataType: BigInt" } elseif ($FieldValue -is [bool]) { $TableMapping.$FieldName = "$FieldNameSQL,[bit]" #Add-ToArray -List $ArrayKeys -Element "[$MapValue] [bit] NULL" #Write-Verbose "New-SqlTableMapping - FieldName: $FieldName FieldValue: $FieldValue FieldNameSQL: $FieldNameSQL FieldDataType: Bit/Bool" } else { $TableMapping.$FieldName = "$FieldNameSQL" #Add-ToArray -List $ArrayKeys -Element "[$MapValue] [nvarchar](max) NULL" #Write-Verbose "New-SqlTableMapping - FieldName: $FieldName FieldValue: $FieldValue FieldNameSQL: $FieldNameSQL FieldDataType: NvarChar" } } } } } #Write-Verbose 'New-SqlTableMapping - Ending' return $TableMapping } function New-UserAdd { [CmdletBinding(SupportsShouldProcess = $true)] param( $Users ) $Success = @() $Failed = @() $Output = @() foreach ($User in $Users) { #New-MsolUser -UserPrincipalName $User.UserPrincipalName -FirstName $User.FirstName -LastName $User.LastName -DisplayName $User.DisplayName -UsageLocation $User.CountryCode -Country $User.Country -City $User.City -WhatIf $PasswordProfile = New-Object -TypeName Microsoft.Open.AzureAD.Model.PasswordProfile $PasswordProfile.Password = $User.Password $PasswordProfile.EnforceChangePasswordPolicy = $false $PasswordProfile.ForceChangePasswordNextLogin = $false try { if ($pscmdlet.ShouldProcess("$($User.DisplayName)", "New-UserAdd")) { Write-Color "New-AzureADUser - Processing new user ", $User.DisplayName -Color White, Yellow if ($User.MailNickName) { if ($User.FirstName -eq $null -or $User.FirstName.Trim() -eq '') { $User.FirstName = 'Not set' } if ($User.LastName -eq $null -or $User.LastName.Trim() -eq '') { $User.LastName = 'Not set' } $Output += New-AzureADUser -UserPrincipalName $User.UserPrincipalName ` -GivenName ([string] $User.FirstName) ` -Surname ([string] $User.LastName) ` -DisplayName ([string] $User.DisplayName) ` -UsageLocation ([string] $User.CountryCode) ` -Country ([string] $User.Country) ` -City ([string] $User.City) ` -PasswordProfile $PasswordProfile ` -AccountEnabled $true ` -MailNickName ([string] $User.MailNickName) ` -ErrorAction Stop $Success += $User } else { $Failed += $User } } else { # pretends WhatIf all success $Success += $User } } catch { $Failed += $User $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " Write-Warning "New-AzureADUser - Failed with error message: $ErrorMessage" } } $Data = @{} $Data.Failed = $Failed $Data.Success = $Success return $Data } function Remove-DuplicateObjects { [CmdletBinding()] param( $Object, $Property ) $Count = Get-ObjectCount -Object $Object if ($Count -eq 0) { return $Object } else { return $Object | Sort-Object -Property $Property -Unique } } function Remove-FromArray { [CmdletBinding()] param( [System.Collections.ArrayList] $List, [Object] $Element, [switch] $LastElement ) if ($LastElement) { $LastID = $List.Count - 1 $List.RemoveAt($LastID) > $null } else { $List.Remove($Element) > $null } } function Remove-ObjectsExistingInTarget { param( $ObjectSource, $ObjectTarget, [string] $ComparePropertySource, [string] $ComparePropertyTarget, [switch] $Reverse # returns only existing objects ) $ObjectsExistingInTarget = @() $ObjectsNotExistingInTarget = @() foreach ($Object in $ObjectSource) { if ($ObjectTarget.$ComparePropertySource -contains $Object.$ComparePropertyTarget) { $ObjectsExistingInTarget += $Object } else { $ObjectsNotExistingInTarget += $Object } } if ($Reverse) { return $ObjectsExistingInTarget } else { return $ObjectsNotExistingInTarget } } function Remove-WhiteSpace { param( [string] $Text ) $Text = $Text -replace '(^\s+|\s+$)','' -replace '\s+',' ' return $Text } <# $MyValue = Remove-WhiteSpace -Text 'My Field ' Write-Color $MyValue, 'No' -Color White, Yellow #> function Remove-WinADUserGroups { [CmdletBinding()] [alias("Remove-ADUserGroups")] param( [parameter(Mandatory = $true)][Object] $User, [ValidateSet("Distribution", "Security")][String] $GroupCategory , [ValidateSet("DomainLocal", "Global", "Universal")][String] $GroupScope, [string[]] $Groups, [switch] $All, [switch] $WhatIf ) $Object = @() try { $ADgroups = Get-ADPrincipalGroupMembership -Identity $User.DistinguishedName -ErrorAction Stop | Where-Object { $_.Name -ne "Domain Users" } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $Group.Name; Extended = $ErrorMessage } } if ($ADgroups) { if ($All) { #Write-Color @Script:WriteParameters -Text '[i]', ' Removing groups ', ($ADgroups.Name -join ', '), ' from user ', $User.DisplayName -Color White, Yellow, Green, White, Yellow foreach ($Group in $ADgroups) { try { if (-not $WhatIf) { Remove-ADPrincipalGroupMembership -Identity $User.DistinguishedName -MemberOf $Group -Confirm:$false -ErrorAction Stop } $Object += @{ Status = $true; Output = $Group.Name; Extended = 'Removed from group.' } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $Group.Name; Extended = $ErrorMessage } } } } if ($GroupCategory) { $ADGroupsByCategory = $ADgroups | Where-Object { $_.GroupCategory -eq $GroupCategory } if ($ADGroupsByCategory) { #Write-Color @Script:WriteParameters -Text '[i]', ' Removing groups (by category - ', $GroupCategory, ") ", ($ADGroupsByCategory.Name -join ', '), ' from user ', $User.DisplayName -Colo White, Yellow, Green, White, Yellow, White, Blue foreach ($Group in $ADGroupsByCategory) { try { if (-not $WhatIf) { Remove-ADPrincipalGroupMembership -Identity $User.DistinguishedName -MemberOf $Group -Confirm:$false -ErrorAction Stop } $Object += @{ Status = $true; Output = $Group.Name; Extended = 'Removed from group.' } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $Group.Name; Extended = $ErrorMessage } } } } } if ($GroupScope) { $ADGroupsByScope = $ADgroups | Where-Object { $_.GroupScope -eq $GroupScope } if ($ADGroupsByScope) { #Write-Color @Script:WriteParameters -Text '[i]', ' Removing groups (by scope ', " - $GroupScope) ", ($ADGroupsByScope.Name -join ', '), ' from user ', $User.DisplayName -Color White, Yellow, Green, White, Yellow, White, Blue foreach ($Group in $ADGroupsByScope) { try { if (-not $WhatIf) { Remove-ADPrincipalGroupMembership -Identity $User.DistinguishedName -MemberOf $Group -Confirm:$false -ErrorAction Stop } $Object += @{ Status = $true; Output = $Group.Name; Extended = 'Removed from group.' } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $Group.Name; Extended = $ErrorMessage } } } } } if ($Groups) { foreach ($Group in $Groups) { $ADGroupsByName = $ADgroups | Where-Object { $_.Name -like $Group } if ($ADGroupsByName) { #Write-Color @Script:WriteParameters -Text '[i]', ' Removing groups (by name) ', ($ADGroupsByName.Name -join ', '), ' from user ', $User.DisplayName -Color White, Yellow, Green, White, Yellow, White, Yellow try { if (-not $WhatIf) { Remove-ADPrincipalGroupMembership -Identity $User.DistinguishedName -MemberOf $ADGroupsByName -Confirm:$false -ErrorAction Stop } $Object += @{ Status = $true; Output = $Group.Name; Extended = 'Removed from group.' } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $Group.Name; Extended = $ErrorMessage } } } else { $Object += @{ Status = $false; Output = $Group.Name; Extended = 'Not available on user.' } } } } } return $Object } function Rename-UserValuesFromHash { [CmdletBinding()] param( $Users, $MatchData, $FieldTypes ) <# foreach ($User in $DataFinland) { $User.UserPrincipalName = $($User.UserPrincipalName).ToLower().Replace('@test.com', '@newdomain.com') $User.License = $($User.License).ToLower().Replace('test:', 'newdomain:') $User.ProxyAddress = $(($User.ProxyAddress).ToLower()).Replace('@test.com', '@newdomain.com').Replace('@test.onmicrosoft.com', '@newdomain.onmicrosoft.com') } #> Write-Verbose "FieldTypes: $($FieldTypes -join ',')" foreach ($User in $Users) { foreach ($Match in $MatchData.Keys) { $Key = $Match $Value = $MatchData.$Match Write-Verbose "User: $($User.UserPrincipalName) Key: $Key Value: $Value" foreach ($Field in $FieldTypes) { if ($User.$Field) { $User.$Field = $($User.$Field).ToLower().Replace($Key, $Value) } } } } return $Users } function Request-Credentials { [CmdletBinding()] param( [string] $UserName, [string] $Password, [switch] $AsSecure, [switch] $FromFile, [switch] $Output, [switch] $NetworkCredentials, [string] $Service ) if ($FromFile) { if (($Password -ne '') -and (Test-Path $Password)) { # File is there and we are reading it into Password Write-Verbose "Request-Credentials - Reading password from file $Password" $Password = Get-Content -Path $Password } else { # File is not there or couldn't be read if ($Output) { $Object = @{ Status = $false; Output = $Service; Extended = 'File with password unreadable.' } return $Object } else { Write-Warning "Request-Credentials - Secure password from file couldn't be read. File not readable. Terminating." return } } } if ($AsSecure) { try { $NewPassword = $Password | ConvertTo-SecureString } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " if ($ErrorMessage -like '*Key not valid for use in specified state*') { if ($Output) { $Object = @{ Status = $false; Output = $Service; Extended = "Couldn't use credentials provided. Most likely using credentials from other user/session/computer." } return $Object } else { Write-Warning -Message "Request-Credentials - Couldn't use credentials provided. Most likely using credentials from other user/session/computer." return } } else { if ($Output) { $Object = @{ Status = $false; Output = $Service; Extended = $ErrorMessage } return $Object } else { Write-Warning -Message "Request-Credentials - $ErrorMessage" return } } } } else { $NewPassword = $Password } if ($UserName -and $NewPassword) { if ($AsSecure) { $Credentials = New-Object System.Management.Automation.PSCredential($Username, $NewPassword) #Write-Verbose "Request-Credentials - Using AsSecure option with Username $Username and password: $NewPassword" } else { $SecurePassword = $Password | ConvertTo-SecureString -asPlainText -Force $Credentials = New-Object System.Management.Automation.PSCredential($Username, $SecurePassword) #Write-Verbose "Request-Credentials - Using AsSecure option with Username $Username and password: $NewPassword converted to $SecurePassword" } } else { if ($Output) { $Object = @{ Status = $false; Output = $Service; Extended = 'Username or/and Password is empty' } return $Object } else { Write-Warning -Message 'Request-Credentials - UserName or Password are empty.' return } } if ($NetworkCredentials) { $RewritePassword = $Credentials.GetNetworkCredential() #Get-ObjectType $RewritePassword -VerboseOnly -Verbose return $RewritePassword } else { #Get-ObjectType $Credentials -VerboseOnly -Verbose return $Credentials } } function Save-XML { param ( [string] $FilePath, [System.Xml.XmlNode] $xml ) $utf8WithoutBom = New-Object System.Text.UTF8Encoding($false) $writer = New-Object System.IO.StreamWriter($FilePath, $false, $utf8WithoutBom) $xml.Save( $writer ) $writer.Close() } function Search-Command { [cmdletbinding()] param ( $CommandName ) return [bool](Get-Command -Name $CommandName -ErrorAction SilentlyContinue) } function Send-Email { [CmdletBinding(SupportsShouldProcess = $true)] param ( [hashtable] $EmailParameters, [string] $Body = "", [string[]] $Attachment, [hashtable] $InlineAttachments, [string] $Subject = "", [string[]] $To ) $SmtpClient = New-Object -TypeName System.Net.Mail.SmtpClient if ($EmailParameters.EmailServer) { $SmtpClient.Host = $EmailParameters.EmailServer } else { return @{ Status = $False Error = "Email Server Host is not set." SentTo = "" } } # Adding parameters to login to server if ($EmailParameters.EmailServerPort) { $SmtpClient.Port = $EmailParameters.EmailServerPort } else { return @{ Status = $False Error = "Email Server Port is not set." SentTo = "" } } if ($EmailParameters.EmailServerLogin -ne '') { $Credentials = Request-Credentials -UserName $EmailParameters.EmailServerLogin ` -Password $EmailParameters.EmailServerPassword ` -AsSecure:$EmailParameters.EmailServerPasswordAsSecure ` -FromFile:$EmailParameters.EmailServerPasswordFromFile ` -NetworkCredentials #-Verbose $SmtpClient.Credentials = $Credentials } $SmtpClient.EnableSsl = $EmailParameters.EmailServerEnableSSL $MailMessage = New-Object -TypeName System.Net.Mail.MailMessage $MailMessage.From = $EmailParameters.EmailFrom if ($To) { foreach ($T in $To) { $MailMessage.To.add($($T)) } } else { if ($EmailParameters.Emailto) { foreach ($To in $EmailParameters.Emailto) { $MailMessage.To.add($($To)) } } } if ($EmailParameters.EmailCC -ne "") { foreach ($CC in $EmailParameters.EmailCC) { $MailMessage.CC.add($($CC)) } } if ($EmailParameters.EmailBCC -ne "") { foreach ($BCC in $EmailParameters.EmailBCC) { $MailMessage.BCC.add($($BCC)) } } $Exists = Test-Key $EmailParameters "EmailParameters" "EmailReplyTo" -DisplayProgress $false if ($Exists -eq $true) { if ($EmailParameters.EmailReplyTo -ne "") { $MailMessage.ReplyTo = $EmailParameters.EmailReplyTo } } $MailMessage.IsBodyHtml = $true if ($Subject -eq '') { $MailMessage.Subject = $EmailParameters.EmailSubject } else { $MailMessage.Subject = $Subject } $MailMessage.Priority = [System.Net.Mail.MailPriority]::$($EmailParameters.EmailPriority) # Encoding $MailMessage.BodyEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncoding) $MailMessage.SubjectEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncoding) # Inlining attachment (s) if ($PSBoundParameters.ContainsKey('InlineAttachments')) { $BodyPart = [Net.Mail.AlternateView]::CreateAlternateViewFromString( $Body, 'text/html' ) $MailMessage.AlternateViews.Add( $BodyPart ) foreach ( $Entry in $InlineAttachments.GetEnumerator() ) { try { $FilePath = $Entry.Value Write-Verbose $FilePath if ($Entry.Value.StartsWith('http')) { $FileName = $Entry.Value.Substring($Entry.Value.LastIndexOf("/") + 1) $FilePath = Join-Path $env:temp $FileName Invoke-WebRequest -Uri $Entry.Value -OutFile $FilePath } $ContentType = Get-MimeType -FileName $FilePath $InAttachment = New-Object Net.Mail.LinkedResource($FilePath, $ContentType ) $InAttachment.ContentId = $Entry.Key $BodyPart.LinkedResources.Add( $InAttachment ) } catch { $MailMessage.Dispose() throw } } } else { $MailMessage.Body = $Body } # Attaching file (s) if ($PSBoundParameters.ContainsKey('Attachment')) { foreach ($Attach in $Attachment) { if (Test-Path $Attach) { $File = New-Object Net.Mail.Attachment($Attach) Write-Verbose "Send-Email - Attaching file $Attach" $MailMessage.Attachments.Add($File) } } } # Sending the Email try { $MailSentTo = "$($MailMessage.To) $($MailMessage.CC) $($MailMessage.BCC)".Trim() if ($pscmdlet.ShouldProcess("$MailSentTo", "Send-Email")) { $SmtpClient.Send($MailMessage) #$att.Dispose(); $MailMessage.Dispose(); return @{ Status = $True Error = "" SentTo = $MailSentTo } } } catch { $MailMessage.Dispose(); return @{ Status = $False Error = $($_.Exception.Message) SentTo = "" } } } function Send-SqlInsert { [CmdletBinding()] param( [Object] $Object, [Object] $SqlSettings, [string] $CheckDuplicateColumn ) $Queries = New-ArrayList $ReturnData = @() if ($SqlSettings.SqlTableTranspose) { $Object = Format-TransposeTable -Object $Object } $SqlTable = Get-SqlQueryColumnInformation -SqlServer $SqlSettings.SqlServer -SqlDatabase $SqlSettings.SqlDatabase -Table $SqlSettings.SqlTable $PropertiesFromAllObject = Get-ObjectPropertiesAdvanced -Object $Object -AddProperties 'AddedWhen', 'AddedWho' $PropertiesFromTable = $SqlTable.Column_name if ($SqlTable -eq $null) { if ($SqlSettings.SqlTableCreate) { Write-Verbose "Send-SqlInsert - SqlTable doesn't exists, table creation is allowed, mapping will be done either on properties from object or from TableMapping defined in config" $TableMapping = New-SqlTableMapping -SqlTableMapping $SqlSettings.SqlTableMapping -Object $Object -Properties $PropertiesFromAllObject $CreateTableSQL = New-SqlQueryCreateTable -SqlSettings $SqlSettings -TableMapping $TableMapping } else { Write-Verbose "Send-SqlInsert - SqlTable doesn't exists, no table creation is allowed. Terminating" $ReturnData += "Error occured: SQL Table doesn't exists. SqlTableCreate option is disabled" return $ReturnData } } else { if ($SqlSettings.SqlTableAlterIfNeeded) { if ( $SqlSettings.SqlTableMapping) { Write-Verbose "Send-SqlInsert - Sql Table exists, Alter is allowed, but SqlTableMapping is already defined" $TableMapping = New-SqlTableMapping -SqlTableMapping $SqlSettings.SqlTableMapping -Object $Object -Properties $PropertiesFromAllObject } else { Write-Verbose "Send-SqlInsert - Sql Table exists, Alter is allowed, and SqlTableMapping is not defined" $TableMapping = New-SqlTableMapping -SqlTableMapping $SqlSettings.SqlTableMapping -Object $Object -Properties $PropertiesFromAllObject $AlterTableSQL = New-SqlQueryAlterTable -SqlSettings $SqlSettings -TableMapping $TableMapping -ExistingColumns $SqlTable.Column_name } } else { if ( $SqlSettings.SqlTableMapping) { Write-Verbose "Send-SqlInsert - Sql Table exists, Alter is not allowed, SqlTableMaping is already defined" $TableMapping = New-SqlTableMapping -SqlTableMapping $SqlSettings.SqlTableMapping -Object $Object -Properties $PropertiesFromAllObject } else { Write-Verbose "Send-SqlInsert - Sql Table exists, Alter is not allowed, SqlTableMaping is not defined, using SqlTable Columns" $TableMapping = New-SqlTableMapping -SqlTableMapping $SqlSettings.SqlTableMapping -Object $Object -Properties $PropertiesFromTable -BasedOnSqlTable } } } ### Rest of code is based on TableMapping <# if ($SqlSettings.SqlTableCreate) { if ($SqlTable -eq $null) { # Table doesn't exists $CreateTableSQL = New-SqlQueryCreateTable -SqlSettings $SqlSettings -TableMapping $TableMapping } else { # Table exists... altering Table to add missing columns $AlterTableSQL = New-SqlQueryAlterTable -SqlSettings $SqlSettings -TableMapping $TableMapping -ExistingColumns $SqlTable.Column_name } } #> Add-ToArrayAdvanced -List $Queries -Element $CreateTableSQL -SkipNull Add-ToArrayAdvanced -List $Queries -Element $AlterTableSQL -SkipNull $Queries += New-SqlQuery -Object $Object -SqlSettings $SqlSettings -TableMapping $TableMapping foreach ($Query in $Queries) { $ReturnData += $Query try { if ($Query) { $ReturnData += Invoke-Sqlcmd2 -SqlInstance $SqlSettings.SqlServer -Database $SqlSettings.SqlDatabase -Query $Query -ErrorAction Stop } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $ReturnData += "Error occured: $ErrorMessage" } } return $ReturnData } <# # Where $ServerName can be set as needed # Where Service is the name of the Network Card (takes wildcard) # Where IpAddresses are given in brackets Set-DnsServerIpAddress -ComputerName $ServerName -NicName "Service*" -IpAddresses '8.8.8.8','8.8.4.4','8.8.8.1' #> function Set-DnsServerIpAddress { [CmdletBinding()] param( [string] $ComputerName, [string] $NicName, [string] $IpAddresses ) if (Test-Connection -ComputerName $ComputerName -Count 2 -Quiet) { Invoke-Command -ComputerName $ComputerName -ScriptBlock { param ($ComputerName, $NicName, $IpAddresses) write-host "Setting on $ComputerName on interface $NicName a new set of DNS Servers $IpAddresses" Set-DnsClientServerAddress -InterfaceAlias $NicName -ServerAddresses $IpAddresses } -ArgumentList $ComputerName, $NicName, $IpAddresses } else { write-host "Can't access $ComputerName. Computer is not online." } } function Set-EmailBody { [CmdletBinding()] param( [Object] $TableData, [alias('TableWelcomeMessage')][string] $TableMessageWelcome, [string] $TableMessageNoData = 'No changes happened during that period.' ) $Body = "<p><i>$TableMessageWelcome</i>" if ($($TableData | Measure-Object).Count -gt 0) { $Body += $TableData | ConvertTo-Html -Fragment | Out-String $Body += "</p>" } else { $Body += "<br><i>$TableMessageNoData</i></p>" } return $body } function Set-EmailBodyPreparedTable ($TableData, $TableWelcomeMessage) { $body = "<p><i>$TableWelcomeMessage</i>" $body += $TableData return $body } function Set-EmailBodyReplacement { [CmdletBinding()] param( [string] $Body, [hashtable] $ReplacementTable, [ValidateSet('Colors', 'Bold')][string] $Type ) switch ($Type) { 'Colors' { foreach ($Field in $ReplacementTable.Keys) { $Value = $ReplacementTable.$Field $Body = $Body -replace $Field, "<font color=`"$Value`">$Field</font>" } } 'Bold' { foreach ($Field in $ReplacementTable.Keys) { $Value = $ReplacementTable.$Field if ($Value -eq $true) { $Body = $Body -replace $Field, "<b>$Field</b>" } } } } return $Body } <# $ReplacementTable = @{ ' Added' = 'green' } $ReplacementTable = @{ ' Added' = $true } #> function Set-EmailBodyReplacementTable { [alias('Set-EmailBodyTableReplacement')] param ( $Body, $TableName, $TableData ) $TableData = $TableData | ConvertTo-Html -Fragment | Out-String $Body = $Body -replace "<<$TableName>>", $TableData return $Body } function Set-EmailFormatting { param ( $Template, $FormattingParameters, $ConfigurationParameters, $Logger ) if ($ConfigurationParameters) { $WriteParameters = $ConfigurationParameters.DisplayConsole } else { $WriteParameters = @{ ShowTime = $true; LogFile = ""; TimeFormat = "yyyy-MM-dd HH:mm:ss" } } $Template = $Template.Split("`n") # https://blogs.msdn.microsoft.com/timid/2014/07/09/one-liner-fun-with-multi-line-blocktext-and-split-split/ $Body = "<body>" if ($Logger) { $Logger.AddInfoRecord("Preparing template - adding HTML <BR> tags...") } else { Write-Color @WriteParameters -Text "[i] Preparing template ", "adding", " HTML ", "<BR>", " tags." -Color White, Yellow, White, Yellow } $StyleFlag = $false foreach ($t in $Template) { ## needs investigation <# if ($t -match 'style>') { $StyleFlag = -not $StyleFlag } if ($StyleFlag) { $Body += $t continue } if ($t -match '[\<|\</][\w+|\d+]') { $Body += $t continue } #> $Body += "$t<br>" }#> foreach ($style in $FormattingParameters.Styles.GetEnumerator()) { foreach ($value in $style.Value) { if ($value -eq "") { continue } if ($Logger) { $Logger.AddInfoRecord("Preparing template - adding HTML $($style.Name) tag for $value.") } else { Write-Color @WriteParameters -Text "[i] Preparing template ", "adding", " HTML ", "$($style.Name)", " tag for ", "$value", ' tags...' -Color White, Yellow, White, Yellow, White, Yellow } $Body = $Body.Replace($value, "<$($style.Name)>$value</$($style.Name)>") } } foreach ($color in $FormattingParameters.Colors.GetEnumerator()) { foreach ($value in $color.Value) { if ($value -eq "") { continue } if ($Logger) { $Logger.AddInfoRecord("Preparing template - adding HTML $($color.Name) tag for $value.") } else { Write-Color @WriteParameters -Text "[i] Preparing template ", "adding", " HTML ", "$($color.Name)", " tag for ", "$value", ' tags...' -Color White, Yellow, White, Yellow, White, Yellow } $Body = $Body.Replace($value, "<span style=color:$($color.Name)>$value</span>") } } foreach ($links in $FormattingParameters.Links.GetEnumerator()) { foreach ($link in $links.Value) { #write-host $link.Text #write-host $link.Link if ($link.Link -like "*@*") { if ($Logger) { $Logger.AddInfoRecord("Preparing template - adding EMAIL Links for $($links.Key).") } else { Write-Color @WriteParameters -Text "[i] Preparing template ", "adding", " EMAIL ", "Links for", " $($links.Key)..." -Color White, Yellow, White, White, Yellow, White } $Body = $Body -replace "<<$($links.Key)>>", "<span style=color:$($link.Color)><a href='mailto:$($link.Link)?subject=$($Link.Subject)'>$($Link.Text)</a></span>" } else { if ($Logger) { $Logger.AddInfoRecord("[i] Preparing template - adding HTML Links for $($links.Key)") } else { Write-Color @WriteParameters -Text "[i] Preparing template ", "adding", " HTML ", "Links for", " $($links.Key)..." -Color White, Yellow, White, White, Yellow, White } $Body = $Body -replace "<<$($links.Key)>>", "<span style=color:$($link.Color)><a href='$($link.Link)'>$($Link.Text)</a></span>" } } } if ($ConfigurationParameters) { if ($ConfigurationParameters.DisplayTemplateHTML -eq $true) { Get-HTML($Body) } } $Body += '</body>' return $Body } function Set-EmailHead { param( $FormattingOptions ) $head = @" <style> BODY { background-color: white; font-family: $($FormattingOptions.FontFamily); font-size: $($FormattingOptions.FontSize); } TABLE { border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse; font-family: $($FormattingOptions.FontTableDataFamily); font-size: $($FormattingOptions.FontTableDataSize); } TH { border-width: 1px; padding: 3px; border-style: solid; border-color: black; background-color: #00297A; color: white; font-family: $($FormattingOptions.FontTableHeadingFamily); font-size: $($FormattingOptions.FontTableHeadingSize); } TR { font-family: $($FormattingOptions.FontTableDataFamily); font-size: $($FormattingOptions.FontTableDataSize); } UL { font-family: $($FormattingOptions.FontFamily); font-size: $($FormattingOptions.FontSize); } LI { font-family: $($FormattingOptions.FontFamily); font-size: $($FormattingOptions.FontSize); } TD { border-width: 1px; padding-right: 2px; padding-left: 2px; padding-top: 0px; padding-bottom: 0px; border-style: solid; border-color: black; background-color: white; font-family: $($FormattingOptions.FontTableDataFamily); font-size: $($FormattingOptions.FontTableDataSize); } H2 { font-family: $($FormattingOptions.FontHeadingFamily); font-size: $($FormattingOptions.FontHeadingSize); } P { font-family: $($FormattingOptions.FontFamily); font-size: $($FormattingOptions.FontSize); } </style> "@ return $Head } function Set-EmailReportBranding { param( [alias('FormattingOptions')] $FormattingParameters ) if ($FormattingParameters.CompanyBranding.Link) { $Report = "<a style=`"text-decoration:none`" href=`"$($FormattingParameters.CompanyBranding.Link)`" class=`"clink logo-container`">" } else { $Report = '' } if ($FormattingParameters.CompanyBranding.Inline) { $Report += "<img width=<fix> height=<fix> src=`"cid:logo`" border=`"0`" class=`"company-logo`" alt=`"company-logo`"></a>" } else { $Report += "<img width=<fix> height=<fix> src=`"$($FormattingParameters.CompanyBranding.Logo)`" border=`"0`" class=`"company-logo`" alt=`"company-logo`"></a>" } if ($FormattingParameters.CompanyBranding.Width -ne "") { $Report = $Report -replace "width=<fix>", "width=$($FormattingParameters.CompanyBranding.Width)" } else { $Report = $Report -replace "width=<fix>", "" } if ($FormattingParameters.CompanyBranding.Height -ne "") { $Report = $Report -replace "height=<fix>", "height=$($FormattingParameters.CompanyBranding.Height)" } else { $Report = $Report -replace "height=<fix>", "" } return $Report } function Set-EmailWordReplacements($Body, $Replace, $ReplaceWith, [switch] $RegEx) { if ($RegEx) { $Body = $Body -Replace $Replace, $ReplaceWith } else { $Body = $Body.Replace($Replace, $ReplaceWith) } return $Body } function Set-EmailWordReplacementsHash { [CmdletBinding()] param ( $Body, $Substitute ) foreach ($Key in $Substitute.Keys) { Write-Verbose "Set-EmailWordReplacementsHash - Key: $Key Value: $($Substitute.$Key)" $Body = Set-EmailWordReplacements -Body $Body -Replace $Key -ReplaceWith $Substitute.$Key } return $Body } function Set-SpecUser { [CmdletBinding()] param( $User, $UsersAzure ) $UserAzure = $UsersAzure | where { $_.UserPrincipalName -eq $User.UserPrincipalName } if ($UserAzure) { Write-Color "Set-SpecUser - Processing user ", $User.DisplayName, ' - ObjectID: ', $($UserAzure.ObjectID), ' user password ', $User.Password -Color White, Yellow $Password = $User.Password | ConvertTo-SecureString -AsPlainText -Force Set-AzureADUserPassword -ObjectId $UserAzure.ObjectID -Password $Password } else { Write-Color "Set-SpecUser - Skipping user ", $User.DisplayName, ' - ObjectID: ', $($UserAzure.ObjectID), ' user password ', $User.Password -Color White, Yellow } } function Set-TimeSynchronization { param( [string[]] $TimeSource = 'time.windows.com', [int] $MaxPosPhaseCorrection = 86400, [int] $MaxnegPhaseCorrection = 86400, [int] $PollInterval = 1800 ) ## set external time source ## set server type to NTP Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters -Name Type -Value 'NTP' Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config -Name AnnounceFlags -Value 5 ## Enable NTP server Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer -Name Enabled -Value 1 ## Specify Time source Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters -Name NtpServer -Value "$TimeSource,0x1" ## Set poll interval in seconds - every 30 minutes Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpClient -Name SpecialPollInterval -Value $PollInterval ## set max +/- time corrections in seconds - 24 hours Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config -Name MaxPosPhaseCorrection -Value $MaxPosPhaseCorrection Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config -Name MaxnegPhaseCorrection -Value $MaxnegPhaseCorrection Stop-Service -Name W32Time Start-Service -Name W32Time } <# $Group1 = 'GDS-TestGroup1' $Group2 = 'GDS-TestGroup2' Set-WinADGroupSynchronization -GroupFrom $Group1 -GroupTo $Group2 -Type 'All' -Recursive None #> function Set-WinADGroupSynchronization { [CmdletBinding()] param( [parameter(Mandatory = $true)][string] $GroupFrom, [parameter(Mandatory = $true)][string] $GroupTo, [parameter(Mandatory = $false)][ValidateSet("User", "Group", "All")][string] $Type = 'User', [parameter(Mandatory = $false)][ValidateSet("None", "RecursiveFrom", "RecursiveBoth", "RecursiveTo")] $Recursive = 'None', [switch] $WhatIf ) Begin { $Object = @() if ($Recursive -eq 'None') { $GroupFromRecursive = $false $GroupToRecursive = $false } elseif ($Recursive -eq 'RecursiveFrom') { $GroupFromRecursive = $true $GroupToRecursive = $false } elseif ($Recursive -eq 'RecursiveBoth') { $GroupFromRecursive = $true $GroupToRecursive = $true } else { $GroupFromRecursive = $false $GroupToRecursive = $true } } Process { try { $GroupMembersFrom = Get-ADGroupMember -Identity $GroupFrom -Recursive:$GroupFromRecursive | Select-Object Name, ObjectClass, SamAccountName, UserPrincipalName } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $Group.Name; Extended = $ErrorMessage } } try { $GroupMembersTo = Get-ADGroupMember -Identity $GroupTo -Recursive:$GroupToRecursive | Select-Object Name, ObjectClass, SamAccountName, UserPrincipalName } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $Group.Name; Extended = $ErrorMessage } } if ($Object.Count -gt 0) { # Something went seriously wrong. Terminate ASAP return $Object } foreach ($User in $GroupMembersFrom) { if ($User.ObjectClass -eq "user") { if ($Type -eq 'User' -or $Type -eq 'All') { if ($GroupMembersTo.SamAccountName -notcontains $User.SamAccountName) { #Write-Color "Not a member ", $User.SamAccountName, " of $GroupTo", ". Adding!" -Color Red -LogFile $LogFile try { if (-not $WhatIf) { Add-ADGroupMember -Identity $GroupTo -Members $User.SamAccountName } $Object += @{ Status = $true; Output = $User.SamAccountName; Extended = "Added to group $GroupTo" } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $Group.Name; Extended = $ErrorMessage } } } } } else { if ($Type -eq 'Group' -or $Type -eq 'All') { if ($GroupMembersTo.SamAccountName -notcontains $User.SamAccountName) { #Write-Color "Not a member ", $User.SamAccountName, " of $GroupTo", ". Adding!" -Color Red -LogFile $LogFile try { if (-not $WhatIf) { Add-ADGroupMember -Identity $GroupTo -Members $User.SamAccountName } $Object += @{ Status = $true; Output = $User.SamAccountName; Extended = "Added to group $GroupTo" } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $Group.Name; Extended = $ErrorMessage } } } } } } foreach ($User in $GroupMembersTo) { if ($User.ObjectClass -eq "user") { if ($Type -eq 'User' -or $Type -eq 'All') { if ($GroupMembersFrom.SamAccountName -notcontains $User.SamAccountName) { Write-Color "Not a member of $GroupFrom - requires removal from $GroupTo ", $User.SamAccountName -Color Red -LogFile $LogFile try { if (-not $WhatIf) { Remove-ADGroupMember -Identity $GroupTo -Members $User.SamAccountName -Confirm:$false } $Object += @{ Status = $true; Output = $User.SamAccountName; Extended = "Removed from group $GroupTo" } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $Group.Name; Extended = $ErrorMessage } } } } } else { if ($Type -eq 'Group' -or $Type -eq 'All') { if ($GroupMembersFrom.SamAccountName -notcontains $User.SamAccountName) { Write-Color "Not a member of $GroupFrom - requires removal from $GroupTo ", $User.SamAccountName -Color Red -LogFile $LogFile try { if (-not $WhatIf) { Remove-ADGroupMember -Identity $GroupTo -Members $User.SamAccountName -Confirm:$false } $Object += @{ Status = $true; Output = $User.SamAccountName; Extended = "Removed from group $GroupTo" } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $Group.Name; Extended = $ErrorMessage } } } } } } } End { return $object } } function Set-WinADUserFields { [CmdletBinding()] [alias("Set-ADUserName")] param ( [parameter(Mandatory = $true)][Object] $User, [parameter(Mandatory = $false)][ValidateSet("Before", "After")][String] $Option, [string] $TextToAdd, [string] $TextToRemove, [string[]] $Fields, [switch] $WhatIf ) $Object = @() if ($TextToAdd) { foreach ($Field in $Fields) { if ($User.$Field -notlike "*$TextToAdd*") { if ($Option -eq 'After') { $NewName = "$($User.$Field)$TextToAdd" } elseif ($Option -eq 'Before') { $NewName = "$TextToAdd$($User."$Field")" } if ($NewName -ne $User.$Field) { if ($Field -eq 'Name') { try { if (-not $WhatIf) { Rename-ADObject -Identity $User.DistinguishedName -NewName $NewName #-WhatIf } $Object += @{ Status = $true; Output = $User.SamAccountName; Extended = "Renamed account '$Field' to '$NewName'" } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $User.SamAccountName; Extended = $ErrorMessage } } } else { $Splat = @{ Identity = $User.DistinguishedName "$Field" = $NewName } try { if (-not $WhatIf) { Set-ADUser @Splat } $Object += @{ Status = $true; Output = $User.SamAccountName; Extended = "Renamed field '$Field' to '$NewName'" } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $User.SamAccountName; Extended = $ErrorMessage } } } } } } } if ($TextToRemove) { foreach ($Field in $Fields) { if ($User.$Field -like "*$TextToRemove*") { $NewName = $($User.$Field).Replace($TextToRemove, '') if ($Field -eq 'Name') { try { if (-not $WhatIf) { Rename-ADObject -Identity $User.DistinguishedName -NewName $NewName #-WhatIf } $Object += @{ Status = $true; Output = $User.SamAccountName; Extended = "Renamed account '$Field' to '$NewName'" } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $User.SamAccountName; Extended = "Field: '$Field' Error: '$ErrorMessage'" } } } else { $Splat = @{ Identity = $User.DistinguishedName "$Field" = $NewName } try { if (-not $WhatIf) { Set-ADUser @Splat #-WhatIf } $Object += @{ Status = $true; Output = $User.SamAccountName; Extended = "Renamed field $Field to $NewName" } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $User.SamAccountName; Extended = "Field: $Field Error: $ErrorMessage" } } } } } } return $Object } Function Set-WinADUserSettingGAL { [CmdletBinding()] [alias("Set-ADUserSettingGAL")] param ( [parameter(Mandatory = $true)][Object] $User, [parameter(Mandatory = $true)][ValidateSet("Hide", "Show")][String]$Option, [switch] $WhatIf ) $Object = @() if ($User) { if ($Option -eq 'Hide') { if (-not $User.msExchHideFromAddressLists) { #Write-Color @Script:WriteParameters -Text '[i]', ' Hiding user ', $User.DisplayName, ' in GAL (Exchange Address Lists)' -Color White, Yellow, Green, White, Yellow try { if (-not $WhatIf) { Set-ADObject -Identity $User.DistinguishedName -Replace @{msExchHideFromAddressLists = $true} } $Object += @{ Status = $true; Output = $User.SamAccountName; Extended = 'Hidden from GAL.' } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $User.SamAccountName; Extended = $ErrorMessage } } } } elseif ($Option -eq 'Show') { if ($User.msExchHideFromAddressLists) { #Write-Color @Script:WriteParameters -Text '[i]', ' Unhiding user ', $User.DisplayName, ' in GAL (Exchange Address Lists)' -Color White, Yellow, Green, White, Yellow try { if ($WhatIf) { Set-ADObject -Identity $User.DistinguishedName -Clear msExchHideFromAddressLists } $Object += @{ Status = $true; Output = $User.SamAccountName; Extended = 'Unhidden in GAL.' } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $User.SamAccountName; Extended = $ErrorMessage } } } } } return $Object } function Set-WinADUserStatus { [CmdletBinding()] [alias("Set-ADUserStatus")] param ( [parameter(Mandatory = $true)][Object] $User, [parameter(Mandatory = $true)][ValidateSet("Enable", "Disable")][String] $Option, [switch] $WhatIf # $WriteParameters ) $Object = @() if ($Option -eq 'Enable' -and $User.Enabled -eq $false) { #if (-not $WriteParameters) { # Write-Color @Script:WriteParameters -Text 'Enabling user ', $User.DisplayName, ' in Active Directory.' -Color Yellow, Green, White, Yellow #} else { # Write-Color @WriteParameters #} try { if (-not $WhatIf) { Set-ADUser -Identity $User.DistinguishedName -Enabled $true } $Object += @{ Status = $true; Output = $User.SamAccountName; Extended = 'Enabled user.' } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $User.SamAccountName; Extended = $ErrorMessage } } } elseif ($Option -eq 'Disable' -and $User.Enabled -eq $true) { #if (-not $WriteParameters) { # Write-Color @Script:WriteParameters -Text 'Disabling user ', $User.DisplayName, 'in Active Directory.' -Color Yellow, Green, White, Yellow #} else { # Write-Color @WriteParameters #} try { if (-not $WhatIf) { Set-ADUser -Identity $User.DistinguishedName -Enabled $false } $Object += @{ Status = $true; Output = $User.SamAccountName; Extended = 'Disabled user.' } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $User.SamAccountName; Extended = $ErrorMessage } } } return $Object } function Set-WinAzureADUserField { [CmdletBinding()] param ( [parameter(Mandatory = $true)][Object] $User, [parameter(Mandatory = $false)][Object] $Value, [switch] $WhatIf ) $Splat = @{} $Splat.UserPrincipalName = $User.UserPrincipalName $Splat.ErrorAction = 'Stop' if ($Value) { $Field = "$($Value.Field)" if ($Field -eq 'UserPrincipalName') { # if UserPrincipalName it means user wants to rename UserPrincipalName # that requires different method $Field = 'NewUserPrincipalName' } $Data = $Value.Value $Splat.$Field = $Data } $Object = @() if ($User.$Field -ne $Data) { try { if (-not $WhatIf) { if ($Field -eq 'UserPrincipalName') { Set-MsolUserPrincipalName @Splat } else { Set-MsolUser @Splat } } $Object += @{ Status = $true; Output = $User.UserPrincipalName; Extended = "Set $Field to $Data" } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " -Replace ' ',' ' $Object += @{ Status = $false; Output = $User.UserPrincipalName; Extended = $ErrorMessage } } } return $Object } function Set-WinAzureADUserLicense { [CmdletBinding()] param ( [parameter(Mandatory = $true)][Object] $User, [parameter(Mandatory = $true)][ValidateSet("Add", "Remove", "RemoveAll", "Replace")][String] $Option, [parameter(Mandatory = $false)][string] $License, [parameter(Mandatory = $false)][string] $LicenseToReplace, [switch] $WhatIf ) $Object = @() if ($Option -eq 'Add') { try { if (-not $WhatIf) { Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $License -ErrorAction Stop } $Object += @{ Status = $true; Output = $User.UserPrincipalName; Extended = "Added license $License to user." } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $User.UserPrincipalName; Extended = $ErrorMessage } } } elseif ($Option -eq 'Remove') { try { if (-not $WhatIf) { Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $License -ErrorAction Stop } $Object += @{ Status = $true; Output = $User.UserPrincipalName; Extended = "Removed license $License from user." } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $User.UserPrincipalName; Extended = $ErrorMessage } } } elseif ($Option -eq 'RemoveAll') { try { foreach ($License in $User.Licenses.AccountSKUID) { if (-not $WhatIf) { Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $License -ErrorAction Stop } $Object += @{ Status = $true; Output = $User.UserPrincipalName; Extended = "Removed license $License from user." } } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $User.UserPrincipalName; Extended = $ErrorMessage } } } elseif ($Option -eq 'Replace') { [bool] $Success = $true try { if (-not $WhatIf) { Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -AddLicenses $License } $Object += @{ Status = $true; Output = $User.UserPrincipalName; Extended = "Added license $License to user before removing $LicenseToReplace." } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $User.UserPrincipalName; Extended = $ErrorMessage } $Success = $false } if ($Success) { try { if (-not $WhatIf) { Set-MsolUserLicense -UserPrincipalName $User.UserPrincipalName -RemoveLicenses $License -ErrorAction Stop } $Object += @{ Status = $true; Output = $User.UserPrincipalName; Extended = "Removed license $LicenseToReplace from user." } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $User.UserPrincipalName; Extended = $ErrorMessage } } } } return $Object } function Set-WinAzureADUserStatus { [CmdletBinding()] param ( [parameter(Mandatory = $true)][Object] $User, [parameter(Mandatory = $true)][ValidateSet("Enable", "Disable")][String] $Option, [switch] $WhatIf ) $Object = @() if ($Option -eq 'Enable' -and $User.BlockCredential -eq $true) { try { if (-not $WhatIf) { Set-MsolUser -UserPrincipalName $User.UserPrincipalName -BlockCredential $false } $Object += @{ Status = $true; Output = $User.UserPrincipalName; Extended = 'Enabled user.' } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $User.UserPrincipalName; Extended = $ErrorMessage } } } elseif ($Option -eq 'Disable' -and $User.BlockCredential -eq $false) { try { if (-not $WhatIf) { Set-MsolUser -UserPrincipalName $User.UserPrincipalName -BlockCredential $true } $Object += @{ Status = $true; Output = $User.UserPrincipalName; Extended = 'Disabled user.' } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " $Object += @{ Status = $false; Output = $User.UserPrincipalName; Extended = $ErrorMessage } } } return $Object } function Set-XML { param ( [string] $FilePath, [string[]]$Paths, [string] $Node, [string] $Value ) [xml]$xmlDocument = Get-Content -Path $FilePath -Encoding UTF8 $XmlElement = $xmlDocument foreach ($Path in $Paths) { $XmlElement = $XmlElement.$Path } $XmlElement.$Node = $Value $xmlDocument.Save($FilePath) # Save-XML -FilePath $FilePath -xml $xmlDocument } function Show-Array { [CmdletBinding()] param( [System.Collections.ArrayList] $List, [switch] $WithType ) foreach ($Element in $List) { $Type = Get-ObjectType -Object $Element if ($WithType) { Write-Output "$Element (Type: $($Type.ObjectTypeName))" } else { Write-Output $Element } } } function Show-DataInVerbose { [CmdletBinding()] param( [Object] $Object ) foreach ($O in $Object) { foreach ($E in $O.PSObject.Properties) { $FieldName = $E.Name $FieldValue = $E.Value Write-Verbose "Display-DataInVerbose - FieldName: $FieldName FieldValue: $FieldValue" } } } function Show-TableVisualization { [CmdletBinding()] param ( [parameter(ValueFromPipelineByPropertyName, ValueFromPipeline)] $Object ) if ($Color) { Write-Color "[i] This is how table looks like in Format-Table" -Color Yellow } Write-Verbose '[i] This is how table looks like in Format-Table' $Object | Format-Table -AutoSize $Data = Format-PSTable $Object #-Verbose Write-Verbose "[i] Rows Count $($Data.Count) Column Count $($Data[0].Count)" $RowNr = 0 if ($Color) { Write-Color "[i] Presenting table after conversion" -Color Yellow } foreach ($Row in $Data) { $ColumnNr = 0 foreach ($Column in $Row) { Write-Verbose "Row: $RowNr Column: $ColumnNr Data: $Column" $ColumnNr++ } $RowNr++ } } function Split-Array { [CmdletBinding()] <# .SYNOPSIS Split an array .NOTES Version : July 2, 2017 - implemented suggestions from ShadowSHarmon for performance .PARAMETER inArray A one dimensional array you want to split .EXAMPLE This splits array into multiple arrays of 3 Example below wil return 1,2,3 + 4,5,6 + 7,8,9 Split-array -inArray @(1,2,3,4,5,6,7,8,9,10) -parts 3 .EXAMPLE This splits array into 3 parts regardless of amount of elements Split-array -inArray @(1,2,3,4,5,6,7,8,9,10) -size 3 # Link: https://gallery.technet.microsoft.com/scriptcenter/Split-an-array-into-parts-4357dcc1 #> param( [Object] $inArray, [int]$parts, [int]$size ) if ($inArray.Count -eq 1) { return $inArray } if ($parts) { $PartSize = [Math]::Ceiling($inArray.count / $parts) } if ($size) { $PartSize = $size $parts = [Math]::Ceiling($inArray.count / $size) } $outArray = New-Object 'System.Collections.Generic.List[psobject]' for ($i = 1; $i -le $parts; $i++) { $start = (($i - 1) * $PartSize) $end = (($i) * $PartSize) - 1 if ($end -ge $inArray.count) {$end = $inArray.count - 1} $outArray.Add(@($inArray[$start..$end])) } return , $outArray } Function Split-ArrayEvery { [CmdletBinding()] param( $list, [int] $count = 4 ) $aggregateList = @() $blocks = [Math]::Floor($list.Count / $count) $leftOver = $list.Count % $count $start = $end = 0 for ($i = 0; $i -lt $blocks; $i++) { $end = $count * ($i + 1) - 1 $aggregateList += @(, $list[$start..$end]) $start = $end + 1 } if ($leftOver -gt 0) { $aggregateList += @(, $list[$start..($end + $leftOver)]) } $aggregateList } function Start-MyProgram { [CmdletBinding()] param ( [string] $Program, [string[]] $CmdArgList ) return & $Program $CmdArgList } function Start-Runspace { [cmdletbinding()] param ( [string] $ScriptBlock, [hashtable] $Parameters, [System.Management.Automation.Runspaces.RunspacePool] $RunspacePool ) $runspace = [PowerShell]::Create() $null = $runspace.AddScript($ScriptBlock) $null = $runspace.AddParameters($Parameters) $runspace.RunspacePool = $RunspacePool $Data = [PSCustomObject]@{ Pipe = $runspace Status = $runspace.BeginInvoke() } return $Data } function Start-TimeLog { [CmdletBinding()] param() $ExecutionTime = [System.Diagnostics.Stopwatch]::StartNew() return $ExecutionTime } function Stop-Runspace { [cmdletbinding()] param( [System.Object[]]$Runspaces, [string] $FunctionName, [System.Management.Automation.Runspaces.RunspacePool] $RunspacePool, [switch] $ExtendedOutput ) $List = while ($Runspaces.Status -ne $null) { foreach ($Runspace in $Runspaces | Where-Object { $_.Status.IsCompleted -eq $true }) { $Errors = foreach ($e in $($Runspace.Pipe.Streams.Error)) { Write-Error -ErrorRecord $e $e } foreach ($w in $($Runspace.Pipe.Streams.Warning)) { Write-Warning -Message $w } foreach ($v in $($Runspace.Pipe.Streams.Verbose)) { Write-Verbose -Message $v } if ($ExtendedOutput) { @{ Output = $Runspace.Pipe.EndInvoke($Runspace.Status) Errors = $Errors } } else { $Runspace.Pipe.EndInvoke($Runspace.Status) } $Runspace.Status = $null } } $RunspacePool.Close() $RunspacePool.Dispose() return $List } function Stop-TimeLog { [CmdletBinding()] param ( [Parameter(ValueFromPipeline = $true)][System.Diagnostics.Stopwatch] $Time, [ValidateSet('OneLiner', 'Array')][string] $Option = 'OneLiner', [switch] $Continue ) Begin {} Process { if ($Option -eq 'Array') { $TimeToExecute = "$($Time.Elapsed.Days) days", "$($Time.Elapsed.Hours) hours", "$($Time.Elapsed.Minutes) minutes", "$($Time.Elapsed.Seconds) seconds", "$($Time.Elapsed.Milliseconds) milliseconds" } else { $TimeToExecute = "$($Time.Elapsed.Days) days, $($Time.Elapsed.Hours) hours, $($Time.Elapsed.Minutes) minutes, $($Time.Elapsed.Seconds) seconds, $($Time.Elapsed.Milliseconds) milliseconds" } } End { if (-not $Continue) { $Time.Stop() } return $TimeToExecute } } function Test-AvailabilityCommands { param ( $Commands ) $CommandsStatus = @() foreach ($Command in $Commands) { $Exists = Search-Command -Command $Command if ($Exists) { Write-Verbose "Test-AvailabilityCommands - Command $Command is available." } else { Write-Verbose "Test-AvailabilityCommands - Command $Command is not available." } $CommandsStatus += $Exists } return $CommandsStatus } function Test-ComputerAvailability { [CmdletBinding()] param( [string[]] $Servers, [ValidateSet('All', 'Ping', 'WinRM', 'PortOpen', 'Ping+WinRM', 'Ping+PortOpen', 'WinRM+PortOpen')] $Test = 'All', [int[]] $Ports = 135, [int] $PortsTimeout = 100, [int] $PingCount = 1 ) $OutputList = @() foreach ($Server in $Servers) { $Output = [PsCustomObject][ordered] @{} $Output.ServerName = $Server if ($Test -eq 'All' -or $Test -like 'Ping*') { $Output.Pingable = Test-Connection -ComputerName $Server -Quiet -Count $PingCount } if ($Test -eq 'All' -or $Test -like '*WinRM*') { $Output.WinRM = Test-WinRM -ComputerName $Server } if ($Test -eq 'All' -or '*PortOpen*') { $Output.PortOpen = Test-ComputerPort -Server $Server -Ports $Ports -Timeout $PortsTimeout } $OutputList += $Output } return $OutputList } function Test-ComputerPort { [CmdletBinding()] Param( [string] $Server, [int[]] $Ports = 135, [int] $TimeOut = 1000 ) foreach ($Port in $Ports) { #Write-Verbose "Test-Port - $Server`:$Port Start" $TcpClient = New-Object system.Net.Sockets.TcpClient $iar = $TcpClient.BeginConnect($server, $port, $null, $null) # Set the Wait time $Wait = $iar.AsyncWaitHandle.WaitOne($TimeOut, $false) # Check to see if the connection is done if (!$Wait) { # Close the connection and report TimeOut $TcpClient.Close() Write-Verbose "Test-Port - $Server`:$Port Connection TimeOut" return $false } else { # Close the connection and report the error if there is one $error.Clear() $TcpClient.EndConnect($iar) | Out-Null if (!$?) { Write-Verbose "Test-Port - $Server`:$Port Error: $($error[0])" $Failed = $true } $TcpClient.Close() } if ($Failed) { break } } if ($Failed) { return $false # Failed on all or just one of tested ports } else { return $true # Established } } function Test-ConfigurationCredentials { [CmdletBinding()] param ( [Object] $Configuration, $AllowEmptyKeys ) $Object = @() foreach ($Key in $Configuration.Keys) { if ($AllowEmptyKeys -notcontains $Key -and [string]::IsNullOrWhiteSpace($Configuration.$Key)) { Write-Verbose "Test-ConfigurationCredentials - Configuration $Key is Null or Empty! Terminating" $Object += @{ Status = $false; Output = $User.SamAccountName; Extended = "Credentials configuration $Key is Null or Empty!" } } } return $Object } function Test-ForestConnectivity { Try { $Test = Get-ADForest return $true } catch { #Write-Warning 'No connectivity to forest/domain.' return $False } } function Test-Key ($ConfigurationTable, $ConfigurationSection = "", $ConfigurationKey, $DisplayProgress = $false) { if ($ConfigurationTable -eq $null) { return $false } try { $value = $ConfigurationTable.ContainsKey($ConfigurationKey) } catch { $value = $false } if ($value -eq $true) { if ($DisplayProgress -eq $true) { Write-Color @script:WriteParameters -Text "[i] ", "Parameter in configuration of ", "$ConfigurationSection.$ConfigurationKey", " exists." -Color White, White, Green, White } return $true } else { if ($DisplayProgress -eq $true) { Write-Color @script:WriteParameters -Text "[i] ", "Parameter in configuration of ", "$ConfigurationSection.$ConfigurationKey", " doesn't exist." -Color White, White, Red, White } return $false } } function Test-ModuleAvailability { if (Search-Command -CommandName 'Get-AdForest') { # future use } else { Write-Warning 'Modules required to run not found.' Exit } } function Test-WinRM { [CmdletBinding()] param ( $ComputerName ) try { $WinRM = Test-WSMan -ComputerName $Server -ErrorAction Stop $Value = $true } catch { $Value = $false } return $Value } Export-ModuleMember ` -Function @('Add-PropertyToList','Add-ToArray','Add-ToArrayAdvanced','Add-ToHashTable','Add-WinADUserGroups','Compare-ObjectProperties','Compare-ObjectsAdvanced','Connect-WinAzure','Connect-WinAzureAD','Connect-WinExchange','Connect-WinService','Connect-WinTeams','Convert-BinaryToHex','Convert-BinaryToString','Convert-ExchangeEmail','Convert-ExchangeItems','Convert-ExchangeSize','ConvertFrom-ErrorRecord','ConvertFrom-OperationType','ConvertFrom-SID','Convert-HexToBinary','Convert-KeyToKeyValue','Convert-Size','Convert-TimeToDays','Convert-ToDateTime','ConvertTo-ImmutableID','Convert-ToTimeSpan','Convert-TwoArraysIntoOne','Convert-UAC','Disconnect-WinAzure','Disconnect-WinAzureAD','Disconnect-WinExchange','Disconnect-WinTeams','Find-DatesCurrentDayMinusDayX','Find-DatesCurrentDayMinuxDaysX','Find-DatesCurrentHour','Find-DatesDayPrevious','Find-DatesDayToday','Find-DatesMonthCurrent','Find-DatesMonthPast','Find-DatesPastHour','Find-DatesPastWeek','Find-DatesQuarterCurrent','Find-DatesQuarterLast','Find-MyProgramData','Find-TypesNeeded','Format-AddSpaceToSentence','Format-FirstXChars','Format-PSTable','Format-Stream','Format-ToTitleCase','Format-TransposeTable','Format-Verbose','Get-CommandInfo','Get-FileInformation','Get-FileName','Get-FilesInFolder','Get-FileSize','Get-HashMaxValue','Get-HTML','Get-Logger','Get-MimeType','Get-ModulesAvailability','Get-MyIP','Get-ObjectCount','Get-ObjectData','Get-ObjectKeys','Get-ObjectProperties','Get-ObjectPropertiesAdvanced','Get-ObjectTitles','Get-ObjectType','Get-PathSeparator','Get-PathTemporary','Get-RandomCharacters','Get-RandomPassword','Get-RandomStringName','Get-SqlQueryColumnInformation','Get-TimeSynchronization','Get-TimeZoneAdvanced','Get-TimeZoneLegacy','Get-Types','Get-WinADForestControllers','Get-WinADOrganizationalUnitData','Get-WinADOrganizationalUnitFromDN','Get-WinADUsersByDN','Get-WinADUsersByOU','Get-WinADUserSnapshot','Merge-Objects','New-ArrayList','New-GenericList','New-Runspace','New-SqlQuery','New-SqlQueryAlterTable','New-SqlQueryCreateTable','New-SqlTableMapping','New-UserAdd','Remove-DuplicateObjects','Remove-FromArray','Remove-ObjectsExistingInTarget','Remove-WhiteSpace','Remove-WinADUserGroups','Rename-UserValuesFromHash','Request-Credentials','Save-XML','Search-Command','Send-Email','Send-SqlInsert','Set-DnsServerIpAddress','Set-EmailBody','Set-EmailBodyPreparedTable','Set-EmailBodyReplacement','Set-EmailBodyReplacementTable','Set-EmailFormatting','Set-EmailHead','Set-EmailReportBranding','Set-EmailWordReplacements','Set-EmailWordReplacementsHash','Set-SpecUser','Set-TimeSynchronization','Set-WinADGroupSynchronization','Set-WinADUserFields','Set-WinADUserSettingGAL','Set-WinADUserStatus','Set-WinAzureADUserField','Set-WinAzureADUserLicense','Set-WinAzureADUserStatus','Set-XML','Show-Array','Show-DataInVerbose','Show-TableVisualization','Split-Array','Split-ArrayEvery','Start-MyProgram','Start-Runspace','Start-TimeLog','Stop-Runspace','Stop-TimeLog','Test-AvailabilityCommands','Test-ComputerAvailability','Test-ComputerPort','Test-ConfigurationCredentials','Test-ForestConnectivity','Test-Key','Test-ModuleAvailability','Test-WinRM') ` -Alias @('Add-ADUserGroups','Format-ListStream','Format-TableStream','fs','FV','Get-ADUserSnapshot','Get-WinADDomainControllers','Remove-ADUserGroups','Set-ADUserName','Set-ADUserSettingGAL','Set-ADUserStatus','Set-EmailBodyTableReplacement') |