PSSharedGoods.psm1
function Get-RequiredModule { [cmdletbinding()] param( [string] $Path, [string] $Name ) $PrimaryModule = Get-ChildItem -LiteralPath "$Path\$Name" -Filter '*.psd1' -Recurse -ErrorAction SilentlyContinue -Depth 1 if ($PrimaryModule) { $Module = Get-Module -ListAvailable $PrimaryModule.FullName -ErrorAction SilentlyContinue -Verbose:$false if ($Module) { [Array] $RequiredModules = $Module.RequiredModules.Name if ($null -ne $RequiredModules) { $null } $RequiredModules foreach ($_ in $RequiredModules) { Get-RequiredModule -Path $Path -Name $_ } } } else { Write-Warning "Initialize-ModulePortable - Modules to load not found in $Path" } } function Write-Color { <# .SYNOPSIS Write-Color is a wrapper around Write-Host delivering a lot of additional features for easier color options. .DESCRIPTION Write-Color is a wrapper around Write-Host delivering a lot of additional features for easier color options. It provides: - Easy manipulation of colors, - Logging output to file (log) - Nice formatting options out of the box. - Ability to use aliases for parameters .PARAMETER Text Text to display on screen and write to log file if specified. Accepts an array of strings. .PARAMETER Color Color of the text. Accepts an array of colors. If more than one color is specified it will loop through colors for each string. If there are more strings than colors it will start from the beginning. Available colors are: Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White .PARAMETER BackGroundColor Color of the background. Accepts an array of colors. If more than one color is specified it will loop through colors for each string. If there are more strings than colors it will start from the beginning. Available colors are: Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White .PARAMETER StartTab Number of tabs to add before text. Default is 0. .PARAMETER LinesBefore Number of empty lines before text. Default is 0. .PARAMETER LinesAfter Number of empty lines after text. Default is 0. .PARAMETER StartSpaces Number of spaces to add before text. Default is 0. .PARAMETER LogFile Path to log file. If not specified no log file will be created. .PARAMETER DateTimeFormat Custom date and time format string. Default is yyyy-MM-dd HH:mm:ss .PARAMETER LogTime If set to $true it will add time to log file. Default is $true. .PARAMETER LogRetry Number of retries to write to log file, in case it can't write to it for some reason, before skipping. Default is 2. .PARAMETER Encoding Encoding of the log file. Default is Unicode. .PARAMETER ShowTime Switch to add time to console output. Default is not set. .PARAMETER NoNewLine Switch to not add new line at the end of the output. Default is not set. .PARAMETER NoConsoleOutput Switch to not output to console. Default all output goes to console. .EXAMPLE Write-Color -Text "Red ", "Green ", "Yellow " -Color Red,Green,Yellow .EXAMPLE Write-Color -Text "This is text in Green ", "followed by red ", "and then we have Magenta... ", "isn't it fun? ", "Here goes DarkCyan" -Color Green,Red,Magenta,White,DarkCyan .EXAMPLE Write-Color -Text "This is text in Green ", "followed by red ", "and then we have Magenta... ", "isn't it fun? ", "Here goes DarkCyan" -Color Green,Red,Magenta,White,DarkCyan -StartTab 3 -LinesBefore 1 -LinesAfter 1 .EXAMPLE Write-Color "1. ", "Option 1" -Color Yellow, Green Write-Color "2. ", "Option 2" -Color Yellow, Green Write-Color "3. ", "Option 3" -Color Yellow, Green Write-Color "4. ", "Option 4" -Color Yellow, Green Write-Color "9. ", "Press 9 to exit" -Color Yellow, Gray -LinesBefore 1 .EXAMPLE Write-Color -LinesBefore 2 -Text "This little ","message is ", "written to log ", "file as well." ` -Color Yellow, White, Green, Red, Red -LogFile "C:\testing.txt" -TimeFormat "yyyy-MM-dd HH:mm:ss" Write-Color -Text "This can get ","handy if ", "want to display things, and log actions to file ", "at the same time." ` -Color Yellow, White, Green, Red, Red -LogFile "C:\testing.txt" .EXAMPLE Write-Color -T "My text", " is ", "all colorful" -C Yellow, Red, Green -B Green, Green, Yellow Write-Color -t "my text" -c yellow -b green Write-Color -text "my text" -c red .EXAMPLE Write-Color -Text "Testuję czy się ładnie zapisze, czy będą problemy" -Encoding unicode -LogFile 'C:\temp\testinggg.txt' -Color Red -NoConsoleOutput .NOTES Understanding Custom date and time format strings: https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings Project support: https://github.com/EvotecIT/PSWriteColor Original idea: Josh (https://stackoverflow.com/users/81769/josh) #> [alias('Write-Colour')] [CmdletBinding()] param ( [alias ('T')] [String[]]$Text, [alias ('C', 'ForegroundColor', 'FGC')] [ConsoleColor[]]$Color = [ConsoleColor]::White, [alias ('B', 'BGC')] [ConsoleColor[]]$BackGroundColor = $null, [alias ('Indent')][int] $StartTab = 0, [int] $LinesBefore = 0, [int] $LinesAfter = 0, [int] $StartSpaces = 0, [alias ('L')] [string] $LogFile = '', [Alias('DateFormat', 'TimeFormat')][string] $DateTimeFormat = 'yyyy-MM-dd HH:mm:ss', [alias ('LogTimeStamp')][bool] $LogTime = $true, [int] $LogRetry = 2, [ValidateSet('unknown', 'string', 'unicode', 'bigendianunicode', 'utf8', 'utf7', 'utf32', 'ascii', 'default', 'oem')][string]$Encoding = 'Unicode', [switch] $ShowTime, [switch] $NoNewLine, [alias('HideConsole')][switch] $NoConsoleOutput ) if (-not $NoConsoleOutput) { $DefaultColor = $Color[0] if ($null -ne $BackGroundColor -and $BackGroundColor.Count -ne $Color.Count) { Write-Error "Colors, BackGroundColors parameters count doesn't match. Terminated." return } if ($LinesBefore -ne 0) { for ($i = 0; $i -lt $LinesBefore; $i++) { Write-Host -Object "`n" -NoNewline } } # Add empty line before if ($StartTab -ne 0) { for ($i = 0; $i -lt $StartTab; $i++) { Write-Host -Object "`t" -NoNewline } } # Add TABS before text if ($StartSpaces -ne 0) { for ($i = 0; $i -lt $StartSpaces; $i++) { Write-Host -Object ' ' -NoNewline } } # Add SPACES before text if ($ShowTime) { Write-Host -Object "[$([datetime]::Now.ToString($DateTimeFormat))] " -NoNewline } # Add Time before output if ($Text.Count -ne 0) { if ($Color.Count -ge $Text.Count) { # the real deal coloring if ($null -eq $BackGroundColor) { for ($i = 0; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -NoNewline } } else { for ($i = 0; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackGroundColor[$i] -NoNewline } } } else { if ($null -eq $BackGroundColor) { for ($i = 0; $i -lt $Color.Length ; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -NoNewline } for ($i = $Color.Length; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $DefaultColor -NoNewline } } else { for ($i = 0; $i -lt $Color.Length ; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackGroundColor[$i] -NoNewline } for ($i = $Color.Length; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $DefaultColor -BackgroundColor $BackGroundColor[0] -NoNewline } } } } if ($NoNewLine -eq $true) { Write-Host -NoNewline } else { Write-Host } # Support for no new line if ($LinesAfter -ne 0) { for ($i = 0; $i -lt $LinesAfter; $i++) { Write-Host -Object "`n" -NoNewline } } # Add empty line after } if ($Text.Count -and $LogFile) { # Save to file $TextToFile = "" for ($i = 0; $i -lt $Text.Length; $i++) { $TextToFile += $Text[$i] } $Saved = $false $Retry = 0 Do { $Retry++ try { if ($LogTime) { "[$([datetime]::Now.ToString($DateTimeFormat))] $TextToFile" | Out-File -FilePath $LogFile -Encoding $Encoding -Append -ErrorAction Stop -WhatIf:$false } else { "$TextToFile" | Out-File -FilePath $LogFile -Encoding $Encoding -Append -ErrorAction Stop -WhatIf:$false } $Saved = $true } catch { if ($Saved -eq $false -and $Retry -eq $LogRetry) { Write-Warning "Write-Color - Couldn't write to log file $($_.Exception.Message). Tried ($Retry/$LogRetry))" } else { Write-Warning "Write-Color - Couldn't write to log file $($_.Exception.Message). Retrying... ($Retry/$LogRetry)" } } } Until ($Saved -eq $true -or $Retry -ge $LogRetry) } } function Get-PSRegistryDictionaries { [cmdletBinding()] param() if ($Script:Dictionary) { return } $Script:Dictionary = @{ # Those don't really exists, but we want to allow targetting all users or default users 'HKUAD:' = 'HKEY_ALL_USERS_DEFAULT' # All users in HKEY_USERS including .DEFAULT_USER (auto mapped from NTUSER.DAT) 'HKUA:' = 'HKEY_ALL_USERS' # All users in HKEY_USERS excluding .DEFAULT / .DEFAULT_USER (auto mapped from NTUSER.DAT) 'HKUD:' = 'HKEY_DEFAULT_USER' # DEFAULT_USER user in HKEY_USERS (auto mapped from NTUSER.DAT) 'HKUDUD:' = 'HKEY_ALL_DOMAIN_USERS_DEFAULT' # All users in HKEY_USERS that are non SPECIAL accounts including .DEFAULT_USER (auto mapped from NTUSER.DAT) # All users in HKEY_USERS that are non SPECIAL accounts excluding .DEFAULT / .DEFAULT_USER (auto mapped from NTUSER.DAT) 'HKUDU:' = 'HKEY_ALL_DOMAIN_USERS' # All users in HKEY_USERS that are non SPECIAL accounts excluding .DEFAULT / .DEFAULT_USER (auto mapped from NTUSER.DAT) # But also including non-loaded users (not logged in) (auto mapped from NTUSER.DAT) 'HKUDUO:' = 'HKEY_ALL_DOMAIN_USERS_OTHER' # All users in HKEY_USERS that are non SPECIAL accounts including .DEFAULT_USER (auto mapped from NTUSER.DAT) # But also including non-loaded users (not logged in) (auto mapped from NTUSER.DAT) 'HKUDUDO:' = 'HKEY_ALL_DOMAIN_USERS_OTHER_DEFAULT' # order matters 'HKCR:' = 'HKEY_CLASSES_ROOT' 'HKCU:' = 'HKEY_CURRENT_USER' 'HKLM:' = 'HKEY_LOCAL_MACHINE' 'HKU:' = 'HKEY_USERS' 'HKCC:' = 'HKEY_CURRENT_CONFIG' 'HKDD:' = 'HKEY_DYN_DATA' 'HKPD:' = 'HKEY_PERFORMANCE_DATA' } $Script:HiveDictionary = [ordered] @{ # Those don't really exists, but we want to allow targetting all users or default users # The order matters 'HKEY_ALL_USERS_DEFAULT' = 'All+Default' 'HKUAD' = 'All+Default' 'HKEY_ALL_USERS' = 'All' 'HKUA' = 'All' 'HKEY_ALL_DOMAIN_USERS_DEFAULT' = 'AllDomain+Default' 'HKUDUD' = 'AllDomain+Default' 'HKEY_ALL_DOMAIN_USERS' = 'AllDomain' 'HKUDU' = 'AllDomain' 'HKEY_DEFAULT_USER' = 'Default' 'HKUD' = 'Default' 'HKEY_ALL_DOMAIN_USERS_OTHER' = 'AllDomain+Other' 'HKUDUO' = 'AllDomain+Other' 'HKUDUDO' = 'AllDomain+Other+Default' 'HKEY_ALL_DOMAIN_USERS_OTHER_DEFAULT' = 'AllDomain+Other+Default' # Those exists 'HKEY_CLASSES_ROOT' = 'ClassesRoot' 'HKCR' = 'ClassesRoot' 'ClassesRoot' = 'ClassesRoot' 'HKCU' = 'CurrentUser' 'HKEY_CURRENT_USER' = 'CurrentUser' 'CurrentUser' = 'CurrentUser' 'HKLM' = 'LocalMachine' 'HKEY_LOCAL_MACHINE' = 'LocalMachine' 'LocalMachine' = 'LocalMachine' 'HKU' = 'Users' 'HKEY_USERS' = 'Users' 'Users' = 'Users' 'HKCC' = 'CurrentConfig' 'HKEY_CURRENT_CONFIG' = 'CurrentConfig' 'CurrentConfig' = 'CurrentConfig' 'HKDD' = 'DynData' 'HKEY_DYN_DATA' = 'DynData' 'DynData' = 'DynData' 'HKPD' = 'PerformanceData' 'HKEY_PERFORMANCE_DATA ' = 'PerformanceData' 'PerformanceData' = 'PerformanceData' } $Script:ReverseTypesDictionary = [ordered] @{ 'REG_SZ' = 'string' 'REG_NONE' = 'none' 'REG_EXPAND_SZ' = 'expandstring' 'REG_BINARY' = 'binary' 'REG_DWORD' = 'dword' 'REG_MULTI_SZ' = 'multistring' 'REG_QWORD' = 'qword' 'string' = 'string' 'expandstring' = 'expandstring' 'binary' = 'binary' 'dword' = 'dword' 'multistring' = 'multistring' 'qword' = 'qword' 'none' = 'none' } } $Script:RGBColors = [ordered] @{ None = $null AirForceBlue = 93, 138, 168 Akaroa = 195, 176, 145 AlbescentWhite = 227, 218, 201 AliceBlue = 240, 248, 255 Alizarin = 227, 38, 54 Allports = 18, 97, 128 Almond = 239, 222, 205 AlmondFrost = 159, 129, 112 Amaranth = 229, 43, 80 Amazon = 59, 122, 87 Amber = 255, 191, 0 Amethyst = 153, 102, 204 AmethystSmoke = 156, 138, 164 AntiqueWhite = 250, 235, 215 Apple = 102, 180, 71 AppleBlossom = 176, 92, 82 Apricot = 251, 206, 177 Aqua = 0, 255, 255 Aquamarine = 127, 255, 212 Armygreen = 75, 83, 32 Arsenic = 59, 68, 75 Astral = 54, 117, 136 Atlantis = 164, 198, 57 Atomic = 65, 74, 76 AtomicTangerine = 255, 153, 102 Axolotl = 99, 119, 91 Azure = 240, 255, 255 Bahia = 176, 191, 26 BakersChocolate = 93, 58, 26 BaliHai = 124, 152, 171 BananaMania = 250, 231, 181 BattleshipGrey = 85, 93, 80 BayOfMany = 35, 48, 103 Beige = 245, 245, 220 Bermuda = 136, 216, 192 Bilbao = 42, 128, 0 BilobaFlower = 181, 126, 220 Bismark = 83, 104, 114 Bisque = 255, 228, 196 Bistre = 61, 43, 31 Bittersweet = 254, 111, 94 Black = 0, 0, 0 BlackPearl = 31, 38, 42 BlackRose = 85, 31, 47 BlackRussian = 23, 24, 43 BlanchedAlmond = 255, 235, 205 BlizzardBlue = 172, 229, 238 Blue = 0, 0, 255 BlueDiamond = 77, 26, 127 BlueMarguerite = 115, 102, 189 BlueSmoke = 115, 130, 118 BlueViolet = 138, 43, 226 Blush = 169, 92, 104 BokaraGrey = 22, 17, 13 Bole = 121, 68, 59 BondiBlue = 0, 147, 175 Bordeaux = 88, 17, 26 Bossanova = 86, 60, 92 Boulder = 114, 116, 114 Bouquet = 183, 132, 167 Bourbon = 170, 108, 57 Brass = 181, 166, 66 BrickRed = 199, 44, 72 BrightGreen = 102, 255, 0 BrightRed = 146, 43, 62 BrightTurquoise = 8, 232, 222 BrilliantRose = 243, 100, 162 BrinkPink = 250, 110, 121 BritishRacingGreen = 0, 66, 37 Bronze = 205, 127, 50 Brown = 165, 42, 42 BrownPod = 57, 24, 2 BuddhaGold = 202, 169, 6 Buff = 240, 220, 130 Burgundy = 128, 0, 32 BurlyWood = 222, 184, 135 BurntOrange = 255, 117, 56 BurntSienna = 233, 116, 81 BurntUmber = 138, 51, 36 ButteredRum = 156, 124, 56 CadetBlue = 95, 158, 160 California = 224, 141, 60 CamouflageGreen = 120, 134, 107 Canary = 255, 255, 153 CanCan = 217, 134, 149 CannonPink = 145, 78, 117 CaputMortuum = 89, 39, 32 Caramel = 255, 213, 154 Cararra = 237, 230, 214 Cardinal = 179, 33, 52 CardinGreen = 18, 53, 36 CareysPink = 217, 152, 160 CaribbeanGreen = 0, 222, 164 Carmine = 175, 0, 42 CarnationPink = 255, 166, 201 CarrotOrange = 242, 142, 28 Cascade = 141, 163, 153 CatskillWhite = 226, 229, 222 Cedar = 67, 48, 46 Celadon = 172, 225, 175 Celeste = 207, 207, 196 Cello = 55, 79, 107 Cement = 138, 121, 93 Cerise = 222, 49, 99 Cerulean = 0, 123, 167 CeruleanBlue = 42, 82, 190 Chantilly = 239, 187, 204 Chardonnay = 255, 200, 124 Charlotte = 167, 216, 222 Charm = 208, 116, 139 Chartreuse = 127, 255, 0 ChartreuseYellow = 223, 255, 0 ChelseaCucumber = 135, 169, 107 Cherub = 246, 214, 222 Chestnut = 185, 78, 72 ChileanFire = 226, 88, 34 Chinook = 150, 200, 162 Chocolate = 210, 105, 30 Christi = 125, 183, 0 Christine = 181, 101, 30 Cinnabar = 235, 76, 66 Citron = 159, 169, 31 Citrus = 141, 182, 0 Claret = 95, 25, 51 ClassicRose = 251, 204, 231 ClayCreek = 145, 129, 81 Clinker = 75, 54, 33 Clover = 74, 93, 35 Cobalt = 0, 71, 171 CocoaBrown = 44, 22, 8 Cola = 60, 48, 36 ColumbiaBlue = 166, 231, 255 CongoBrown = 103, 76, 71 Conifer = 178, 236, 93 Copper = 218, 138, 103 CopperRose = 153, 102, 102 Coral = 255, 127, 80 CoralRed = 255, 64, 64 CoralTree = 173, 111, 105 Coriander = 188, 184, 138 Corn = 251, 236, 93 CornField = 250, 240, 190 Cornflower = 147, 204, 234 CornflowerBlue = 100, 149, 237 Cornsilk = 255, 248, 220 Cosmic = 132, 63, 91 Cosmos = 255, 204, 203 CostaDelSol = 102, 93, 30 CottonCandy = 255, 188, 217 Crail = 164, 90, 82 Cranberry = 205, 96, 126 Cream = 255, 255, 204 CreamCan = 242, 198, 73 Crimson = 220, 20, 60 Crusta = 232, 142, 90 Cumulus = 255, 255, 191 Cupid = 246, 173, 198 CuriousBlue = 40, 135, 200 Cyan = 0, 255, 255 Cyprus = 6, 78, 64 DaisyBush = 85, 53, 146 Dandelion = 250, 218, 94 Danube = 96, 130, 182 DarkBlue = 0, 0, 139 DarkBrown = 101, 67, 33 DarkCerulean = 8, 69, 126 DarkChestnut = 152, 105, 96 DarkCoral = 201, 90, 73 DarkCyan = 0, 139, 139 DarkGoldenrod = 184, 134, 11 DarkGray = 169, 169, 169 DarkGreen = 0, 100, 0 DarkGreenCopper = 73, 121, 107 DarkGrey = 169, 169, 169 DarkKhaki = 189, 183, 107 DarkMagenta = 139, 0, 139 DarkOliveGreen = 85, 107, 47 DarkOrange = 255, 140, 0 DarkOrchid = 153, 50, 204 DarkPastelGreen = 3, 192, 60 DarkPink = 222, 93, 131 DarkPurple = 150, 61, 127 DarkRed = 139, 0, 0 DarkSalmon = 233, 150, 122 DarkSeaGreen = 143, 188, 143 DarkSlateBlue = 72, 61, 139 DarkSlateGray = 47, 79, 79 DarkSlateGrey = 47, 79, 79 DarkSpringGreen = 23, 114, 69 DarkTangerine = 255, 170, 29 DarkTurquoise = 0, 206, 209 DarkViolet = 148, 0, 211 DarkWood = 130, 102, 68 DeepBlush = 245, 105, 145 DeepCerise = 224, 33, 138 DeepKoamaru = 51, 51, 102 DeepLilac = 153, 85, 187 DeepMagenta = 204, 0, 204 DeepPink = 255, 20, 147 DeepSea = 14, 124, 97 DeepSkyBlue = 0, 191, 255 DeepTeal = 24, 69, 59 Denim = 36, 107, 206 DesertSand = 237, 201, 175 DimGray = 105, 105, 105 DimGrey = 105, 105, 105 DodgerBlue = 30, 144, 255 Dolly = 242, 242, 122 Downy = 95, 201, 191 DutchWhite = 239, 223, 187 EastBay = 76, 81, 109 EastSide = 178, 132, 190 EchoBlue = 169, 178, 195 Ecru = 194, 178, 128 Eggplant = 162, 0, 109 EgyptianBlue = 16, 52, 166 ElectricBlue = 125, 249, 255 ElectricIndigo = 111, 0, 255 ElectricLime = 208, 255, 20 ElectricPurple = 191, 0, 255 Elm = 47, 132, 124 Emerald = 80, 200, 120 Eminence = 108, 48, 130 Endeavour = 46, 88, 148 EnergyYellow = 245, 224, 80 Espresso = 74, 44, 42 Eucalyptus = 26, 162, 96 Falcon = 126, 94, 96 Fallow = 204, 153, 102 FaluRed = 128, 24, 24 Feldgrau = 77, 93, 83 Feldspar = 205, 149, 117 Fern = 113, 188, 120 FernGreen = 79, 121, 66 Festival = 236, 213, 64 Finn = 97, 64, 81 FireBrick = 178, 34, 34 FireBush = 222, 143, 78 FireEngineRed = 211, 33, 45 Flamingo = 233, 92, 75 Flax = 238, 220, 130 FloralWhite = 255, 250, 240 ForestGreen = 34, 139, 34 Frangipani = 250, 214, 165 FreeSpeechAquamarine = 0, 168, 119 FreeSpeechRed = 204, 0, 0 FrenchLilac = 230, 168, 215 FrenchRose = 232, 83, 149 FriarGrey = 135, 134, 129 Froly = 228, 113, 122 Fuchsia = 255, 0, 255 FuchsiaPink = 255, 119, 255 Gainsboro = 220, 220, 220 Gallery = 219, 215, 210 Galliano = 204, 160, 29 Gamboge = 204, 153, 0 Ghost = 196, 195, 208 GhostWhite = 248, 248, 255 Gin = 216, 228, 188 GinFizz = 247, 231, 206 Givry = 230, 208, 171 Glacier = 115, 169, 194 Gold = 255, 215, 0 GoldDrop = 213, 108, 43 GoldenBrown = 150, 113, 23 GoldenFizz = 240, 225, 48 GoldenGlow = 248, 222, 126 GoldenPoppy = 252, 194, 0 Goldenrod = 218, 165, 32 GoldenSand = 233, 214, 107 GoldenYellow = 253, 238, 0 GoldTips = 225, 189, 39 GordonsGreen = 37, 53, 41 Gorse = 255, 225, 53 Gossamer = 49, 145, 119 GrannySmithApple = 168, 228, 160 Gray = 128, 128, 128 GrayAsparagus = 70, 89, 69 Green = 0, 128, 0 GreenLeaf = 76, 114, 29 GreenVogue = 38, 67, 72 GreenYellow = 173, 255, 47 Grey = 128, 128, 128 GreyAsparagus = 70, 89, 69 GuardsmanRed = 157, 41, 51 GumLeaf = 178, 190, 181 Gunmetal = 42, 52, 57 Hacienda = 155, 135, 12 HalfAndHalf = 232, 228, 201 HalfBaked = 95, 138, 139 HalfColonialWhite = 246, 234, 190 HalfPearlLusta = 240, 234, 214 HanPurple = 63, 0, 255 Harlequin = 74, 255, 0 HarleyDavidsonOrange = 194, 59, 34 Heather = 174, 198, 207 Heliotrope = 223, 115, 255 Hemp = 161, 122, 116 Highball = 134, 126, 54 HippiePink = 171, 75, 82 Hoki = 110, 127, 128 HollywoodCerise = 244, 0, 161 Honeydew = 240, 255, 240 Hopbush = 207, 113, 175 HorsesNeck = 108, 84, 30 HotPink = 255, 105, 180 HummingBird = 201, 255, 229 HunterGreen = 53, 94, 59 Illusion = 244, 152, 173 InchWorm = 202, 224, 13 IndianRed = 205, 92, 92 Indigo = 75, 0, 130 InternationalKleinBlue = 0, 24, 168 InternationalOrange = 255, 79, 0 IrisBlue = 28, 169, 201 IrishCoffee = 102, 66, 40 IronsideGrey = 113, 112, 110 IslamicGreen = 0, 144, 0 Ivory = 255, 255, 240 Jacarta = 61, 50, 93 JackoBean = 65, 54, 40 JacksonsPurple = 46, 45, 136 Jade = 0, 171, 102 JapaneseLaurel = 47, 117, 50 Jazz = 93, 43, 44 JazzberryJam = 165, 11, 94 JellyBean = 68, 121, 142 JetStream = 187, 208, 201 Jewel = 0, 107, 60 Jon = 79, 58, 60 JordyBlue = 124, 185, 232 Jumbo = 132, 132, 130 JungleGreen = 41, 171, 135 KaitokeGreen = 30, 77, 43 Karry = 255, 221, 202 KellyGreen = 70, 203, 24 Keppel = 93, 164, 147 Khaki = 240, 230, 140 Killarney = 77, 140, 87 KingfisherDaisy = 85, 27, 140 Kobi = 230, 143, 172 LaPalma = 60, 141, 13 LaserLemon = 252, 247, 94 Laurel = 103, 146, 103 Lavender = 230, 230, 250 LavenderBlue = 204, 204, 255 LavenderBlush = 255, 240, 245 LavenderPink = 251, 174, 210 LavenderRose = 251, 160, 227 LawnGreen = 124, 252, 0 LemonChiffon = 255, 250, 205 LightBlue = 173, 216, 230 LightCoral = 240, 128, 128 LightCyan = 224, 255, 255 LightGoldenrodYellow = 250, 250, 210 LightGray = 211, 211, 211 LightGreen = 144, 238, 144 LightGrey = 211, 211, 211 LightPink = 255, 182, 193 LightSalmon = 255, 160, 122 LightSeaGreen = 32, 178, 170 LightSkyBlue = 135, 206, 250 LightSlateGray = 119, 136, 153 LightSlateGrey = 119, 136, 153 LightSteelBlue = 176, 196, 222 LightYellow = 255, 255, 224 Lilac = 204, 153, 204 Lime = 0, 255, 0 LimeGreen = 50, 205, 50 Limerick = 139, 190, 27 Linen = 250, 240, 230 Lipstick = 159, 43, 104 Liver = 83, 75, 79 Lochinvar = 86, 136, 125 Lochmara = 38, 97, 156 Lola = 179, 158, 181 LondonHue = 170, 152, 169 Lotus = 124, 72, 72 LuckyPoint = 29, 41, 81 MacaroniAndCheese = 255, 189, 136 Madang = 193, 249, 162 Madras = 81, 65, 0 Magenta = 255, 0, 255 MagicMint = 170, 240, 209 Magnolia = 248, 244, 255 Mahogany = 215, 59, 62 Maire = 27, 24, 17 Maize = 230, 190, 138 Malachite = 11, 218, 81 Malibu = 93, 173, 236 Malta = 169, 154, 134 Manatee = 140, 146, 172 Mandalay = 176, 121, 57 MandarianOrange = 146, 39, 36 Mandy = 191, 79, 81 Manhattan = 229, 170, 112 Mantis = 125, 194, 66 Manz = 217, 230, 80 MardiGras = 48, 25, 52 Mariner = 57, 86, 156 Maroon = 128, 0, 0 Matterhorn = 85, 85, 85 Mauve = 244, 187, 255 Mauvelous = 255, 145, 175 MauveTaupe = 143, 89, 115 MayaBlue = 119, 181, 254 McKenzie = 129, 97, 60 MediumAquamarine = 102, 205, 170 MediumBlue = 0, 0, 205 MediumCarmine = 175, 64, 53 MediumOrchid = 186, 85, 211 MediumPurple = 147, 112, 219 MediumRedViolet = 189, 51, 164 MediumSeaGreen = 60, 179, 113 MediumSlateBlue = 123, 104, 238 MediumSpringGreen = 0, 250, 154 MediumTurquoise = 72, 209, 204 MediumVioletRed = 199, 21, 133 MediumWood = 166, 123, 91 Melon = 253, 188, 180 Merlot = 112, 54, 66 MetallicGold = 211, 175, 55 Meteor = 184, 115, 51 MidnightBlue = 25, 25, 112 MidnightExpress = 0, 20, 64 Mikado = 60, 52, 31 MilanoRed = 168, 55, 49 Ming = 54, 116, 125 MintCream = 245, 255, 250 MintGreen = 152, 255, 152 Mischka = 168, 169, 173 MistyRose = 255, 228, 225 Moccasin = 255, 228, 181 Mojo = 149, 69, 53 MonaLisa = 255, 153, 153 Mongoose = 179, 139, 109 Montana = 53, 56, 57 MoodyBlue = 116, 108, 192 MoonYellow = 245, 199, 26 MossGreen = 173, 223, 173 MountainMeadow = 28, 172, 120 MountainMist = 161, 157, 148 MountbattenPink = 153, 122, 141 Mulberry = 211, 65, 157 Mustard = 255, 219, 88 Myrtle = 25, 89, 5 MySin = 255, 179, 71 NavajoWhite = 255, 222, 173 Navy = 0, 0, 128 NavyBlue = 2, 71, 254 NeonCarrot = 255, 153, 51 NeonPink = 255, 92, 205 Nepal = 145, 163, 176 Nero = 20, 20, 20 NewMidnightBlue = 0, 0, 156 Niagara = 58, 176, 158 NightRider = 59, 47, 47 Nobel = 152, 152, 152 Norway = 169, 186, 157 Nugget = 183, 135, 39 OceanGreen = 95, 167, 120 Ochre = 202, 115, 9 OldCopper = 111, 78, 55 OldGold = 207, 181, 59 OldLace = 253, 245, 230 OldLavender = 121, 104, 120 OldRose = 195, 33, 72 Olive = 128, 128, 0 OliveDrab = 107, 142, 35 OliveGreen = 181, 179, 92 Olivetone = 110, 110, 48 Olivine = 154, 185, 115 Onahau = 196, 216, 226 Opal = 168, 195, 188 Orange = 255, 165, 0 OrangePeel = 251, 153, 2 OrangeRed = 255, 69, 0 Orchid = 218, 112, 214 OuterSpace = 45, 56, 58 OutrageousOrange = 254, 90, 29 Oxley = 95, 167, 119 PacificBlue = 0, 136, 220 Padua = 128, 193, 151 PalatinatePurple = 112, 41, 99 PaleBrown = 160, 120, 90 PaleChestnut = 221, 173, 175 PaleCornflowerBlue = 188, 212, 230 PaleGoldenrod = 238, 232, 170 PaleGreen = 152, 251, 152 PaleMagenta = 249, 132, 239 PalePink = 250, 218, 221 PaleSlate = 201, 192, 187 PaleTaupe = 188, 152, 126 PaleTurquoise = 175, 238, 238 PaleVioletRed = 219, 112, 147 PalmLeaf = 53, 66, 48 Panache = 233, 255, 219 PapayaWhip = 255, 239, 213 ParisDaisy = 255, 244, 79 Parsley = 48, 96, 48 PastelGreen = 119, 221, 119 PattensBlue = 219, 233, 244 Peach = 255, 203, 164 PeachOrange = 255, 204, 153 PeachPuff = 255, 218, 185 PeachYellow = 250, 223, 173 Pear = 209, 226, 49 PearlLusta = 234, 224, 200 Pelorous = 42, 143, 189 Perano = 172, 172, 230 Periwinkle = 197, 203, 225 PersianBlue = 34, 67, 182 PersianGreen = 0, 166, 147 PersianIndigo = 51, 0, 102 PersianPink = 247, 127, 190 PersianRed = 192, 54, 44 PersianRose = 233, 54, 167 Persimmon = 236, 88, 0 Peru = 205, 133, 63 Pesto = 128, 117, 50 PictonBlue = 102, 153, 204 PigmentGreen = 0, 173, 67 PigPink = 255, 218, 233 PineGreen = 1, 121, 111 PineTree = 42, 47, 35 Pink = 255, 192, 203 PinkFlare = 191, 175, 178 PinkLace = 240, 211, 220 PinkSwan = 179, 179, 179 Plum = 221, 160, 221 Pohutukawa = 102, 12, 33 PoloBlue = 119, 158, 203 Pompadour = 129, 20, 83 Portage = 146, 161, 207 PotPourri = 241, 221, 207 PottersClay = 132, 86, 60 PowderBlue = 176, 224, 230 Prim = 228, 196, 207 PrussianBlue = 0, 58, 108 PsychedelicPurple = 223, 0, 255 Puce = 204, 136, 153 Pueblo = 108, 46, 31 PuertoRico = 67, 179, 174 Pumpkin = 255, 99, 28 Purple = 128, 0, 128 PurpleMountainsMajesty = 150, 123, 182 PurpleTaupe = 93, 57, 84 QuarterSpanishWhite = 230, 224, 212 Quartz = 220, 208, 255 Quincy = 106, 84, 69 RacingGreen = 26, 36, 33 RadicalRed = 255, 32, 82 Rajah = 251, 171, 96 RawUmber = 123, 63, 0 RazzleDazzleRose = 254, 78, 218 Razzmatazz = 215, 10, 83 Red = 255, 0, 0 RedBerry = 132, 22, 23 RedDamask = 203, 109, 81 RedOxide = 99, 15, 15 RedRobin = 128, 64, 64 RichBlue = 84, 90, 167 Riptide = 141, 217, 204 RobinsEggBlue = 0, 204, 204 RobRoy = 225, 169, 95 RockSpray = 171, 56, 31 RomanCoffee = 131, 105, 83 RoseBud = 246, 164, 148 RoseBudCherry = 135, 50, 96 RoseTaupe = 144, 93, 93 RosyBrown = 188, 143, 143 Rouge = 176, 48, 96 RoyalBlue = 65, 105, 225 RoyalHeath = 168, 81, 110 RoyalPurple = 102, 51, 152 Ruby = 215, 24, 104 Russet = 128, 70, 27 Rust = 192, 64, 0 RusticRed = 72, 6, 7 Saddle = 99, 81, 71 SaddleBrown = 139, 69, 19 SafetyOrange = 255, 102, 0 Saffron = 244, 196, 48 Sage = 143, 151, 121 Sail = 161, 202, 241 Salem = 0, 133, 67 Salmon = 250, 128, 114 SandyBeach = 253, 213, 177 SandyBrown = 244, 164, 96 Sangria = 134, 1, 17 SanguineBrown = 115, 54, 53 SanMarino = 80, 114, 167 SanteFe = 175, 110, 77 Sapphire = 6, 42, 120 Saratoga = 84, 90, 44 Scampi = 102, 102, 153 Scarlet = 255, 36, 0 ScarletGum = 67, 28, 83 SchoolBusYellow = 255, 216, 0 Schooner = 139, 134, 128 ScreaminGreen = 102, 255, 102 Scrub = 59, 60, 54 SeaBuckthorn = 249, 146, 69 SeaGreen = 46, 139, 87 Seagull = 140, 190, 214 SealBrown = 61, 12, 2 Seance = 96, 47, 107 SeaPink = 215, 131, 127 SeaShell = 255, 245, 238 Selago = 250, 230, 250 SelectiveYellow = 242, 180, 0 SemiSweetChocolate = 107, 68, 35 Sepia = 150, 90, 62 Serenade = 255, 233, 209 Shadow = 133, 109, 77 Shakespeare = 114, 160, 193 Shalimar = 252, 255, 164 Shamrock = 68, 215, 168 ShamrockGreen = 0, 153, 102 SherpaBlue = 0, 75, 73 SherwoodGreen = 27, 77, 62 Shilo = 222, 165, 164 ShipCove = 119, 139, 165 Shocking = 241, 156, 187 ShockingPink = 255, 29, 206 ShuttleGrey = 84, 98, 111 Sidecar = 238, 224, 177 Sienna = 160, 82, 45 Silk = 190, 164, 147 Silver = 192, 192, 192 SilverChalice = 175, 177, 174 SilverTree = 102, 201, 146 SkyBlue = 135, 206, 235 SlateBlue = 106, 90, 205 SlateGray = 112, 128, 144 SlateGrey = 112, 128, 144 Smalt = 0, 48, 143 SmaltBlue = 74, 100, 108 Snow = 255, 250, 250 SoftAmber = 209, 190, 168 Solitude = 235, 236, 240 Sorbus = 233, 105, 44 Spectra = 53, 101, 77 SpicyMix = 136, 101, 78 Spray = 126, 212, 230 SpringBud = 150, 255, 0 SpringGreen = 0, 255, 127 SpringSun = 236, 235, 189 SpunPearl = 170, 169, 173 Stack = 130, 142, 132 SteelBlue = 70, 130, 180 Stiletto = 137, 63, 69 Strikemaster = 145, 92, 131 StTropaz = 50, 82, 123 Studio = 115, 79, 150 Sulu = 201, 220, 135 SummerSky = 33, 171, 205 Sun = 237, 135, 45 Sundance = 197, 179, 88 Sunflower = 228, 208, 10 Sunglow = 255, 204, 51 SunsetOrange = 253, 82, 64 SurfieGreen = 0, 116, 116 Sushi = 111, 153, 64 SuvaGrey = 140, 140, 140 Swamp = 35, 43, 43 SweetCorn = 253, 219, 109 SweetPink = 243, 153, 152 Tacao = 236, 177, 118 TahitiGold = 235, 97, 35 Tan = 210, 180, 140 Tangaroa = 0, 28, 61 Tangerine = 228, 132, 0 TangerineYellow = 253, 204, 13 Tapestry = 183, 110, 121 Taupe = 72, 60, 50 TaupeGrey = 139, 133, 137 TawnyPort = 102, 66, 77 TaxBreak = 79, 102, 106 TeaGreen = 208, 240, 192 Teak = 176, 141, 87 Teal = 0, 128, 128 TeaRose = 255, 133, 207 Temptress = 60, 20, 33 Tenne = 200, 101, 0 TerraCotta = 226, 114, 91 Thistle = 216, 191, 216 TickleMePink = 245, 111, 161 Tidal = 232, 244, 140 TitanWhite = 214, 202, 221 Toast = 165, 113, 100 Tomato = 255, 99, 71 TorchRed = 255, 3, 62 ToryBlue = 54, 81, 148 Tradewind = 110, 174, 161 TrendyPink = 133, 96, 136 TropicalRainForest = 0, 127, 102 TrueV = 139, 114, 190 TulipTree = 229, 183, 59 Tumbleweed = 222, 170, 136 Turbo = 255, 195, 36 TurkishRose = 152, 119, 123 Turquoise = 64, 224, 208 TurquoiseBlue = 118, 215, 234 Tuscany = 175, 89, 62 TwilightBlue = 253, 255, 245 Twine = 186, 135, 89 TyrianPurple = 102, 2, 60 Ultramarine = 10, 17, 149 UltraPink = 255, 111, 255 Valencia = 222, 82, 70 VanCleef = 84, 61, 55 VanillaIce = 229, 204, 201 VenetianRed = 209, 0, 28 Venus = 138, 127, 128 Vermilion = 251, 79, 20 VeryLightGrey = 207, 207, 207 VidaLoca = 94, 140, 49 Viking = 71, 171, 204 Viola = 180, 131, 149 ViolentViolet = 50, 23, 77 Violet = 238, 130, 238 VioletRed = 255, 57, 136 Viridian = 64, 130, 109 VistaBlue = 159, 226, 191 VividViolet = 127, 62, 152 WaikawaGrey = 83, 104, 149 Wasabi = 150, 165, 60 Watercourse = 0, 106, 78 Wedgewood = 67, 107, 149 WellRead = 147, 61, 65 Wewak = 255, 152, 153 Wheat = 245, 222, 179 Whiskey = 217, 154, 108 WhiskeySour = 217, 144, 88 White = 255, 255, 255 WhiteSmoke = 245, 245, 245 WildRice = 228, 217, 111 WildSand = 229, 228, 226 WildStrawberry = 252, 65, 154 WildWatermelon = 255, 84, 112 WildWillow = 172, 191, 96 Windsor = 76, 40, 130 Wisteria = 191, 148, 228 Wistful = 162, 162, 208 Yellow = 255, 255, 0 YellowGreen = 154, 205, 50 YellowOrange = 255, 174, 66 YourPink = 244, 194, 194 } function Convert-GenericRightsToFileSystemRights { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER OriginalRights Parameter description .EXAMPLE An example .NOTES .LINK Improved https://blog.cjwdev.co.uk/2011/06/28/permissions-not-included-in-net-accessrule-filesystemrights-enum/ #> [cmdletBinding()] param( [System.Security.AccessControl.FileSystemRights] $OriginalRights ) Begin { $FileSystemRights = [System.Security.AccessControl.FileSystemRights] $GenericRights = @{ GENERIC_READ = 0x80000000; GENERIC_WRITE = 0x40000000; GENERIC_EXECUTE = 0x20000000; GENERIC_ALL = 0x10000000; FILTER_GENERIC = 0x0FFFFFFF; } $MappedGenericRights = @{ FILE_GENERIC_EXECUTE = $FileSystemRights::ExecuteFile -bor $FileSystemRights::ReadPermissions -bor $FileSystemRights::ReadAttributes -bor $FileSystemRights::Synchronize FILE_GENERIC_READ = $FileSystemRights::ReadAttributes -bor $FileSystemRights::ReadData -bor $FileSystemRights::ReadExtendedAttributes -bor $FileSystemRights::ReadPermissions -bor $FileSystemRights::Synchronize FILE_GENERIC_WRITE = $FileSystemRights::AppendData -bor $FileSystemRights::WriteAttributes -bor $FileSystemRights::WriteData -bor $FileSystemRights::WriteExtendedAttributes -bor $FileSystemRights::ReadPermissions -bor $FileSystemRights::Synchronize FILE_GENERIC_ALL = $FileSystemRights::FullControl } } Process { $MappedRights = [System.Security.AccessControl.FileSystemRights]::new() if ($OriginalRights -band $GenericRights.GENERIC_EXECUTE) { $MappedRights = $MappedRights -bor $MappedGenericRights.FILE_GENERIC_EXECUTE } if ($OriginalRights -band $GenericRights.GENERIC_READ) { $MappedRights = $MappedRights -bor $MappedGenericRights.FILE_GENERIC_READ } if ($OriginalRights -band $GenericRights.GENERIC_WRITE) { $MappedRights = $MappedRights -bor $MappedGenericRights.FILE_GENERIC_WRITE } if ($OriginalRights -band $GenericRights.GENERIC_ALL) { $MappedRights = $MappedRights -bor $MappedGenericRights.FILE_GENERIC_ALL } (($OriginalRights -bAND $GenericRights.FILTER_GENERIC) -bOR $MappedRights) -as $FileSystemRights } End { } } function ConvertFrom-HTML { [alias('Convert-HTMLToString')] [CmdletBinding()] param( [string[]] $HTML, [switch] $RemoveTags ) foreach ($H in $HTML) { if ($RemoveTags) { # this removes things like between brackets like </p> or <p> $H = $H -replace '<[^>]+>', '' } # This replaces chars to their clean equivalent $H -replace '“', '"' -replace '’', "'" -replace '”', '"' -replace '…', '...' -replace '—', '-' -replace '–', '-' } } function ConvertTo-HkeyUser { [CmdletBinding()] param( [System.Collections.IDictionary] $HiveDictionary, [Array] $SubKeys, [string] $DictionaryKey, [string] $RegistryPath ) $OutputRegistryKeys = foreach ($Sub in $Subkeys) { if ($HiveDictionary[$DictionaryKey] -eq 'All') { if ($Sub -notlike "*_Classes*" -and $Sub -ne '.DEFAULT') { $RegistryPath.Replace($DictionaryKey, "Users\$Sub") } } elseif ($HiveDictionary[$DictionaryKey] -eq 'All+Default') { if ($Sub -notlike "*_Classes*") { if (-not $Script:DefaultRegistryMounted) { $Script:DefaultRegistryMounted = Mount-DefaultRegistryPath } if ($Sub -eq '.DEFAULT') { $RegistryPath.Replace($DictionaryKey, "Users\.DEFAULT_USER") } else { $RegistryPath.Replace($DictionaryKey, "Users\$Sub") } } } elseif ($HiveDictionary[$DictionaryKey] -eq 'Default') { if ($Sub -eq '.DEFAULT') { if (-not $Script:DefaultRegistryMounted) { $Script:DefaultRegistryMounted = Mount-DefaultRegistryPath } $RegistryPath.Replace($DictionaryKey, "Users\.DEFAULT_USER") } } elseif ($HiveDictionary[$DictionaryKey] -eq 'AllDomain+Default') { if (($Sub.StartsWith("S-1-5-21") -and $Sub -notlike "*_Classes*") -or $Sub -eq '.DEFAULT') { if (-not $Script:DefaultRegistryMounted) { $Script:DefaultRegistryMounted = Mount-DefaultRegistryPath } if ($Sub -eq '.DEFAULT') { $RegistryPath.Replace($DictionaryKey, "Users\.DEFAULT_USER") } else { $RegistryPath.Replace($DictionaryKey, "Users\$Sub") } } } elseif ($HiveDictionary[$DictionaryKey] -eq 'AllDomain+Other') { if (($Sub.StartsWith("S-1-5-21") -and $Sub -notlike "*_Classes*")) { if (-not $Script:OfflineRegistryMounted) { $Script:OfflineRegistryMounted = Mount-AllRegistryPath foreach ($Key in $Script:OfflineRegistryMounted.Keys) { $RegistryPath.Replace($DictionaryKey, "Users\$Key") } } $RegistryPath.Replace($DictionaryKey, "Users\$Sub") } } elseif ($HiveDictionary[$DictionaryKey] -eq 'AllDomain+Other+Default') { if (($Sub.StartsWith("S-1-5-21") -and $Sub -notlike "*_Classes*") -or $Sub -eq '.DEFAULT') { if (-not $Script:DefaultRegistryMounted) { $Script:DefaultRegistryMounted = Mount-DefaultRegistryPath } if (-not $Script:OfflineRegistryMounted) { $Script:OfflineRegistryMounted = Mount-AllRegistryPath foreach ($Key in $Script:OfflineRegistryMounted.Keys) { $RegistryPath.Replace($DictionaryKey, "Users\$Key") } } if ($Sub -eq '.DEFAULT') { $RegistryPath.Replace($DictionaryKey, "Users\.DEFAULT_USER") } else { $RegistryPath.Replace($DictionaryKey, "Users\$Sub") } } } elseif ($HiveDictionary[$DictionaryKey] -eq 'AllDomain') { if ($Sub.StartsWith("S-1-5-21") -and $Sub -notlike "*_Classes*") { $RegistryPath.Replace($DictionaryKey, "Users\$Sub") } } elseif ($HiveDictionary[$DictionaryKey] -eq 'Users') { if ($Sub -like "Offline_*") { $Script:OfflineRegistryMounted = Mount-AllRegistryPath -MountUsers $Sub foreach ($Key in $Script:OfflineRegistryMounted.Keys) { if ($Script:OfflineRegistryMounted[$Key].Status -eq $true) { $RegistryPath } } } } } $OutputRegistryKeys | Sort-Object -Unique } function ConvertTo-StringByType { <# .SYNOPSIS Private function to use within ConvertTo-JsonLiteral .DESCRIPTION Private function to use within ConvertTo-JsonLiteral .PARAMETER Value Value to convert to JsonValue .PARAMETER Depth Specifies how many levels of contained objects are included in the JSON representation. The default value is 0. .PARAMETER AsArray Outputs the object in array brackets, even if the input is a single object. .PARAMETER DateTimeFormat Changes DateTime string format. Default "yyyy-MM-dd HH:mm:ss" .PARAMETER NumberAsString Provides an alternative serialization option that converts all numbers to their string representation. .PARAMETER BoolAsString Provides an alternative serialization option that converts all bool to their string representation. .PARAMETER PropertyName Uses PropertyNames provided by user (only works with Force) .PARAMETER ArrayJoin Forces any array to be a string regardless of depth level .PARAMETER ArrayJoinString Uses defined string or char for array join. By default it uses comma with a space when used. .PARAMETER Force Forces using property names from first object or given thru PropertyName parameter .EXAMPLE $Value = ConvertTo-StringByType -Value $($Object[$a][$i]) -DateTimeFormat $DateTimeFormat .NOTES General notes #> [cmdletBinding()] param( [Object] $Value, [int] $Depth, [int] $MaxDepth, [string] $DateTimeFormat, [switch] $NumberAsString, [switch] $BoolAsString, [System.Collections.IDictionary] $NewLineFormat = @{ NewLineCarriage = '\r\n' NewLine = "\n" Carriage = "\r" }, [System.Collections.IDictionary] $NewLineFormatProperty = @{ NewLineCarriage = '\r\n' NewLine = "\n" Carriage = "\r" }, [System.Collections.IDictionary] $AdvancedReplace, [System.Text.StringBuilder] $TextBuilder, [string[]] $PropertyName, [switch] $ArrayJoin, [string] $ArrayJoinString, [switch] $Force ) Process { if ($null -eq $Value) { "`"`"" } elseif ($Value -is [string]) { $Value = $Value.Replace('\', "\\").Replace('"', '\"').Replace([System.Environment]::NewLine, $NewLineFormat.NewLineCarriage).Replace("`n", $NewLineFormat.NewLine).Replace("`r", $NewLineFormat.Carriage) #$Value = $Value.Replace('.', '\.').Replace('$', '\$') foreach ($Key in $AdvancedReplace.Keys) { $Value = $Value.Replace($Key, $AdvancedReplace[$Key]) } "`"$Value`"" } elseif ($Value -is [DateTime]) { "`"$($($Value).ToString($DateTimeFormat))`"" } elseif ($Value -is [bool]) { if ($BoolAsString) { "`"$($Value)`"" } else { $Value.ToString().ToLower() } } elseif ($Value -is [System.Collections.IDictionary]) { if ($MaxDepth -eq 0 -or $Depth -eq $MaxDepth) { "`"$($Value)`"" } else { $Depth++ $null = $TextBuilder.AppendLine("{") for ($i = 0; $i -lt ($Value.Keys).Count; $i++) { $Property = ([string[]]$Value.Keys)[$i] $DisplayProperty = $Property.Replace('\', "\\").Replace('"', '\"').Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage) $null = $TextBuilder.Append("`"$DisplayProperty`":") $OutputValue = ConvertTo-StringByType -Value $Value[$Property] -DateTimeFormat $DateTimeFormat -NumberAsString:$NumberAsString -BoolAsString:$BoolAsString -Depth $Depth -MaxDepth $MaxDepth -TextBuilder $TextBuilder -Force:$Force -ArrayJoinString $ArrayJoinString -ArrayJoin:$ArrayJoin.IsPresent $null = $TextBuilder.Append("$OutputValue") if ($i -ne ($Value.Keys).Count - 1) { $null = $TextBuilder.AppendLine(',') } } $null = $TextBuilder.Append("}") } } elseif ($Value -is [System.Collections.IList] -or $Value -is [System.Collections.ReadOnlyCollectionBase]) { if ($ArrayJoin) { $Value = $Value -join $ArrayJoinString $Value = "$Value".Replace('\', "\\").Replace('"', '\"').Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage) "`"$Value`"" } else { if ($MaxDepth -eq 0 -or $Depth -eq $MaxDepth) { $Value = "$Value".Replace('\', "\\").Replace('"', '\"').Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage) "`"$Value`"" } else { $CountInternalObjects = 0 $null = $TextBuilder.Append("[") foreach ($V in $Value) { $CountInternalObjects++ if ($CountInternalObjects -gt 1) { $null = $TextBuilder.Append(',') } if ($Force -and -not $PropertyName) { $PropertyName = $V.PSObject.Properties.Name } elseif ($Force -and $PropertyName) { } else { $PropertyName = $V.PSObject.Properties.Name } $OutputValue = ConvertTo-StringByType -Value $V -DateTimeFormat $DateTimeFormat -NumberAsString:$NumberAsString -BoolAsString:$BoolAsString -Depth $Depth -MaxDepth $MaxDepth -TextBuilder $TextBuilder -Force:$Force -PropertyName $PropertyName -ArrayJoinString $ArrayJoinString -ArrayJoin:$ArrayJoin.IsPresent $null = $TextBuilder.Append($OutputValue) } $null = $TextBuilder.Append("]") } } } elseif ($Value -is [System.Enum]) { "`"$($($Value).ToString())`"" } elseif (($Value | IsNumeric) -eq $true) { $Value = $($Value).ToString().Replace(',', '.') if ($NumberAsString) { "`"$Value`"" } else { $Value } } elseif ($Value -is [PSObject]) { if ($MaxDepth -eq 0 -or $Depth -eq $MaxDepth) { "`"$($Value)`"" } else { $Depth++ $CountInternalObjects = 0 $null = $TextBuilder.AppendLine("{") if ($Force -and -not $PropertyName) { $PropertyName = $Value.PSObject.Properties.Name } elseif ($Force -and $PropertyName) { } else { $PropertyName = $Value.PSObject.Properties.Name } foreach ($Property in $PropertyName) { $CountInternalObjects++ if ($CountInternalObjects -gt 1) { $null = $TextBuilder.AppendLine(',') } $DisplayProperty = $Property.Replace('\', "\\").Replace('"', '\"').Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage) $null = $TextBuilder.Append("`"$DisplayProperty`":") $OutputValue = ConvertTo-StringByType -Value $Value.$Property -DateTimeFormat $DateTimeFormat -NumberAsString:$NumberAsString -BoolAsString:$BoolAsString -Depth $Depth -MaxDepth $MaxDepth -TextBuilder $TextBuilder -Force:$Force -ArrayJoinString $ArrayJoinString -ArrayJoin:$ArrayJoin.IsPresent $null = $TextBuilder.Append("$OutputValue") } $null = $TextBuilder.Append("}") } } else { $Value = $Value.ToString().Replace('\', "\\").Replace('"', '\"').Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage) "`"$Value`"" } } } function Get-ComputerSplit { [CmdletBinding()] param( [string[]] $ComputerName ) if ($null -eq $ComputerName) { $ComputerName = $Env:COMPUTERNAME } try { $LocalComputerDNSName = [System.Net.Dns]::GetHostByName($Env:COMPUTERNAME).HostName } catch { $LocalComputerDNSName = $Env:COMPUTERNAME } $ComputersLocal = $null [Array] $Computers = foreach ($Computer in $ComputerName) { if ($Computer -eq '' -or $null -eq $Computer) { $Computer = $Env:COMPUTERNAME } if ($Computer -ne $Env:COMPUTERNAME -and $Computer -ne $LocalComputerDNSName) { $Computer } else { $ComputersLocal = $Computer } } , @($ComputersLocal, $Computers) } function Get-LocalComputerSid { <# .SYNOPSIS Get the SID of the local computer. .DESCRIPTION Get the SID of the local computer. .EXAMPLE Get-LocalComputerSid .NOTES General notes #> [cmdletBinding()] param() try { Add-Type -AssemblyName System.DirectoryServices.AccountManagement $PrincipalContext = [System.DirectoryServices.AccountManagement.PrincipalContext]::new([System.DirectoryServices.AccountManagement.ContextType]::Machine) $UserPrincipal = [System.DirectoryServices.AccountManagement.UserPrincipal]::new($PrincipalContext) $Searcher = [System.DirectoryServices.AccountManagement.PrincipalSearcher]::new() $Searcher.QueryFilter = $UserPrincipal $User = $Searcher.FindAll() foreach ($U in $User) { if ($U.Sid.Value -like "*-500") { return $U.Sid.Value.TrimEnd("-500") } } } catch { Write-Warning -Message "Get-LocalComputerSid - Error: $($_.Exception.Message)" } } function Get-OfflineRegistryProfilesPath { <# .SYNOPSIS Short description .DESCRIPTION Long description .EXAMPLE Get-OfflineRegistryProfilesPath .NOTES Name Value ---- ----- Przemek {[FilePath, C:\Users\Przemek\NTUSER.DAT], [Status, ]} test.1 {[FilePath, C:\Users\test.1\NTUSER.DAT], [Status, ]} #> [CmdletBinding()] param( ) $Profiles = [ordered] @{} $CurrentMapping = (Get-PSRegistry -RegistryPath 'HKEY_USERS' -ExpandEnvironmentNames -DoNotUnmount).PSSubKeys $UsersInSystem = (Get-PSRegistry -RegistryPath 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' -ExpandEnvironmentNames -DoNotUnmount).PSSubKeys $MissingProfiles = foreach ($Profile in $UsersInSystem) { if ($Profile.StartsWith("S-1-5-21") -and $CurrentMapping -notcontains $Profile) { Get-PSRegistry -RegistryPath "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$Profile" -ExpandEnvironmentNames -DoNotUnmount } } foreach ($Profile in $MissingProfiles) { $PathToNTUser = [io.path]::Combine($Profile.ProfileImagePath, 'NTUSER.DAT') $ProfileName = [io.path]::GetFileName($Profile.ProfileImagePath) $StartPath = "Offline_$ProfileName" try { $PathExists = Test-Path -LiteralPath $PathToNTUser -ErrorAction Stop if ($PathExists) { $Profiles[$StartPath] = [ordered] @{ FilePath = $PathToNTUser Status = $null } } } catch { Write-Warning -Message "Mount-OfflineRegistryPath - Couldn't execute. Error: $($_.Exception.Message)" continue } } $Profiles } function Get-PrivateRegistryTranslated { [cmdletBinding()] param( [Array] $RegistryPath, [System.Collections.IDictionary] $HiveDictionary, [System.Collections.IDictionary] $ReverseTypesDictionary, [Parameter()][ValidateSet('REG_SZ', 'REG_NONE', 'None', 'REG_EXPAND_SZ', 'REG_BINARY', 'REG_DWORD', 'REG_MULTI_SZ', 'REG_QWORD', 'string', 'binary', 'dword', 'qword', 'multistring', 'expandstring')][string] $Type, [Parameter()][string] $Key, [Parameter()][object] $Value ) foreach ($Registry in $RegistryPath) { # Remove additional slashes if ($Registry -is [string]) { $Registry = $Registry.Replace("\\", "\").Replace("\\", "\").TrimStart("\").TrimEnd("\") } else { $Registry.RegistryPath = $Registry.RegistryPath.Replace("\\", "\").Replace("\\", "\").TrimStart("\").TrimEnd("\") } foreach ($Hive in $HiveDictionary.Keys) { if ($Registry -is [string] -and $Registry.StartsWith($Hive, [System.StringComparison]::CurrentCultureIgnoreCase)) { if ($Hive.Length -eq $Registry.Length) { [ordered] @{ HiveKey = $HiveDictionary[$Hive] SubKeyName = $null ValueKind = if ($Type) { [Microsoft.Win32.RegistryValueKind]::($ReverseTypesDictionary[$Type]) } else { $null } Key = $Key Value = $Value } } else { [ordered] @{ HiveKey = $HiveDictionary[$Hive] SubKeyName = $Registry.substring($Hive.Length + 1) ValueKind = if ($Type) { [Microsoft.Win32.RegistryValueKind]::($ReverseTypesDictionary[$Type]) } else { $null } Key = $Key Value = $Value } } break } elseif ($Registry -isnot [string] -and $Registry.RegistryPath.StartsWith($Hive, [System.StringComparison]::CurrentCultureIgnoreCase)) { if ($Hive.Length -eq $Registry.RegistryPath.Length) { [ordered] @{ ComputerName = $Registry.ComputerName HiveKey = $HiveDictionary[$Hive] SubKeyName = $null ValueKind = if ($Type) { [Microsoft.Win32.RegistryValueKind]::($ReverseTypesDictionary[$Type]) } else { $null } Key = $Key Value = $Value } } else { [ordered] @{ ComputerName = $Registry.ComputerName HiveKey = $HiveDictionary[$Hive] SubKeyName = $Registry.RegistryPath.substring($Hive.Length + 1) ValueKind = if ($Type) { [Microsoft.Win32.RegistryValueKind]::($ReverseTypesDictionary[$Type]) } else { $null } Key = $Key Value = $Value } } break } } } } function Get-PSConvertSpecialRegistry { [cmdletbinding()] param( [Array] $RegistryPath, [Array] $Computers, [System.Collections.IDictionary] $HiveDictionary, [switch] $ExpandEnvironmentNames ) $FixedPath = foreach ($R in $RegistryPath) { foreach ($DictionaryKey in $HiveDictionary.Keys) { $SplitParts = $R.Split("\") $FirstPart = $SplitParts[0] if ($FirstPart -eq $DictionaryKey) { #if ($R.StartsWith($DictionaryKey, [System.StringComparison]::CurrentCultureIgnoreCase)) { if ($HiveDictionary[$DictionaryKey] -in 'All', 'All+Default', 'Default', 'AllDomain+Default', 'AllDomain', 'AllDomain+Other', 'AllDomain+Other+Default') { foreach ($Computer in $Computers) { $SubKeys = Get-PSRegistry -RegistryPath "HKEY_USERS" -ComputerName $Computer -ExpandEnvironmentNames:$ExpandEnvironmentNames.IsPresent -DoNotUnmount if ($SubKeys.PSSubKeys) { $RegistryKeys = ConvertTo-HKeyUser -SubKeys ($SubKeys.PSSubKeys | Sort-Object) -HiveDictionary $HiveDictionary -DictionaryKey $DictionaryKey -RegistryPath $R foreach ($S in $RegistryKeys) { [PSCustomObject] @{ ComputerName = $Computer RegistryPath = $S Error = $null ErrorMessage = $null } } } else { [PSCustomObject] @{ ComputerName = $Computer RegistryPath = $R Error = $true ErrorMessage = "Couldn't connect to $Computer to list HKEY_USERS" } } } } elseif ($FirstPart -in 'Users', 'HKEY_USERS', 'HKU' -and $SplitParts[1] -and $SplitParts[1] -like "Offline_*") { # this is a special keys to handle for offline users # 'Users\Offline_Przemek\Software\Policies1\Microsoft\Windows\CloudContent' -Key 'DisableTailoredExperiencesWithDiagnosticData' # 'HKU\Offline_Przemek\Software\Policies1\Microsoft\Windows\CloudContent' -Key 'DisableWindowsConsumerFeatures' # 'Users\Offline_test.1\Software\Policies1\Microsoft\Windows\CloudContent' -Key 'DisableTailoredExperiencesWithDiagnosticData' foreach ($Computer in $Computers) { $SubKeys = Get-PSRegistry -RegistryPath "HKEY_USERS" -ComputerName $Computer -ExpandEnvironmentNames:$ExpandEnvironmentNames.IsPresent -DoNotUnmount if ($SubKeys.PSSubKeys) { $RegistryKeys = ConvertTo-HKeyUser -SubKeys ($SubKeys.PSSubKeys + $SplitParts[1] | Sort-Object) -HiveDictionary $HiveDictionary -DictionaryKey $DictionaryKey -RegistryPath $R foreach ($S in $RegistryKeys) { [PSCustomObject] @{ ComputerName = $Computer RegistryPath = $S Error = $null ErrorMessage = $null } } } else { [PSCustomObject] @{ ComputerName = $Computer RegistryPath = $R Error = $true ErrorMessage = "Couldn't connect to $Computer to list HKEY_USERS" } } } } else { $R } break } } } $FixedPath } function Get-PSSubRegistry { [cmdletBinding()] param( [System.Collections.IDictionary] $Registry, [string] $ComputerName, [switch] $Remote, [switch] $ExpandEnvironmentNames ) if ($Registry.ComputerName) { if ($Registry.ComputerName -ne $ComputerName) { return } } if (-not $Registry.Error) { try { if ($Remote) { $BaseHive = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($Registry.HiveKey, $ComputerName, 0 ) } else { $BaseHive = [Microsoft.Win32.RegistryKey]::OpenBaseKey($Registry.HiveKey, 0 ) } $PSConnection = $true $PSError = $null } catch { $PSConnection = $false $PSError = $($_.Exception.Message) } } else { # this should happen if we weren't able to get registry keys in Get-PSConvertSpecialRegistry for HKEY_USERS $PSConnection = $false $PSError = $($Registry.ErrorMessage) } if ($PSError) { [PSCustomObject] @{ PSComputerName = $ComputerName PSConnection = $PSConnection PSError = $true PSErrorMessage = $PSError PSPath = $Registry.Registry PSKey = $Registry.Key PSValue = $null PSType = $null } } else { try { $SubKey = $BaseHive.OpenSubKey($Registry.SubKeyName, $false) if ($null -ne $SubKey) { [PSCustomObject] @{ PSComputerName = $ComputerName PSConnection = $PSConnection PSError = $false PSErrorMessage = $null PSPath = $Registry.Registry PSKey = $Registry.Key PSValue = if (-not $ExpandEnvironmentNames) { $SubKey.GetValue($Registry.Key, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames) } else { $SubKey.GetValue($Registry.Key) } PSType = $SubKey.GetValueKind($Registry.Key) } } else { [PSCustomObject] @{ PSComputerName = $ComputerName PSConnection = $PSConnection PSError = $true PSErrorMessage = "Registry path $($Registry.Registry) doesn't exists." PSPath = $Registry.Registry PSKey = $Registry.Key PSValue = $null PSType = $null } } } catch { [PSCustomObject] @{ PSComputerName = $ComputerName PSConnection = $PSConnection PSError = $true PSErrorMessage = $_.Exception.Message PSPath = $Registry.Registry PSKey = $Registry.Key PSValue = $null PSType = $null } } } if ($null -ne $SubKey) { $SubKey.Close() $SubKey.Dispose() } if ($null -ne $BaseHive) { $BaseHive.Close() $BaseHive.Dispose() } } function Get-PSSubRegistryComplete { [cmdletBinding()] param( [System.Collections.IDictionary] $Registry, [string] $ComputerName, [switch] $Remote, [switch] $Advanced, [switch] $ExpandEnvironmentNames ) if ($Registry.ComputerName) { if ($Registry.ComputerName -ne $ComputerName) { return } } if (-not $Registry.Error) { try { if ($Remote) { $BaseHive = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($Registry.HiveKey, $ComputerName, 0 ) } else { $BaseHive = [Microsoft.Win32.RegistryKey]::OpenBaseKey($Registry.HiveKey, 0 ) } $PSConnection = $true $PSError = $null } catch { $PSConnection = $false $PSError = $($_.Exception.Message) } } else { # this should happen if we weren't able to get registry keys in Get-PSConvertSpecialRegistry for HKEY_USERS $PSConnection = $false $PSError = $($Registry.ErrorMessage) } if ($PSError) { [PSCustomObject] @{ PSComputerName = $ComputerName PSConnection = $PSConnection PSError = $true PSErrorMessage = $PSError PSSubKeys = $null PSPath = $Registry.Registry PSKey = $Registry.Key } } else { try { $SubKey = $BaseHive.OpenSubKey($Registry.SubKeyName, $false) if ($null -ne $SubKey) { $Object = [ordered] @{ PSComputerName = $ComputerName PSConnection = $PSConnection PSError = $false PSErrorMessage = $null PSSubKeys = $SubKey.GetSubKeyNames() PSPath = $Registry.Registry } $Keys = $SubKey.GetValueNames() foreach ($K in $Keys) { if ($K -eq "") { if ($Advanced) { $Object['DefaultKey'] = [ordered] @{ Value = if (-not $ExpandEnvironmentNames) { $SubKey.GetValue($K, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames) } else { $SubKey.GetValue($K) } Type = $SubKey.GetValueKind($K) } } else { $Object['DefaultKey'] = $SubKey.GetValue($K) } } else { if ($Advanced) { $Object[$K] = [ordered] @{ Value = if (-not $ExpandEnvironmentNames) { $SubKey.GetValue($K, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames) } else { $SubKey.GetValue($K) } Type = $SubKey.GetValueKind($K) } } else { $Object[$K] = if (-not $ExpandEnvironmentNames) { $SubKey.GetValue($K, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames) } else { $SubKey.GetValue($K) } } } } [PSCustomObject] $Object } else { [PSCustomObject] @{ PSComputerName = $ComputerName PSConnection = $PSConnection PSError = $true PSErrorMessage = "Registry path $($Registry.Registry) doesn't exists." PSSubKeys = $null PSPath = $Registry.Registry } } } catch { [PSCustomObject] @{ PSComputerName = $ComputerName PSConnection = $PSConnection PSError = $true PSErrorMessage = $_.Exception.Message PSSubKeys = $null PSPath = $Registry.Registry } } } if ($null -ne $SubKey) { $SubKey.Close() $SubKey.Dispose() } if ($null -ne $BaseHive) { $BaseHive.Close() $BaseHive.Dispose() } } function Get-PSSubRegistryTranslated { [cmdletBinding()] param( [Array] $RegistryPath, [System.Collections.IDictionary] $HiveDictionary, [string] $Key ) foreach ($Registry in $RegistryPath) { # Remove additional slashes if ($Registry -is [string]) { $Registry = $Registry.Replace("\\", "\").Replace("\\", "\").TrimStart("\").TrimEnd("\") $FirstPartSplit = $Registry -split "\\" $FirstPart = $FirstPartSplit[0] } else { $Registry.RegistryPath = $Registry.RegistryPath.Replace("\\", "\").Replace("\\", "\").TrimStart("\").TrimEnd("\") $FirstPartSplit = $Registry.RegistryPath -split "\\" $FirstPart = $FirstPartSplit[0] } foreach ($Hive in $HiveDictionary.Keys) { if ($Registry -is [string] -and $FirstPart -eq $Hive) { # if ($Registry -is [string] -and $Registry.StartsWith($Hive, [System.StringComparison]::CurrentCultureIgnoreCase)) { if ($Hive.Length -eq $Registry.Length) { [ordered] @{ Registry = $Registry HiveKey = $HiveDictionary[$Hive] SubKeyName = $null Key = if ($Key -eq "") { $null } else { $Key } Error = $null ErrorMessage = $null } } else { [ordered] @{ Registry = $Registry HiveKey = $HiveDictionary[$Hive] SubKeyName = $Registry.substring($Hive.Length + 1) Key = if ($Key -eq "") { $null } else { $Key } Error = $null ErrorMessage = $null } } break } elseif ($Registry -isnot [string] -and $FirstPart -eq $Hive) { #} elseif ($Registry -isnot [string] -and $Registry.RegistryPath.StartsWith($Hive, [System.StringComparison]::CurrentCultureIgnoreCase)) { if ($Hive.Length -eq $Registry.RegistryPath.Length) { [ordered] @{ ComputerName = $Registry.ComputerName Registry = $Registry.RegistryPath HiveKey = $HiveDictionary[$Hive] SubKeyName = $null Key = if ($Key -eq "") { $null } else { $Key } Error = $Registry.Error ErrorMessage = $Registry.ErrorMessage } } else { [ordered] @{ ComputerName = $Registry.ComputerName Registry = $Registry.RegistryPath HiveKey = $HiveDictionary[$Hive] SubKeyName = $Registry.RegistryPath.substring($Hive.Length + 1) Key = if ($Key -eq "") { $null } else { $Key } Error = $Registry.Error ErrorMessage = $Registry.ErrorMessage } } break } } } } function Mount-AllRegistryPath { [CmdletBinding()] param( [string] $MountPoint = "HKEY_USERS\", [string] $MountUsers ) $AllProfiles = Get-OfflineRegistryProfilesPath foreach ($Profile in $AllProfiles.Keys) { if ($MountUsers) { if ($MountUsers -ne $Profile) { continue } } $WhereMount = "$MountPoint\$Profile".Replace("\\", "\") Write-Verbose -Message "Mount-OfflineRegistryPath - Mounting $WhereMount to $($AllProfiles[$Profile].FilePath)" $AllProfiles[$Profile].Status = Mount-PSRegistryPath -MountPoint $WhereMount -FilePath $AllProfiles[$Profile].FilePath } $AllProfiles } function Mount-DefaultRegistryPath { [CmdletBinding()] param( [string] $MountPoint = "HKEY_USERS\.DEFAULT_USER" ) $DefaultRegistryPath = Get-PSRegistry -RegistryPath 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' -Key 'Default' -ExpandEnvironmentNames -DoNotUnmount if ($PSError -ne $true) { $PathToNTUser = [io.path]::Combine($DefaultRegistryPath.PSValue, 'NTUSER.DAT') Write-Verbose -Message "Mount-DefaultRegistryPath - Mounting $MountPoint to $PathToNTUser" Mount-PSRegistryPath -MountPoint $MountPoint -FilePath $PathToNTUser } else { if ($PSBoundParameters.ErrorAction -eq 'Stop') { throw $PSErrorMessage } else { Write-Warning -Message "Mount-DefaultRegistryPath - Couldn't execute. Error: $PSErrorMessage" } } } function New-PrivateRegistry { [CmdletBinding(SupportsShouldProcess)] param( [System.Collections.IDictionary] $RegistryValue, [string] $Computer, [switch] $Remote ) try { if ($Remote) { $BaseHive = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($RegistryValue.HiveKey, $Computer, 0 ) } else { $BaseHive = [Microsoft.Win32.RegistryKey]::OpenBaseKey($RegistryValue.HiveKey, 0 ) } $PSConnection = $true $PSError = $null } catch { $PSConnection = $false $PSError = $($_.Exception.Message) if ($PSBoundParameters.ErrorAction -eq 'Stop') { if ($null -ne $BaseHive) { $BaseHive.Close() $BaseHive.Dispose() } throw } else { Write-Warning "New-PSRegistry - Setting registry $($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName) on $($RegistryValue.Key) to $($RegistryValue.Value) of $($RegistryValue.ValueKind) on $Computer have failed. Error: $($_.Exception.Message.Replace([System.Environment]::NewLine, " "))" } } if ($PSError) { if (-not $Suppress) { [PSCustomObject] @{ PSComputerName = $Computer PSConnection = $PSConnection PSError = $true PSErrorMessage = $PSError Path = "$($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName)" } } } else { try { if ($PSCmdlet.ShouldProcess($Computer, "Creating registry $($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName)")) { $SubKey = $BaseHive.OpenSubKey($RegistryValue.SubKeyName, $true) if (-not $SubKey) { $SubKeysSplit = $RegistryValue.SubKeyName.Split('\') $SubKey = $BaseHive.OpenSubKey($SubKeysSplit[0], $true) if (-not $SubKey) { $SubKey = $BaseHive.CreateSubKey($SubKeysSplit[0]) } $SubKey = $BaseHive.OpenSubKey($SubKeysSplit[0], $true) foreach ($S in $SubKeysSplit | Select-Object -Skip 1) { $SubKey = $SubKey.CreateSubKey($S) } $PSError = $false $PSErrorMessage = $null } else { $PSError = $false $PSErrorMessage = "$($RegistryValue.SubKeyName) already exists." } } else { $PSError = $true $PSErrorMessage = "WhatIf was used. No changes done." } if (-not $Suppress) { [PSCustomObject] @{ PSComputerName = $Computer PSConnection = $PSConnection PSError = $PSError PSErrorMessage = $PSErrorMessage Path = "$($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName)" } } } catch { if ($PSBoundParameters.ErrorAction -eq 'Stop') { if ($null -ne $SubKey) { $SubKey.Close() $SubKey.Dispose() } if ($null -ne $BaseHive) { $BaseHive.Close() $BaseHive.Dispose() } throw } else { Write-Warning "New-PSRegistry - Creating registry $RegistryPath on $Computer have failed. Error: $($_.Exception.Message.Replace([System.Environment]::NewLine, " "))" } if (-not $Suppress) { [PSCustomObject] @{ PSComputerName = $Computer PSConnection = $PSConnection PSError = $true PSErrorMessage = $($_.Exception.Message) Path = "$($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName)" } } } } if ($null -ne $SubKey) { $SubKey.Close() $SubKey.Dispose() } if ($null -ne $BaseHive) { $BaseHive.Close() $BaseHive.Dispose() } } function Remove-PrivateRegistry { [cmdletBinding(SupportsShouldProcess)] param( [string] $Computer, [string] $Key, [System.Collections.IDictionary] $RegistryValue, [switch] $Remote, [switch] $Suppress ) $PSConnection = $null $PSError = $null $PSErrorMessage = $null try { if ($Remote) { $BaseHive = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($RegistryValue.HiveKey, $Computer, 0 ) } else { $BaseHive = [Microsoft.Win32.RegistryKey]::OpenBaseKey($RegistryValue.HiveKey, 0 ) } $PSConnection = $true $PSError = $null } catch { $PSConnection = $false $PSError = $($_.Exception.Message) if ($PSBoundParameters.ErrorAction -eq 'Stop') { if ($null -ne $BaseHive) { $BaseHive.Close() $BaseHive.Dispose() } throw } else { Write-Warning "Remove-PSRegistry - Removing registry $($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName) key $($RegistryValue.Key) on $Computer have failed. Error: $($_.Exception.Message.Replace([System.Environment]::NewLine, " "))" } } if ($PSError) { if (-not $Suppress) { [PSCustomObject] @{ PSComputerName = $Computer PSConnection = $PSConnection PSError = $true PSErrorMessage = $PSError Path = "$($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName)" Key = $RegistryValue.Key } } } else { try { if ($Key) { $SubKey = $BaseHive.OpenSubKey($RegistryValue.SubKeyName, $true) if ($PSCmdlet.ShouldProcess($Computer, "Removing registry $($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName) key $($RegistryValue.Key)")) { if ($SubKey) { $SubKey.DeleteValue($RegistryValue.Key, $true) } } else { $PSError = $true $PSErrorMessage = "WhatIf was used. No changes done." } } else { if ($PSCmdlet.ShouldProcess($Computer, "Removing registry $($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName) folder")) { if ($BaseHive) { if ($Recursive) { $BaseHive.DeleteSubKeyTree($RegistryValue.SubKeyName, $true) } else { $BaseHive.DeleteSubKey($RegistryValue.SubKeyName, $true) } } } else { $PSError = $true $PSErrorMessage = "WhatIf was used. No changes done." } } if (-not $Suppress) { [PSCustomObject] @{ PSComputerName = $Computer PSConnection = $PSConnection PSError = $PSError PSErrorMessage = $PSErrorMessage Path = "$($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName)" Key = $RegistryValue.Key } } } catch { if ($PSBoundParameters.ErrorAction -eq 'Stop') { if ($null -ne $SubKey) { $SubKey.Close() $SubKey.Dispose() } if ($null -ne $BaseHive) { $BaseHive.Close() $BaseHive.Dispose() } throw } else { Write-Warning "Remove-PSRegistry - Removing registry $($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName) key $($RegistryValue.Key) on $Computer have failed. Error: $($_.Exception.Message.Replace([System.Environment]::NewLine, " "))" } if (-not $Suppress) { [PSCustomObject] @{ PSComputerName = $Computer PSConnection = $PSConnection PSError = $true PSErrorMessage = $_.Exception.Message Path = "$($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName)" Key = $RegistryValue.Key } } } } if ($null -ne $SubKey) { $SubKey.Close() $SubKey.Dispose() } if ($null -ne $BaseHive) { $BaseHive.Close() $BaseHive.Dispose() } } 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) { return @{ Status = $false; Output = $Service; Extended = 'File with password unreadable.' } } 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 -ErrorAction Stop } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " if ($ErrorMessage -like '*Key not valid for use in specified state*') { if ($Output) { return @{ Status = $false; Output = $Service; Extended = "Couldn't use credentials provided. Most likely using credentials from other user/session/computer." } } else { Write-Warning -Message "Request-Credentials - Couldn't use credentials provided. Most likely using credentials from other user/session/computer." return } } else { if ($Output) { return @{ Status = $false; Output = $Service; Extended = $ErrorMessage } } 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) } else { Try { $SecurePassword = $Password | ConvertTo-SecureString -AsPlainText -Force -ErrorAction Stop } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " if ($ErrorMessage -like '*Key not valid for use in specified state*') { if ($Output) { return @{ Status = $false; Output = $Service; Extended = "Couldn't use credentials provided. Most likely using credentials from other user/session/computer." } } else { Write-Warning -Message "Request-Credentials - Couldn't use credentials provided. Most likely using credentials from other user/session/computer." return } } else { if ($Output) { return @{ Status = $false; Output = $Service; Extended = $ErrorMessage } } else { Write-Warning -Message "Request-Credentials - $ErrorMessage" return } } } $Credentials = New-Object System.Management.Automation.PSCredential($Username, $SecurePassword) } } else { if ($Output) { return @{ Status = $false; Output = $Service; Extended = 'Username or/and Password is empty' } } else { Write-Warning -Message 'Request-Credentials - UserName or Password are empty.' return } } if ($NetworkCredentials) { return $Credentials.GetNetworkCredential() } else { return $Credentials } } function Resolve-PrivateRegistry { [CmdletBinding()] param( [alias('Path')][string[]] $RegistryPath ) foreach ($R in $RegistryPath) { # clean up the path $R = $R.Replace("\\", "\").Replace("\\", "\") # This is to address DEFAULT USER Mapping when after returning value from registry someone takes it and feeds it back to cmdlets If ($R.StartsWith("Users\.DEFAULT_USER") -or $R.StartsWith('HKEY_USERS\.DEFAULT_USER')) { $R = $R.Replace("Users\.DEFAULT_USER", "HKUD") $R.Replace('HKEY_USERS\.DEFAULT_USER', "HKUD") } elseif ($R -like '*:*') { $Found = $false # This makes sure any short HIVES are converted to expected values foreach ($DictionaryKey in $Script:Dictionary.Keys) { $SplitParts = $R.Split("\") $FirstPart = $SplitParts[0] if ($FirstPart -eq $DictionaryKey) { $R -replace $DictionaryKey, $Script:Dictionary[$DictionaryKey] $Found = $true break } } # Lets try to fix things for user if he uses LONG HIVE names but still with colon if (-not $Found) { $R.Replace(":", "") } } else { # This makes sure we do any string without ":" as literal and follow what user wants $R } } } function Set-PrivateRegistry { [cmdletBinding(SupportsShouldProcess)] param( [System.Collections.IDictionary] $RegistryValue, [string] $Computer, [switch] $Remote, [switch] $Suppress ) Write-Verbose -Message "Set-PSRegistry - Setting registry $($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName) on $($RegistryValue.Key) to $($RegistryValue.Value) of $($RegistryValue.ValueKind) on $Computer" if ($RegistryValue.ComputerName) { if ($RegistryValue.ComputerName -ne $Computer) { return } } try { if ($Remote) { $BaseHive = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($RegistryValue.HiveKey, $Computer, 0 ) } else { $BaseHive = [Microsoft.Win32.RegistryKey]::OpenBaseKey($RegistryValue.HiveKey, 0 ) } $PSConnection = $true $PSError = $null } catch { $PSConnection = $false $PSError = $($_.Exception.Message) if ($PSBoundParameters.ErrorAction -eq 'Stop') { if ($null -ne $BaseHive) { $BaseHive.Close() $BaseHive.Dispose() } throw } else { Write-Warning "Set-PSRegistry - Setting registry $($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName) on $($RegistryValue.Key) to $($RegistryValue.Value) of $($RegistryValue.ValueKind) on $Computer have failed. Error: $($_.Exception.Message.Replace([System.Environment]::NewLine, " "))" } } if ($PSCmdlet.ShouldProcess($Computer, "Setting registry $($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName) on $($RegistryValue.Key) to $($RegistryValue.Value) of $($RegistryValue.ValueKind)")) { if ($PSError) { if (-not $Suppress) { [PSCustomObject] @{ PSComputerName = $Computer PSConnection = $PSConnection PSError = $true PSErrorMessage = $PSError Path = "$($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName)" Key = $RegistryValue.Key Value = $RegistryValue.Value Type = $RegistryValue.ValueKind } } } else { try { #$BaseHive = [Microsoft.Win32.RegistryKey]::OpenBaseKey($RegistryValue.HiveKey, 0 ) $SubKey = $BaseHive.OpenSubKey($RegistryValue.SubKeyName, $true) if (-not $SubKey) { $SubKeysSplit = $RegistryValue.SubKeyName.Split('\') $SubKey = $BaseHive.OpenSubKey($SubKeysSplit[0], $true) if (-not $SubKey) { $SubKey = $BaseHive.CreateSubKey($SubKeysSplit[0]) } $SubKey = $BaseHive.OpenSubKey($SubKeysSplit[0], $true) foreach ($S in $SubKeysSplit | Select-Object -Skip 1) { $SubKey = $SubKey.CreateSubKey($S) } } if ($RegistryValue.ValueKind -eq [Microsoft.Win32.RegistryValueKind]::MultiString) { $SubKey.SetValue($RegistryValue.Key, [string[]] $RegistryValue.Value, $RegistryValue.ValueKind) } elseif ($RegistryValue.ValueKind -in [Microsoft.Win32.RegistryValueKind]::None, [Microsoft.Win32.RegistryValueKind]::Binary) { $SubKey.SetValue($RegistryValue.Key, [byte[]] $RegistryValue.Value, $RegistryValue.ValueKind) } else { $SubKey.SetValue($RegistryValue.Key, $RegistryValue.Value, $RegistryValue.ValueKind) } if (-not $Suppress) { [PSCustomObject] @{ PSComputerName = $Computer PSConnection = $PSConnection PSError = $false PSErrorMessage = $null Path = "$($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName)" Key = $RegistryValue.Key Value = $RegistryValue.Value Type = $RegistryValue.ValueKind } } } catch { if ($PSBoundParameters.ErrorAction -eq 'Stop') { if ($null -ne $SubKey) { $SubKey.Close() $SubKey.Dispose() } if ($null -ne $BaseHive) { $BaseHive.Close() $BaseHive.Dispose() } throw } else { Write-Warning "Set-PSRegistry - Setting registry $($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName) on $($RegistryValue.Key) to $($RegistryValue.Value) of $($RegistryValue.ValueKind) on $Computer have failed. Error: $($_.Exception.Message.Replace([System.Environment]::NewLine, " "))" } if (-not $Suppress) { [PSCustomObject] @{ PSComputerName = $Computer PSConnection = $PSConnection PSError = $true PSErrorMessage = $_.Exception.Message Path = "$($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName)" Key = $RegistryValue.Key Value = $RegistryValue.Value Type = $RegistryValue.ValueKind } } } } } else { if (-not $Suppress) { [PSCustomObject] @{ PSComputerName = $Computer PSConnection = $PSConnection PSError = $true PSErrorMessage = if ($PSError) { $PSError } else { "WhatIf used - skipping registry setting" } Path = "$($RegistryValue.HiveKey)\$($RegistryValue.SubKeyName)" Key = $RegistryValue.Key Value = $RegistryValue.Value Type = $RegistryValue.ValueKind } } } if ($null -ne $SubKey) { $SubKey.Close() $SubKey.Dispose() } if ($null -ne $BaseHive) { $BaseHive.Close() $BaseHive.Dispose() } } function Unregister-MountedRegistry { [CmdletBinding()] param( ) if ($null -ne $Script:DefaultRegistryMounted) { Write-Verbose -Message "Unregister-MountedRegistry - Dismounting HKEY_USERS\.DEFAULT_USER" $null = Dismount-PSRegistryPath -MountPoint "HKEY_USERS\.DEFAULT_USER" $Script:DefaultRegistryMounted = $null } if ($null -ne $Script:OfflineRegistryMounted) { foreach ($Key in $Script:OfflineRegistryMounted.Keys) { if ($Script:OfflineRegistryMounted[$Key].Status -eq $true) { Write-Verbose -Message "Unregister-MountedRegistry - Dismounting HKEY_USERS\$Key" $null = Dismount-PSRegistryPath -MountPoint "HKEY_USERS\$Key" } } $Script:OfflineRegistryMounted = $null } } <# 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 Convert-ADGuidToSchema { <# .SYNOPSIS Converts Guid to schema properties .DESCRIPTION Converts Guid to schema properties .PARAMETER Guid Guid to Convert to Schema Name .PARAMETER Domain Domain to query. By default the current domain is used .PARAMETER RootDSE RootDSE to query. By default RootDSE is queried from the domain .PARAMETER DisplayName Return the schema name by display name. By default it returns as Name .EXAMPLE $T2 = '570b9266-bbb3-4fad-a712-d2e3fedc34dd' $T = [guid] '570b9266-bbb3-4fad-a712-d2e3fedc34dd' Convert-ADGuidToSchema -Guid $T Convert-ADGuidToSchema -Guid $T2 .NOTES General notes #> [alias('Get-WinADDomainGUIDs', 'Get-WinADForestGUIDs')] [cmdletbinding()] param( [string] $Guid, [string] $Domain, [Microsoft.ActiveDirectory.Management.ADEntity] $RootDSE, [switch] $DisplayName ) if (-not $Script:ADSchemaMap -or -not $Script:ADSchemaMapDisplayName) { if ($RootDSE) { $Script:RootDSE = $RootDSE } elseif (-not $Script:RootDSE) { if ($Domain) { $Script:RootDSE = Get-ADRootDSE -Server $Domain } else { $Script:RootDSE = Get-ADRootDSE } } $DomainCN = ConvertFrom-DistinguishedName -DistinguishedName $Script:RootDSE.defaultNamingContext -ToDomainCN $QueryServer = (Get-ADDomainController -DomainName $DomainCN -Discover -ErrorAction Stop).Hostname[0] $Script:ADSchemaMap = @{ } $Script:ADSchemaMapDisplayName = @{ } $Script:ADSchemaMapDisplayName['00000000-0000-0000-0000-000000000000'] = 'All' $Script:ADSchemaMap.Add('00000000-0000-0000-0000-000000000000', 'All') Write-Verbose "Convert-ADGuidToSchema - Querying Schema from $QueryServer" $Time = [System.Diagnostics.Stopwatch]::StartNew() if (-not $Script:StandardRights) { $Script:StandardRights = Get-ADObject -SearchBase $Script:RootDSE.schemaNamingContext -LDAPFilter "(schemaidguid=*)" -Properties name, lDAPDisplayName, schemaIDGUID -Server $QueryServer -ErrorAction Stop | Select-Object name, lDAPDisplayName, schemaIDGUID } foreach ($S in $Script:StandardRights) { $Script:ADSchemaMap["$(([System.GUID]$S.schemaIDGUID).Guid)"] = $S.name $Script:ADSchemaMapDisplayName["$(([System.GUID]$S.schemaIDGUID).Guid)"] = $S.lDAPDisplayName } $Time.Stop() $TimeToExecute = "$($Time.Elapsed.Days) days, $($Time.Elapsed.Hours) hours, $($Time.Elapsed.Minutes) minutes, $($Time.Elapsed.Seconds) seconds, $($Time.Elapsed.Milliseconds) milliseconds" Write-Verbose "Convert-ADGuidToSchema - Querying Schema from $QueryServer took $TimeToExecute" Write-Verbose "Convert-ADGuidToSchema - Querying Extended Rights from $QueryServer" $Time = [System.Diagnostics.Stopwatch]::StartNew() #Create a hashtable to store the GUID value of each extended right in the forest if (-not $Script:ExtendedRightsGuids) { $Script:ExtendedRightsGuids = Get-ADObject -SearchBase $Script:RootDSE.ConfigurationNamingContext -LDAPFilter "(&(objectclass=controlAccessRight)(rightsguid=*))" -Properties name, displayName, lDAPDisplayName, rightsGuid -Server $QueryServer -ErrorAction Stop | Select-Object name, displayName, lDAPDisplayName, rightsGuid } foreach ($S in $Script:ExtendedRightsGuids) { $Script:ADSchemaMap["$(([System.GUID]$S.rightsGUID).Guid)"] = $S.name $Script:ADSchemaMapDisplayName["$(([System.GUID]$S.rightsGUID).Guid)"] = $S.displayName } $Time.Stop() $TimeToExecute = "$($Time.Elapsed.Days) days, $($Time.Elapsed.Hours) hours, $($Time.Elapsed.Minutes) minutes, $($Time.Elapsed.Seconds) seconds, $($Time.Elapsed.Milliseconds) milliseconds" Write-Verbose "Convert-ADGuidToSchema - Querying Extended Rights from $QueryServer took $TimeToExecute" } if ($Guid) { if ($DisplayName) { $Script:ADSchemaMapDisplayName[$Guid] } else { $Script:ADSchemaMap[$Guid] } } else { if ($DisplayName) { $Script:ADSchemaMapDisplayName } else { $Script:ADSchemaMap } } } function Convert-ADSchemaToGuid { <# .SYNOPSIS Converts name of schema properties to guids .DESCRIPTION Converts name of schema properties to guids .PARAMETER SchemaName Schema Name to convert to guid .PARAMETER All Get hashtable of all schema properties and their guids .PARAMETER Domain Domain to query. By default the current domain is used .PARAMETER RootDSE RootDSE to query. By default RootDSE is queried from the domain .PARAMETER AsString Return the guid as a string .EXAMPLE Convert-ADSchemaToGuid -SchemaName 'ms-Exch-MSO-Forward-Sync-Cookie' .EXAMPLE Convert-ADSchemaToGuid -SchemaName 'ms-Exch-MSO-Forward-Sync-Cookie' -AsString .NOTES General notes #> [CmdletBinding()] param( [string] $SchemaName, [string] $Domain, [Microsoft.ActiveDirectory.Management.ADEntity] $RootDSE, [switch] $AsString ) if (-not $Script:ADGuidMap -or -not $Script:ADGuidMapString) { if ($RootDSE) { $Script:RootDSE = $RootDSE } elseif (-not $Script:RootDSE) { if ($Domain) { $Script:RootDSE = Get-ADRootDSE -Server $Domain } else { $Script:RootDSE = Get-ADRootDSE } } $DomainCN = ConvertFrom-DistinguishedName -DistinguishedName $Script:RootDSE.defaultNamingContext -ToDomainCN $QueryServer = (Get-ADDomainController -DomainName $DomainCN -Discover -ErrorAction Stop).Hostname[0] # Create a hashtable to store the GUID value of each schema class and attribute $Script:ADGuidMap = [ordered] @{ 'All' = [System.GUID]'00000000-0000-0000-0000-000000000000' } $Script:ADGuidMapString = [ordered] @{ 'All' = '00000000-0000-0000-0000-000000000000' } Write-Verbose "Convert-ADSchemaToGuid - Querying Schema from $QueryServer" $Time = [System.Diagnostics.Stopwatch]::StartNew() if (-not $Script:StandardRights) { $Script:StandardRights = Get-ADObject -SearchBase $Script:RootDSE.schemaNamingContext -LDAPFilter "(schemaidguid=*)" -Properties name, lDAPDisplayName, schemaIDGUID -Server $QueryServer -ErrorAction Stop | Select-Object name, lDAPDisplayName, schemaIDGUID } foreach ($Guid in $Script:StandardRights) { $Script:ADGuidMapString[$Guid.lDAPDisplayName] = ([System.GUID]$Guid.schemaIDGUID).Guid $Script:ADGuidMapString[$Guid.Name] = ([System.GUID]$Guid.schemaIDGUID).Guid $Script:ADGuidMap[$Guid.lDAPDisplayName] = ([System.GUID]$Guid.schemaIDGUID) $Script:ADGuidMap[$Guid.Name] = ([System.GUID]$Guid.schemaIDGUID) } $Time.Stop() $TimeToExecute = "$($Time.Elapsed.Days) days, $($Time.Elapsed.Hours) hours, $($Time.Elapsed.Minutes) minutes, $($Time.Elapsed.Seconds) seconds, $($Time.Elapsed.Milliseconds) milliseconds" Write-Verbose "Convert-ADSchemaToGuid - Querying Schema from $QueryServer took $TimeToExecute" Write-Verbose "Convert-ADSchemaToGuid - Querying Extended Rights from $QueryServer" $Time = [System.Diagnostics.Stopwatch]::StartNew() #Create a hashtable to store the GUID value of each extended right in the forest if (-not $Script:ExtendedRightsGuids) { $Script:ExtendedRightsGuids = Get-ADObject -SearchBase $Script:RootDSE.ConfigurationNamingContext -LDAPFilter "(&(objectclass=controlAccessRight)(rightsguid=*))" -Properties name, displayName, lDAPDisplayName, rightsGuid -Server $QueryServer -ErrorAction Stop | Select-Object name, displayName, lDAPDisplayName, rightsGuid } foreach ($Guid in $Script:ExtendedRightsGuids) { $Script:ADGuidMapString[$Guid.Name] = ([System.GUID]$Guid.RightsGuid).Guid $Script:ADGuidMapString[$Guid.DisplayName] = ([System.GUID]$Guid.RightsGuid).Guid $Script:ADGuidMap[$Guid.Name] = ([System.GUID]$Guid.RightsGuid) $Script:ADGuidMap[$Guid.DisplayName] = ([System.GUID]$Guid.RightsGuid) } $Time.Stop() $TimeToExecute = "$($Time.Elapsed.Days) days, $($Time.Elapsed.Hours) hours, $($Time.Elapsed.Minutes) minutes, $($Time.Elapsed.Seconds) seconds, $($Time.Elapsed.Milliseconds) milliseconds" Write-Verbose "Convert-ADSchemaToGuid - Querying Extended Rights from $QueryServer took $TimeToExecute" } if ($SchemaName) { if ($AsString) { return $Script:ADGuidMapString[$SchemaName] } else { return $Script:ADGuidMap[$SchemaName] } } else { if ($AsString) { $Script:ADGuidMapString } else { $Script:ADGuidMap } } } function Find-ADConnectServer { [alias('Find-ADSyncServer')] param( ) $Description = Get-ADUser -Filter { Name -like "MSOL*" } -Properties Description | Select-Object Description -ExpandProperty Description foreach ($Desc in $Description) { $PatternType = "(?<=(Account created by ))(.*)(?=(with installation identifier))" $PatternServerName = "(?<=(on computer ))(.*)(?=(configured))" $PatternTenantName = "(?<=(to tenant ))(.*)(?=(. This))" $PatternInstallationID = "(?<=(installation identifier ))(.*)(?=( running on ))" if ($Desc -match $PatternServerName) { $ServerName = ($Matches[0]).Replace("'", '').Replace(' ', '') if ($Desc -match $PatternTenantName) { $TenantName = ($Matches[0]).Replace("'", '').Replace(' ', '') } else { $TenantName = '' } if ($Desc -match $PatternInstallationID) { $InstallationID = ($Matches[0]).Replace("'", '').Replace(' ', '') } else { $InstallationID = '' } if ($Desc -match $PatternType) { $Type = ($Matches[0]).Replace("'", '').Replace('by ', '').Replace('the ', '') } else { $Type = '' } $Data = Get-ADComputer -Identity $ServerName [PSCustomObject] @{ Name = $Data.Name FQDN = $Data.DNSHostName DistinguishedName = $Data.DistinguishedName Type = $Type TenantName = $TenantName InstallatioNID = $InstallationID } } } } function Find-ExchangeServer { <# .SYNOPSIS Find Exchange Servers in Active Directory .DESCRIPTION Find Exchange Servers in Active Directory .EXAMPLE Find-ExchangeServer .NOTES General notes #> [CmdletBinding()] param( ) $ExchangeServers = Get-ADGroup -Identity "Exchange Servers" foreach ($Server in $ExchangeServers) { $Data = Get-ADComputer -Identity $Server.SamAccountName -Properties Name, DNSHostName, OperatingSystem, DistinguishedName, ServicePrincipalName [PSCustomObject] @{ Name = $Data.Name FQDN = $Data.DNSHostName OperatingSystem = $Data.OperatingSystem DistinguishedName = $Data.DistinguishedName Enabled = $Data.Enabled } } } function Find-HyperVServer { [cmdletbinding()] param() try { $ADObjects = Get-ADObject -Filter 'ObjectClass -eq "serviceConnectionPoint" -and Name -eq "Microsoft Hyper-V"' -ErrorAction Stop } catch { Write-Error "Error: $_" } foreach ($Server in $ADObjects) { $Temporary = $Server.DistinguishedName.split(",") $DistinguishedName = $Temporary[1..$Temporary.Count] -join "," $Data = Get-ADComputer -Identity $DistinguishedName -Properties Name, DNSHostName, OperatingSystem, DistinguishedName, ServicePrincipalName [PSCustomObject] @{ Name = $Data.Name FQDN = $Data.DNSHostName OperatingSystem = $Data.OperatingSystem DistinguishedName = $Data.DistinguishedName Enabled = $Data.Enabled } } } function Find-ServerTypes { [cmdletbinding()] param( [string[]][ValidateSet('All', 'ADConnect', 'DomainController', 'Exchange', 'Hyper-V', 'RDSLicense', 'SQL', 'VirtualMachine')] $Type = 'All' ) $Forest = Get-ADForest foreach ($Domain in $Forest.Domains) { try { $DomainInformation = Get-ADDomain -Server $Domain -ErrorAction Stop } catch { Write-Warning "Find-ServerTypes - Domain $Domain couldn't be reached. Skipping" continue } try { $ServiceConnectionPoint = Get-ADObject -Filter 'ObjectClass -eq "serviceConnectionPoint"' -ErrorAction Stop -Server $Domain foreach ($Point in $ServiceConnectionPoint) { $Temporary = $Point.DistinguishedName.split(",") $DistinguishedName = $Temporary[1..$Temporary.Count] -join "," $Point | Add-Member -MemberType 'NoteProperty' -Name 'DN' -Value $DistinguishedName -Force } } catch { Write-Error "Find-ServerTypes - Get-ADObject command failed. Terminating. Error $_" return } $ADConnect = Find-ADConnectServer $Computers = Get-ADComputer -Filter * -Properties Name, DNSHostName, OperatingSystem, DistinguishedName, ServicePrincipalName -Server $Domain $Servers = foreach ($Computer in $Computers) { $Services = foreach ($Service in $Computer.servicePrincipalName) { ($Service -split '/')[0] } [PSCustomObject] @{ Name = $Computer.Name FQDN = $Computer.DNSHostName OperatingSystem = $Computer.OperatingSystem DistinguishedName = $Computer.DistinguishedName Enabled = $Computer.Enabled IsExchange = if ($Services -like '*ExchangeMDB*' -or $Services -like '*ExchangeRFR*') { $true } else { $false } IsSql = if ($Services -like '*MSSql*') { $true } else { $false } IsVM = if ($ServiceConnectionPoint.DN -eq $Computer.DistinguishedName -and $ServiceConnectionPoint.Name -eq 'Windows Virtual Machine') { $true } else { $false } IsHyperV = if ($Services -like '*Hyper-V Replica*') { $true } else { $false } IsSPHyperV = if ($ServiceConnectionPoint.DN -eq $Computer.DistinguishedName -and $ServiceConnectionPoint.Name -eq 'Microsoft Hyper-V') { $true } else { $false } IsRDSLicense = if ($ServiceConnectionPoint.DN -eq $Computer.DistinguishedName -and $ServiceConnectionPoint.Name -eq 'TermServLicensing') { $true } else { $false } #IsDC = if ($Services -like '*ldap*' -and $Services -like '*DNS*') { $true } else { $false } IsDC = if ($DomainInformation.ReplicaDirectoryServers -contains $Computer.DNSHostName) { $true } else { $false } IsADConnect = if ($ADConnect.FQDN -eq $Computer.DNSHostName) { $true } else { $false } Forest = $Forest.Name Domain = $Domain ServicePrincipalName = ($Services | Sort-Object -Unique) -Join ',' ServiceConnectionPoint = ($ServiceConnectionPoint | Where-Object { $_.DN -eq $Computer.DistinguishedName }).Name -join ',' } } if ($Type -eq 'All') { $Servers } else { if ($Type -contains 'SQL') { $Servers | Where-Object { $_.IsSql -eq $true } } if ($Type -contains 'Exchange' ) { $Servers | Where-Object { $_.IsExchange -eq $true } } if ($Type -contains 'Hyper-V') { $Servers | Where-Object { $_.IsHyperV -eq $true -or $_.IsSPHyperV -eq $true } } if ($Type -contains 'VirtualMachine') { $Servers | Where-Object { $_.IsVM -eq $true } } if ($Type -contains 'RDSLicense') { $Servers | Where-Object { $_.IsRDSLicense -eq $true } } if ($Type -contains 'DomainController') { $Servers | Where-Object { $_.IsDC -eq $true } } if ($Type -contains 'DomainController') { $Servers | Where-Object { $_.IsDC -eq $true } } if ($Type -contains 'ADConnect') { $Servers | Where-Object { $_.IsADConnect -eq $true } } } } } # not sure why I have it, or where it's used. Need to find out function Find-UsersProxyAddressesStatus { param( $User ) $status = 'No proxy' if ($null -ne $user.proxyAddresses) { $count = 0 foreach ($proxy in $($user.ProxyAddresses)) { if ($proxy.SubString(0, 4) -ceq 'SMTP') { $count++ } } if ($count -eq 0) { $status = 'Missing primary proxy' } elseif ($count -gt 1) { $status = 'Multiple primary proxy' } else { $status = 'All OK' } } else { $status = 'Missing all proxy' } return $status } function Get-ADADministrativeGroups { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER Type Parameter description .PARAMETER Forest Parameter description .PARAMETER ExcludeDomains Parameter description .PARAMETER IncludeDomains Parameter description .PARAMETER ExtendedForestInformation Parameter description .EXAMPLE Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins Output (Where VALUE is Get-ADGroup output): Name Value ---- ----- ByNetBIOS {EVOTEC\Domain Admins, EVOTEC\Enterprise Admins, EVOTECPL\Domain Admins} ad.evotec.xyz {DomainAdmins, EnterpriseAdmins} ad.evotec.pl {DomainAdmins} .NOTES General notes #> [cmdletBinding()] param( [parameter(Mandatory)][validateSet('DomainAdmins', 'EnterpriseAdmins')][string[]] $Type, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation ) $ADDictionary = [ordered] @{ } $ADDictionary['ByNetBIOS'] = [ordered] @{ } $ADDictionary['BySID'] = [ordered] @{ } $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation foreach ($Domain in $ForestInformation.Domains) { $ADDictionary[$Domain] = [ordered] @{ } $QueryServer = $ForestInformation['QueryServers'][$Domain]['HostName'][0] $DomainInformation = Get-ADDomain -Server $QueryServer if ($Type -contains 'DomainAdmins') { Get-ADGroup -Filter "SID -eq '$($DomainInformation.DomainSID)-512'" -Server $QueryServer -ErrorAction SilentlyContinue | ForEach-Object { $ADDictionary['ByNetBIOS']["$($DomainInformation.NetBIOSName)\$($_.Name)"] = $_ $ADDictionary[$Domain]['DomainAdmins'] = "$($DomainInformation.NetBIOSName)\$($_.Name)" $ADDictionary['BySID'][$_.SID.Value] = $_ } } } # We need to treat EnterpriseAdmins separatly as it should be always available, not only when requested specific domain foreach ($Domain in $ForestInformation.Forest.Domains) { if (-not $ADDictionary[$Domain]) { $ADDictionary[$Domain] = [ordered] @{ } } if ($Type -contains 'EnterpriseAdmins') { $QueryServer = $ForestInformation['QueryServers'][$Domain]['HostName'][0] $DomainInformation = Get-ADDomain -Server $QueryServer Get-ADGroup -Filter "SID -eq '$($DomainInformation.DomainSID)-519'" -Server $QueryServer -ErrorAction SilentlyContinue | ForEach-Object { $ADDictionary['ByNetBIOS']["$($DomainInformation.NetBIOSName)\$($_.Name)"] = $_ $ADDictionary[$Domain]['EnterpriseAdmins'] = "$($DomainInformation.NetBIOSName)\$($_.Name)" $ADDictionary['BySID'][$_.SID.Value] = $_ #"$($DomainInformation.NetBIOSName)\$($_.Name)" } } } return $ADDictionary } #Get-WinADForestDetails -IncludeDomains 'ad.evotec.xyz' #Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins -IncludeDomains 'ad.evotec.pl' function Get-ADEncryptionTypes { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER Value Parameter description .EXAMPLE Get-ADEncryptionTypes -Value 24 Output: AES128-CTS-HMAC-SHA1-96 AES256-CTS-HMAC-SHA1-96 .NOTES General notes #> [cmdletbinding()] Param( [parameter(Mandatory = $false, ValueFromPipeline = $True)][int32]$Value ) # https://lists.samba.org/archive/cifs-protocol/attachments/20100111/e8bb3520/attachment-0001.pdf # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/6cfc7b50-11ed-4b4d-846d-6f08f0812919 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-kile/1163bb03-7035-433e-b5a4-802247262d18#Appendix_A_9 [String[]]$EncryptionTypes = @( Foreach ($V in $Value) { if ([int32]$V -band 0x00000001) { "DES-CBC-CRC" } # KERB_ENCTYPE_DES_CBC_CRC -> CRC if ([int32]$V -band 0x00000002) { "DES-CBC-MD5" } # KERB_ENCTYPE_DES_CBC_MD5 -> MD5 if ([int32]$V -band 0x00000004) { "RC4-HMAC" } # KERB_ENCTYPE_RC4_HMAC_MD5 -> RC4 if ([int32]$V -band 0x00000008) { "AES128-CTS-HMAC-SHA1-96" } # KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 -> A128 if ([int32]$V -band 0x00000010) { "AES256-CTS-HMAC-SHA1-96" } # KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96 -> A256 if ([int32]$V -band 0x00000020) { "FAST-supported" } # if ([int32]$V -band 0x00000040) { "Compound-identity-supported" } # if ([int32]$V -band 0x00000080) { "Claims-supported" } # if ([int32]$V -band 0x00000200) { "Resource-SID-compression-disabled" } # } ) $EncryptionTypes } function Get-ADTrustAttributes { [cmdletbinding()] Param( [parameter(Mandatory = $false, ValueFromPipeline = $True)][int32]$Value ) # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/e9a2d23c-c31e-4a6f-88a0-6646fdb51a3c [String[]]$TrustAttributes = @( Foreach ($V in $Value) { if ([int32]$V -band 0x00000001) { "Non Transitive" } # TRUST_ATTRIBUTE_NON_TRANSITIVE if ([int32]$V -band 0x00000002) { "UpLevel Only" } # TRUST_ATTRIBUTE_UPLEVEL_ONLY if ([int32]$V -band 0x00000004) { "Quarantined Domain" } # TRUST_ATTRIBUTE_QUARANTINED_DOMAIN if ([int32]$V -band 0x00000008) { "Forest Transitive" } # TRUST_ATTRIBUTE_FOREST_TRANSITIVE if ([int32]$V -band 0x00000010) { "Cross Organization" } #TRUST_ATTRIBUTE_CROSS_ORGANIZATION if ([int32]$V -band 0x00000020) { "Within Forest" } # TRUST_ATTRIBUTE_WITHIN_FOREST if ([int32]$V -band 0x00000040) { "Treat as External" } # TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL if ([int32]$V -band 0x00000080) { "Uses RC4 Encryption" } # TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION if ([int32]$V -band 0x00000200) { "No TGT DELEGATION" } # TRUST_ATTRIBUTE_CROSS_ORGANIZATION_NO_TGT_DELEGATION if ([int32]$V -band 0x00000800) { "Enable TGT DELEGATION" } #TRUST_ATTRIBUTE_CROSS_ORGANIZATION_ENABLE_TGT_DELEGATION if ([int32]$V -band 0x00000400) { "PIM Trust" } #TRUST_ATTRIBUTE_PIM_TRUST } ) return $TrustAttributes } function Get-WinADForestControllers { [alias('Get-WinADDomainControllers')] <# .SYNOPSIS .DESCRIPTION Long description .PARAMETER TestAvailability Parameter description .EXAMPLE Get-WinADForestControllers -TestAvailability | Format-Table .EXAMPLE Get-WinADDomainControllers .EXAMPLE Get-WinADDomainControllers -Credential $Credential .EXAMPLE Get-WinADDomainControllers | Format-Table * Output: Domain HostName Forest IPV4Address IsGlobalCatalog IsReadOnly SchemaMaster DomainNamingMasterMaster PDCEmulator RIDMaster InfrastructureMaster Comment ------ -------- ------ ----------- --------------- ---------- ------------ ------------------------ ----------- --------- -------------------- ------- ad.evotec.xyz AD1.ad.evotec.xyz ad.evotec.xyz 192.168.240.189 True False True True True True True ad.evotec.xyz AD2.ad.evotec.xyz ad.evotec.xyz 192.168.240.192 True False False False False False False ad.evotec.pl ad.evotec.xyz False False False False False Unable to contact the server. This may be becau... .NOTES General notes #> [CmdletBinding()] param( [string[]] $Domain, [switch] $TestAvailability, [switch] $SkipEmpty, [pscredential] $Credential ) try { if ($Credential) { $Forest = Get-ADForest -Credential $Credential } else { $Forest = Get-ADForest } if (-not $Domain) { $Domain = $Forest.Domains } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " Write-Warning "Get-WinADForestControllers - Couldn't use Get-ADForest feature. Error: $ErrorMessage" return } $Servers = foreach ($D in $Domain) { try { $LocalServer = Get-ADDomainController -Discover -DomainName $D -ErrorAction Stop -Writable if ($Credential) { $DC = Get-ADDomainController -Server $LocalServer.HostName[0] -Credential $Credential -Filter * -ErrorAction Stop } else { $DC = Get-ADDomainController -Server $LocalServer.HostName[0] -Filter * -ErrorAction Stop } foreach ($S in $DC) { $Server = [ordered] @{ Domain = $D HostName = $S.HostName Name = $S.Name Forest = $Forest.RootDomain IPV4Address = $S.IPV4Address IPV6Address = $S.IPV6Address IsGlobalCatalog = $S.IsGlobalCatalog IsReadOnly = $S.IsReadOnly Site = $S.Site SchemaMaster = ($S.OperationMasterRoles -contains 'SchemaMaster') DomainNamingMaster = ($S.OperationMasterRoles -contains 'DomainNamingMaster') PDCEmulator = ($S.OperationMasterRoles -contains 'PDCEmulator') RIDMaster = ($S.OperationMasterRoles -contains 'RIDMaster') InfrastructureMaster = ($S.OperationMasterRoles -contains 'InfrastructureMaster') LdapPort = $S.LdapPort SslPort = $S.SslPort Pingable = $null Comment = '' } if ($TestAvailability) { $Server['Pingable'] = foreach ($_ in $Server.IPV4Address) { Test-Connection -Count 1 -Server $_ -Quiet -ErrorAction SilentlyContinue } } [PSCustomObject] $Server } } catch { [PSCustomObject]@{ Domain = $D HostName = '' Name = '' Forest = $Forest.RootDomain IPV4Address = '' IPV6Address = '' IsGlobalCatalog = '' IsReadOnly = '' Site = '' SchemaMaster = $false DomainNamingMasterMaster = $false PDCEmulator = $false RIDMaster = $false InfrastructureMaster = $false LdapPort = '' SslPort = '' Pingable = $null Comment = $_.Exception.Message -replace "`n", " " -replace "`r", " " } } } if ($SkipEmpty) { return $Servers | Where-Object { $_.HostName -ne '' } } return $Servers } function Get-WinADForestDetails { [CmdletBinding()] param( [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [string[]] $ExcludeDomainControllers, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [alias('DomainControllers', 'ComputerName')][string[]] $IncludeDomainControllers, [switch] $SkipRODC, [string] $Filter = '*', [switch] $TestAvailability, [ValidateSet('All', 'Ping', 'WinRM', 'PortOpen', 'Ping+WinRM', 'Ping+PortOpen', 'WinRM+PortOpen')] $Test = 'All', [int[]] $Ports = 135, [int] $PortsTimeout = 100, [int] $PingCount = 1, [switch] $PreferWritable, [switch] $Extended, [System.Collections.IDictionary] $ExtendedForestInformation ) if ($Global:ProgressPreference -ne 'SilentlyContinue') { $TemporaryProgress = $Global:ProgressPreference $Global:ProgressPreference = 'SilentlyContinue' } if (-not $ExtendedForestInformation) { # standard situation, building data from AD $Findings = [ordered] @{ } try { if ($Forest) { $ForestInformation = Get-ADForest -ErrorAction Stop -Identity $Forest } else { $ForestInformation = Get-ADForest -ErrorAction Stop } <# $ForestInformation = [ordered] @{ ApplicationPartitions = $ForestInf.ApplicationPartitions | ForEach-Object -Process { $_ } # : {DC=DomainDnsZones,DC=ad,DC=evotec,DC=xyz, DC=DomainDnsZones,DC=ad,DC=evotec,DC=pl, DC=ForestDnsZones,DC=ad,DC=evotec,DC=xyz} CrossForestReferences = $ForestInf.CrossForestReferences | ForEach-Object -Process { $_ } # : {} DomainNamingMaster = $ForestInf.DomainNamingMaster # : AD1.ad.evotec.xyz Domains = $ForestInf.Domains | ForEach-Object -Process { $_ } # : {ad.evotec.xyz, ad.evotec.pl} ForestMode = $ForestInf.ForestMode # : Windows2012R2Forest GlobalCatalogs = $ForestInf.GlobalCatalogs | ForEach-Object -Process { $_ } # : {AD1.ad.evotec.xyz, AD2.ad.evotec.xyz, ADRODC.ad.evotec.pl, AD3.ad.evotec.xyz...} Name = $ForestInf.Name # : ad.evotec.xyz PartitionsContainer = $ForestInf.PartitionsContainer # : CN=Partitions,CN=Configuration,DC=ad,DC=evotec,DC=xyz RootDomain = $ForestInf.RootDomain # : ad.evotec.xyz SchemaMaster = $ForestInf.SchemaMaster # : AD1.ad.evotec.xyz Sites = $ForestInf.Sites | ForEach-Object -Process { $_ } # : {KATOWICE-1, KATOWICE-2} SPNSuffixes = $ForestInf.SPNSuffixes | ForEach-Object -Process { $_ } # : {} UPNSuffixes = $ForestInf.UPNSuffixes | ForEach-Object -Process { $_ } # : {myneva.eu, single.evotec.xyz, newUPN@com, evotec.xyz...} } #> } catch { Write-Warning "Get-WinADForestDetails - Error discovering DC for Forest - $($_.Exception.Message)" return } if (-not $ForestInformation) { return } $Findings['Forest'] = $ForestInformation $Findings['ForestDomainControllers'] = @() $Findings['QueryServers'] = @{ } $Findings['DomainDomainControllers'] = @{ } [Array] $Findings['Domains'] = foreach ($Domain in $ForestInformation.Domains) { if ($IncludeDomains) { if ($Domain -in $IncludeDomains) { $Domain.ToLower() } # We skip checking for exclusions continue } if ($Domain -notin $ExcludeDomains) { $Domain.ToLower() } } # We want to have QueryServers always available for all domains [Array] $DomainsActive = foreach ($Domain in $Findings['Forest'].Domains) { try { $DC = Get-ADDomainController -DomainName $Domain -Discover -ErrorAction Stop -Writable:$PreferWritable.IsPresent $OrderedDC = [ordered] @{ Domain = $DC.Domain Forest = $DC.Forest HostName = [Array] $DC.HostName IPv4Address = $DC.IPv4Address IPv6Address = $DC.IPv6Address Name = $DC.Name Site = $DC.Site } } catch { Write-Warning "Get-WinADForestDetails - Error discovering DC for domain $Domain - $($_.Exception.Message)" continue } if ($Domain -eq $Findings['Forest']['Name']) { $Findings['QueryServers']['Forest'] = $OrderedDC } $Findings['QueryServers']["$Domain"] = $OrderedDC # lets return domain as something that wroks $Domain } # we need to make sure to remove domains that don't have DCs for some reason [Array] $Findings['Domains'] = foreach ($Domain in $Findings['Domains']) { if ($Domain -notin $DomainsActive) { Write-Warning "Get-WinADForestDetails - Domain $Domain doesn't seem to be active (no DCs). Skipping." continue } $Domain } [Array] $Findings['ForestDomainControllers'] = foreach ($Domain in $Findings.Domains) { $QueryServer = $Findings['QueryServers'][$Domain]['HostName'][0] [Array] $AllDC = try { try { $DomainControllers = Get-ADDomainController -Filter $Filter -Server $QueryServer -ErrorAction Stop } catch { Write-Warning "Get-WinADForestDetails - Error listing DCs for domain $Domain - $($_.Exception.Message)" continue } foreach ($S in $DomainControllers) { if ($IncludeDomainControllers.Count -gt 0) { If (-not $IncludeDomainControllers[0].Contains('.')) { if ($S.Name -notin $IncludeDomainControllers) { continue } } else { if ($S.HostName -notin $IncludeDomainControllers) { continue } } } if ($ExcludeDomainControllers.Count -gt 0) { If (-not $ExcludeDomainControllers[0].Contains('.')) { if ($S.Name -in $ExcludeDomainControllers) { continue } } else { if ($S.HostName -in $ExcludeDomainControllers) { continue } } } $Server = [ordered] @{ Domain = $Domain HostName = $S.HostName Name = $S.Name Forest = $ForestInformation.RootDomain Site = $S.Site IPV4Address = $S.IPV4Address IPV6Address = $S.IPV6Address IsGlobalCatalog = $S.IsGlobalCatalog IsReadOnly = $S.IsReadOnly IsSchemaMaster = ($S.OperationMasterRoles -contains 'SchemaMaster') IsDomainNamingMaster = ($S.OperationMasterRoles -contains 'DomainNamingMaster') IsPDC = ($S.OperationMasterRoles -contains 'PDCEmulator') IsRIDMaster = ($S.OperationMasterRoles -contains 'RIDMaster') IsInfrastructureMaster = ($S.OperationMasterRoles -contains 'InfrastructureMaster') OperatingSystem = $S.OperatingSystem OperatingSystemVersion = $S.OperatingSystemVersion OperatingSystemLong = ConvertTo-OperatingSystem -OperatingSystem $S.OperatingSystem -OperatingSystemVersion $S.OperatingSystemVersion LdapPort = $S.LdapPort SslPort = $S.SslPort DistinguishedName = $S.ComputerObjectDN Pingable = $null WinRM = $null PortOpen = $null Comment = '' } if ($TestAvailability) { if ($Test -eq 'All' -or $Test -like 'Ping*') { $Server.Pingable = Test-Connection -ComputerName $Server.IPV4Address -Quiet -Count $PingCount } if ($Test -eq 'All' -or $Test -like '*WinRM*') { $Server.WinRM = (Test-WinRM -ComputerName $Server.HostName).Status } if ($Test -eq 'All' -or '*PortOpen*') { $Server.PortOpen = (Test-ComputerPort -Server $Server.HostName -PortTCP $Ports -Timeout $PortsTimeout).Status } } [PSCustomObject] $Server } } catch { [PSCustomObject]@{ Domain = $Domain HostName = '' Name = '' Forest = $ForestInformation.RootDomain IPV4Address = '' IPV6Address = '' IsGlobalCatalog = '' IsReadOnly = '' Site = '' SchemaMaster = $false DomainNamingMasterMaster = $false PDCEmulator = $false RIDMaster = $false InfrastructureMaster = $false LdapPort = '' SslPort = '' DistinguishedName = '' Pingable = $null WinRM = $null PortOpen = $null Comment = $_.Exception.Message -replace "`n", " " -replace "`r", " " } } if ($SkipRODC) { [Array] $Findings['DomainDomainControllers'][$Domain] = $AllDC | Where-Object { $_.IsReadOnly -eq $false } #$Findings[$Domain] = $AllDC | Where-Object { $_.IsReadOnly -eq $false } } else { [Array] $Findings['DomainDomainControllers'][$Domain] = $AllDC #$Findings[$Domain] = $AllDC } # Building all DCs for whole Forest [Array] $Findings['DomainDomainControllers'][$Domain] } if ($Extended) { $Findings['DomainsExtended'] = @{ } $Findings['DomainsExtendedNetBIOS'] = @{ } foreach ($DomainEx in $Findings['Domains']) { try { #$Findings['DomainsExtended'][$DomainEx] = Get-ADDomain -Server $Findings['QueryServers'][$DomainEx].HostName[0] $Findings['DomainsExtended'][$DomainEx] = Get-ADDomain -Server $Findings['QueryServers'][$DomainEx].HostName[0] | ForEach-Object { # We need to use ForEach-Object to convert ADPropertyValueCollection to normal strings. Otherwise Copy-Dictionary fails #True False ADPropertyValueCollection System.Collections.CollectionBase [ordered] @{ AllowedDNSSuffixes = $_.AllowedDNSSuffixes | ForEach-Object -Process { $_ } #: { } ChildDomains = $_.ChildDomains | ForEach-Object -Process { $_ } #: { } ComputersContainer = $_.ComputersContainer #: CN = Computers, DC = ad, DC = evotec, DC = xyz DeletedObjectsContainer = $_.DeletedObjectsContainer #: CN = Deleted Objects, DC = ad, DC = evotec, DC = xyz DistinguishedName = $_.DistinguishedName #: DC = ad, DC = evotec, DC = xyz DNSRoot = $_.DNSRoot #: ad.evotec.xyz DomainControllersContainer = $_.DomainControllersContainer #: OU = Domain Controllers, DC = ad, DC = evotec, DC = xyz DomainMode = $_.DomainMode #: Windows2012R2Domain DomainSID = $_.DomainSID.Value #: S - 1 - 5 - 21 - 853615985 - 2870445339 - 3163598659 ForeignSecurityPrincipalsContainer = $_.ForeignSecurityPrincipalsContainer #: CN = ForeignSecurityPrincipals, DC = ad, DC = evotec, DC = xyz Forest = $_.Forest #: ad.evotec.xyz InfrastructureMaster = $_.InfrastructureMaster #: AD1.ad.evotec.xyz LastLogonReplicationInterval = $_.LastLogonReplicationInterval #: LinkedGroupPolicyObjects = $_.LinkedGroupPolicyObjects | ForEach-Object -Process { $_ } #: LostAndFoundContainer = $_.LostAndFoundContainer #: CN = LostAndFound, DC = ad, DC = evotec, DC = xyz ManagedBy = $_.ManagedBy #: Name = $_.Name #: ad NetBIOSName = $_.NetBIOSName #: EVOTEC ObjectClass = $_.ObjectClass #: domainDNS ObjectGUID = $_.ObjectGUID #: bc875580 - 4c70-41ad-a487-c57337e26024 ParentDomain = $_.ParentDomain #: PDCEmulator = $_.PDCEmulator #: AD1.ad.evotec.xyz PublicKeyRequiredPasswordRolling = $_.PublicKeyRequiredPasswordRolling | ForEach-Object -Process { $_ } #: QuotasContainer = $_.QuotasContainer #: CN = NTDS Quotas, DC = ad, DC = evotec, DC = xyz ReadOnlyReplicaDirectoryServers = $_.ReadOnlyReplicaDirectoryServers | ForEach-Object -Process { $_ } #: { } ReplicaDirectoryServers = $_.ReplicaDirectoryServers | ForEach-Object -Process { $_ } #: { AD1.ad.evotec.xyz, AD2.ad.evotec.xyz, AD3.ad.evotec.xyz } RIDMaster = $_.RIDMaster #: AD1.ad.evotec.xyz SubordinateReferences = $_.SubordinateReferences | ForEach-Object -Process { $_ } #: { DC = ForestDnsZones, DC = ad, DC = evotec, DC = xyz, DC = DomainDnsZones, DC = ad, DC = evotec, DC = xyz, CN = Configuration, DC = ad, DC = evotec, DC = xyz } SystemsContainer = $_.SystemsContainer #: CN = System, DC = ad, DC = evotec, DC = xyz UsersContainer = $_.UsersContainer #: CN = Users, DC = ad, DC = evotec, DC = xyz } } $NetBios = $Findings['DomainsExtended'][$DomainEx]['NetBIOSName'] $Findings['DomainsExtendedNetBIOS'][$NetBios] = $Findings['DomainsExtended'][$DomainEx] } catch { Write-Warning "Get-WinADForestDetails - Error gathering Domain Information for domain $DomainEx - $($_.Exception.Message)" continue } } } # Bring back setting as per default if ($TemporaryProgress) { $Global:ProgressPreference = $TemporaryProgress } $Findings } else { # this takes care of limiting output to only what we requested, but based on prior input # this makes sure we ask once for all AD stuff and then subsequent calls just filter out things # this should be much faster then asking again and again for stuff from AD $Findings = Copy-DictionaryManual -Dictionary $ExtendedForestInformation [Array] $Findings['Domains'] = foreach ($_ in $Findings.Domains) { if ($IncludeDomains) { if ($_ -in $IncludeDomains) { $_.ToLower() } # We skip checking for exclusions continue } if ($_ -notin $ExcludeDomains) { $_.ToLower() } } # Now that we have Domains we need to remove all DCs that are not from domains we excluded or included foreach ($_ in [string[]] $Findings.DomainDomainControllers.Keys) { if ($_ -notin $Findings.Domains) { $Findings.DomainDomainControllers.Remove($_) } } # Same as above but for query servers - we don't remove queried servers #foreach ($_ in [string[]] $Findings.QueryServers.Keys) { # if ($_ -notin $Findings.Domains -and $_ -ne 'Forest') { # $Findings.QueryServers.Remove($_) # } #} # Now that we have Domains we need to remove all Domains that are excluded or included foreach ($_ in [string[]] $Findings.DomainsExtended.Keys) { if ($_ -notin $Findings.Domains) { $Findings.DomainsExtended.Remove($_) $NetBiosName = $Findings.DomainsExtended.$_.'NetBIOSName' if ($NetBiosName) { $Findings.DomainsExtendedNetBIOS.Remove($NetBiosName) } } } [Array] $Findings['ForestDomainControllers'] = foreach ($Domain in $Findings.Domains) { [Array] $AllDC = foreach ($S in $Findings.DomainDomainControllers["$Domain"]) { if ($IncludeDomainControllers.Count -gt 0) { If (-not $IncludeDomainControllers[0].Contains('.')) { if ($S.Name -notin $IncludeDomainControllers) { continue } } else { if ($S.HostName -notin $IncludeDomainControllers) { continue } } } if ($ExcludeDomainControllers.Count -gt 0) { If (-not $ExcludeDomainControllers[0].Contains('.')) { if ($S.Name -in $ExcludeDomainControllers) { continue } } else { if ($S.HostName -in $ExcludeDomainControllers) { continue } } } $S } if ($SkipRODC) { [Array] $Findings['DomainDomainControllers'][$Domain] = $AllDC | Where-Object { $_.IsReadOnly -eq $false } } else { [Array] $Findings['DomainDomainControllers'][$Domain] = $AllDC } # Building all DCs for whole Forest [Array] $Findings['DomainDomainControllers'][$Domain] } $Findings } } #$F = Get-WinADForestDetails -Forest 'test.evotec.pl' #$F #$F.DomainDomainControllers['ad.evotec.xyz'] | Format-Table -AutoSize #$F <# $F = Get-WinADForestDetails -SkipRODC -ExcludeDomainControllers 'AD1.ad.evotec.xyz' #-TestAvailability #-IncludeDomains 'ad.evotec.xyz' $F | Format-Table -AutoSize $F.'ad.evotec.xyz' | Format-Table -AutoSize * $F.'ad.evotec.pl' | Format-Table -AutoSize * #> Function Get-WinADForestOptions { <# .SYNOPSIS This Cmdlet gets Active Directory Site Options. .DESCRIPTION This Cmdlet gets Active Directory Site Options. We can fill out the rest of this comment-based help later. .LINK http://myotherpcisacloud.com .LINK https://serverfault.com/questions/543143/detecting-ad-site-options-using-powershell .NOTES Written by Ryan Ries, October 2013. ryanries09@gmail.com. #> [CmdletBinding()] Param( [string] $Domain = $Env:USERDNSDOMAIN ) BEGIN { # This enum comes from NtDsAPI.h in the Windows SDK. # Also thanks to Jason Scott for pointing it out to me. http://serverfault.com/users/23067/jscott Add-Type -TypeDefinition @" [System.Flags] public enum nTDSSiteSettingsFlags { NTDSSETTINGS_OPT_IS_AUTO_TOPOLOGY_DISABLED = 0x00000001, NTDSSETTINGS_OPT_IS_TOPL_CLEANUP_DISABLED = 0x00000002, NTDSSETTINGS_OPT_IS_TOPL_MIN_HOPS_DISABLED = 0x00000004, NTDSSETTINGS_OPT_IS_TOPL_DETECT_STALE_DISABLED = 0x00000008, NTDSSETTINGS_OPT_IS_INTER_SITE_AUTO_TOPOLOGY_DISABLED = 0x00000010, NTDSSETTINGS_OPT_IS_GROUP_CACHING_ENABLED = 0x00000020, NTDSSETTINGS_OPT_FORCE_KCC_WHISTLER_BEHAVIOR = 0x00000040, NTDSSETTINGS_OPT_FORCE_KCC_W2K_ELECTION = 0x00000080, NTDSSETTINGS_OPT_IS_RAND_BH_SELECTION_DISABLED = 0x00000100, NTDSSETTINGS_OPT_IS_SCHEDULE_HASHING_ENABLED = 0x00000200, NTDSSETTINGS_OPT_IS_REDUNDANT_SERVER_TOPOLOGY_ENABLED = 0x00000400, NTDSSETTINGS_OPT_W2K3_IGNORE_SCHEDULES = 0x00000800, NTDSSETTINGS_OPT_W2K3_BRIDGES_REQUIRED = 0x00001000 } "@ if ($Domain) { $RootDSE = Get-ADRootDSE -Server $Domain } else { $RootDSE = Get-ADRootDSE } $DomainCN = ConvertFrom-DistinguishedName -DistinguishedName $RootDSE.defaultNamingContext -ToDomainCN $QueryServer = (Get-ADDomainController -DomainName $DomainCN -Discover -ErrorAction Stop).Hostname[0] $Sites = Get-ADObject -Filter 'objectClass -eq "site"' -SearchBase ($RootDSE).ConfigurationNamingContext -Server $QueryServer ForEach ($Site In $Sites) { $SiteSettings = Get-ADObject "CN=NTDS Site Settings,$($Site.DistinguishedName)" -Properties Options -Server $QueryServer If (!$SiteSettings.PSObject.Properties.Match('Options').Count -OR $SiteSettings.Options -EQ 0) { # I went with '(none)' here to give it a more classic repadmin.exe feel. # You could also go with $Null, or omit the property altogether for a more modern, Powershell feel. [PSCustomObject]@{ SiteName = $Site.Name DistinguishedName = $Site.DistinguishedName SiteOptions = '(none)' } } Else { [PSCustomObject]@{ SiteName = $Site.Name DistinguishedName = $Site.DistinguishedName SiteOptions = [Enum]::Parse('nTDSSiteSettingsFlags', $SiteSettings.Options) } } } } } 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 } 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 } <# $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 Get-CimData { <# .SYNOPSIS Helper function for retreiving CIM data from local and remote computers .DESCRIPTION Helper function for retreiving CIM data from local and remote computers .PARAMETER ComputerName Specifies computer on which you want to run the CIM operation. You can specify a fully qualified domain name (FQDN), a NetBIOS name, or an IP address. If you do not specify this parameter, the cmdlet performs the operation on the local computer using Component Object Model (COM). .PARAMETER Protocol Specifies the protocol to use. The acceptable values for this parameter are: DCOM, Default, or Wsman. .PARAMETER Class Specifies the name of the CIM class for which to retrieve the CIM instances. You can use tab completion to browse the list of classes, because PowerShell gets a list of classes from the local WMI server to provide a list of class names. .PARAMETER Properties Specifies a set of instance properties to retrieve. Use this parameter when you need to reduce the size of the object returned, either in memory or over the network. The object returned also contains the key properties even if you have not listed them using the Property parameter. Other properties of the class are present but they are not populated. .EXAMPLE Get-CimData -Class 'win32_bios' -ComputerName AD1,EVOWIN Get-CimData -Class 'win32_bios' # Get-CimClass to get all classes .NOTES General notes #> [CmdletBinding()] param( [parameter(Mandatory)][string] $Class, [string] $NameSpace = 'root\cimv2', [string[]] $ComputerName = $Env:COMPUTERNAME, [ValidateSet('Default', 'Dcom', 'Wsman')][string] $Protocol = 'Default', [string[]] $Properties = '*' ) $ExcludeProperties = 'CimClass', 'CimInstanceProperties', 'CimSystemProperties', 'SystemCreationClassName', 'CreationClassName' # Querying CIM locally usually doesn't work. This means if you're querying same computer you neeed to skip CimSession/ComputerName if it's local query [Array] $ComputersSplit = Get-ComputerSplit -ComputerName $ComputerName $CimObject = @( # requires removal of this property for query [string[]] $PropertiesOnly = $Properties | Where-Object { $_ -ne 'PSComputerName' } # Process all remote computers $Computers = $ComputersSplit[1] if ($Computers.Count -gt 0) { if ($Protocol = 'Default') { Get-CimInstance -ClassName $Class -ComputerName $Computers -ErrorAction SilentlyContinue -Property $PropertiesOnly -Namespace $NameSpace -Verbose:$false -ErrorVariable ErrorsToProcess | Select-Object -Property $Properties -ExcludeProperty $ExcludeProperties } else { $Option = New-CimSessionOption -Protocol $Protocol $Session = New-CimSession -ComputerName $Computers -SessionOption $Option -ErrorAction SilentlyContinue $Info = Get-CimInstance -ClassName $Class -CimSession $Session -ErrorAction SilentlyContinue -Property $PropertiesOnly -Namespace $NameSpace -Verbose:$false -ErrorVariable ErrorsToProcess | Select-Object -Property $Properties -ExcludeProperty $ExcludeProperties $null = Remove-CimSession -CimSession $Session -ErrorAction SilentlyContinue $Info } } foreach ($E in $ErrorsToProcess) { Write-Warning -Message "Get-CimData - No data for computer $($E.OriginInfo.PSComputerName). Failed with errror: $($E.Exception.Message)" } # Process local computer $Computers = $ComputersSplit[0] if ($Computers.Count -gt 0) { $Info = Get-CimInstance -ClassName $Class -ErrorAction SilentlyContinue -Property $PropertiesOnly -Namespace $NameSpace -Verbose:$false -ErrorVariable ErrorsLocal | Select-Object -Property $Properties -ExcludeProperty $ExcludeProperties $Info | Add-Member -Name 'PSComputerName' -Value $Computers -MemberType NoteProperty -Force $Info } foreach ($E in $ErrorsLocal) { Write-Warning -Message "Get-CimData - No data for computer $($Env:COMPUTERNAME). Failed with errror: $($E.Exception.Message)" } ) $CimObject } function Get-Computer { [cmdletBinding()] param( [string[]] $ComputerName = $Env:COMPUTERNAME, [ValidateSet('Application', 'BIOS', 'CPU', 'RAM', 'Disk', 'DiskLogical', 'Network', 'NetworkFirewall', 'OperatingSystem', 'Services', 'System', 'Startup', 'Time', 'WindowsUpdates' )][string[]] $Type, [switch] $AsHashtable ) Begin { } Process { foreach ($Computer in $ComputerName) { $OutputObject = [ordered] @{} if ($Type -contains 'Application' -or $null -eq $Type) { Write-Verbose "Get-Computer - Processing Application for $Computer" $Application = Get-ComputerApplication -ComputerName $Computer $OutputObject['Application'] = $Application } if ($Type -contains 'BIOS' -or $null -eq $Type) { Write-Verbose "Get-Computer - Processing BIOS for $Computer" $BIOS = Get-ComputerBios -ComputerName $Computer $OutputObject['BIOS'] = $BIOS } if ($Type -contains 'CPU' -or $null -eq $Type) { Write-Verbose "Get-Computer - Processing CPU for $Computer" $CPU = Get-ComputerCPU -ComputerName $Computer $OutputObject['CPU'] = $CPU } if ($Type -contains 'RAM' -or $null -eq $Type) { Write-Verbose "Get-Computer - Processing RAM for $Computer" $RAM = Get-ComputerRAM -ComputerName $Computer $OutputObject['RAM'] = $RAM } if ($Type -contains 'Disk' -or $null -eq $Type) { Write-Verbose "Get-Computer - Processing Disk for $Computer" $Disk = Get-ComputerDisk -ComputerName $Computer $OutputObject['Disk'] = $Disk } if ($Type -contains 'DiskLogical' -or $null -eq $Type) { Write-Verbose "Get-Computer - Processing DiskLogical for $Computer" $DiskLogical = Get-ComputerDiskLogical -ComputerName $Computer $OutputObject['DiskLogical'] = $DiskLogical } if ($Type -contains 'OperatingSystem' -or $null -eq $Type) { Write-Verbose "Get-Computer - Processing OperatingSystem for $Computer" $OperatingSystem = Get-ComputerOperatingSystem -ComputerName $Computer $OutputObject['OperatingSystem'] = $OperatingSystem } if ($Type -contains 'Network' -or $null -eq $Type) { Write-Verbose "Get-Computer - Processing Network for $Computer" $Network = Get-ComputerNetwork -ComputerName $Computer $OutputObject['Network'] = $Network } if ($Type -contains 'NetworkFirewall' -or $null -eq $Type) { Write-Verbose "Get-Computer - Processing NetworkFirewall for $Computer" $NetworkFirewall = Get-ComputerNetwork -ComputerName $Computer -NetworkFirewallOnly $OutputObject['NetworkFirewall'] = $NetworkFirewall } if ($Type -contains 'RDP' -or $null -eq $Type) { Write-Verbose "Get-Computer - Processing RDP for $Computer" $RDP = Get-ComputerRDP -ComputerName $Computer $OutputObject['RDP'] = $RDP } if ($Type -contains 'Services' -or $null -eq $Type) { Write-Verbose "Get-Computer - Processing Services for $Computer" $Services = Get-ComputerService -ComputerName $Computer $OutputObject['Services'] = $Services } if ($Type -contains 'System' -or $null -eq $Type) { Write-Verbose "Get-Computer - Processing System for $Computer" $System = Get-ComputerSystem -ComputerName $Computer $OutputObject['System'] = $System } if ($Type -contains 'Startup' -or $null -eq $Type) { Write-Verbose "Get-Computer - Processing Startup for $Computer" $Startup = Get-ComputerStartup -ComputerName $Computer $OutputObject['Startup'] = $Startup } if ($Type -contains 'Time' -or $null -eq $Type) { Write-Verbose "Get-Computer - Processing Time for $Computer" $Time = Get-ComputerTime -TimeTarget $Computer $OutputObject['Time'] = $Time } if ($Type -contains 'Tasks' -or $null -eq $Type) { Write-Verbose "Get-Computer - Processing Tasks for $Computer" $Tasks = Get-ComputerTask -ComputerName $Computer $OutputObject['Tasks'] = $Tasks } if ($Type -contains 'WindowsUpdates' -or $null -eq $Type) { Write-Verbose "Get-Computer - Processing WindowsUpdates for $Computer" $WindowsUpdates = Get-ComputerWindowsUpdates -ComputerName $Computer $OutputObject['WindowsUpdates'] = $WindowsUpdates } if ($AsHashtable) { $OutputObject } else { [PSCustomObject] $OutputObject } } } } function Get-ComputerApplication { <# .SYNOPSIS Get software installed on computer or server .DESCRIPTION Get software installed on computer or server .PARAMETER ComputerName Specifies computer on which you want to run the operation. .EXAMPLE Get-ComputerApplications -Verbose | Format-Table .EXAMPLE Get-ComputerApplications -Verbose -ComputerName AD1, AD2 | Format-Table .NOTES General notes #> [alias('Get-ComputerApplications')] [CmdletBinding()] param( [string[]] $ComputerName = $Env:COMPUTERNAME ) $ScriptBlock = { $objapp1 = Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* $objapp2 = Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* $app1 = $objapp1 | Select-Object Displayname, Displayversion , Publisher, Installdate, @{Expression = { 'x64' }; Label = 'WindowsType' } $app2 = $objapp2 | Select-Object Displayname, Displayversion , Publisher, Installdate, @{Expression = { 'x86' }; Label = 'WindowsType' } | Where-Object { -NOT (([string]$_.displayname).contains('Security Update for Microsoft') -or ([string]$_.displayname).contains('Update for Microsoft')) } $app = $app1 + $app2 #| Sort-Object -Unique $app | Where-Object { $null -ne $_.Displayname } #| Sort-Object DisplayName } foreach ($Computer in $ComputerName) { try { $LocalComputerDNSName = [System.Net.Dns]::GetHostByName($Env:COMPUTERNAME).HostName } catch { $LocalComputerDNSName = $Computer } if ($Computer -eq $Env:COMPUTERNAME -or $Computer -eq $LocalComputerDNSName) { $Parameters = @{ ScriptBlock = $ScriptBlock } } else { $Parameters = @{ ComputerName = $Computer ScriptBlock = $ScriptBlock } } try { $Data = Invoke-Command @Parameters } catch { Write-Warning "Get-ComputerApplication - No data for computer $Computer" continue } foreach ($Information in $Data) { if ($Information.Installdate) { try { $InstallDate = [datetime]::ParseExact($Information.Installdate, 'yyyyMMdd', $null) } catch { Write-Verbose "Get-ComputerApplication - InstallDate $($Information.Installdate) couldn't be converted." $InstallDate = $null } } else { $InstallDate = $null } [PSCustomObject] @{ DisplayName = $Information.DisplayName Version = $Information.DisplayVersion Publisher = $Information.Publisher Installdate = $InstallDate Type = $Information.WindowsType ComputerName = $Computer } } } } function Get-ComputerBios { [CmdletBinding()] param( [string] $ComputerName = $Env:COMPUTERNAME, [ValidateSet('Default', 'Dcom', 'Wsman')][string] $Protocol = 'Default', [switch] $All ) [string] $Class = 'win32_bios' if ($All) { [string] $Properties = '*' } else { [string[]] $Properties = 'PSComputerName', 'Status', 'Version', 'PrimaryBIOS', 'Manufacturer', 'ReleaseDate', 'SerialNumber', 'SMBIOSBIOSVersion', 'SMBIOSMajorVersion', 'SMBIOSMinorVersion', 'SystemBiosMajorVersion', 'SystemBiosMinorVersion' } $Information = Get-CimData -ComputerName $ComputerName -Protocol $Protocol -Class $Class -Properties $Properties if ($All) { $Information } else { foreach ($Info in $Information) { foreach ($Data in $Info) { # # Remember to expand if changing properties above [PSCustomObject] @{ ComputerName = if ($Data.PSComputerName) { $Data.PSComputerName } else { $Env:COMPUTERNAME } Status = $Data.Status Version = $Data.Version VersionBIOS = -join ($Data.SMBIOSMajorVersion, ".", $Data.SMBIOSMinorVersion, ".", $Data.SystemBiosMajorVersion, ".", $Data.SystemBiosMinorVersion) PrimaryBIOS = $Data.PrimaryBIOS Manufacturer = $Data.Manufacturer ReleaseDate = $Data.ReleaseDate } } } } } function Get-ComputerCPU { [CmdletBinding()] param( [string[]] $ComputerName = $Env:COMPUTERNAME, [ValidateSet('Default', 'Dcom', 'Wsman')][string] $Protocol = 'Default', [switch] $All ) [string] $Class = 'win32_processor' if ($All) { [string] $Properties = '*' } else { [string[]] $Properties = 'PSComputerName', 'Name', 'DeviceID', 'Caption', 'SystemName', 'CurrentClockSpeed', 'MaxClockSpeed', 'ProcessorID', 'ThreadCount', 'Architecture', 'Status', 'LoadPercentage', 'L3CacheSize', 'Manufacturer', 'VirtualizationFirmwareEnabled', 'NumberOfCores', 'NumberOfEnabledCore', 'NumberOfLogicalProcessors' } $Information = Get-CimData -ComputerName $ComputerName -Protocol $Protocol -Class $Class -Properties $Properties if ($All) { $Information } else { foreach ($Info in $Information) { foreach ($Data in $Info) { # # Remember to expand if changing properties above [PSCustomObject] @{ ComputerName = if ($Data.PSComputerName) { $Data.PSComputerName } else { $Env:COMPUTERNAME } Name = $Data.Name DeviceID = $Data.DeviceID Caption = $Data.Caption CurrentClockSpeed = $Data.CurrentClockSpeed MaxClockSpeed = $Data.MaxClockSpeed ProcessorID = $Data.ProcessorID ThreadCount = $Data.ThreadCount Architecture = $Data.Architecture Status = $Data.Status LoadPercentage = $Data.LoadPercentage Manufacturer = $Data.Manufacturer #VirtualizationFirmwareEnabled = $Data.VirtualizationFirmwareEnabled NumberOfCores = $Data.NumberOfCores NumberOfEnabledCore = $Data.NumberOfEnabledCore NumberOfLogicalProcessors = $Data.NumberOfLogicalProcessors } } } } } #get-counter -Counter '\Processor(*)\% Processor Time' -SampleInterval 1 -MaxSamples 3 | select -ExpandProperty countersamples | select -ExpandProperty cookedvalue | Measure-Object -Average | select -ExpandProperty Average function Get-ComputerCulture { [CmdletBinding()] param( [string] $ComputerName = $Env:COMPUTERNAME ) $ScriptBlock = { Get-Culture | Select-Object KeyboardLayoutId, DisplayName, @{Expression = { $_.ThreeLetterWindowsLanguageName }; Label = "Windows Language" } } if ($ComputerName -eq $Env:COMPUTERNAME) { $Data8 = Invoke-Command -ScriptBlock $ScriptBlock } else { $Data8 = Invoke-Command -ComputerName $ComputerName -ScriptBlock $ScriptBlock } return $Data8 } function Get-ComputerDevice { [CmdletBinding()] param( [string] $ComputerName = $Env:COMPUTERNAME, [ValidateSet('Default', 'Dcom', 'Wsman')][string] $Protocol = 'Default', [switch] $All, [switch] $Extended ) [string] $Class = 'win32_pnpentity' if ($All) { [string] $Properties = '*' } else { [string[]] $Properties = @( #'Caption' #'Description' #'InstallDate' 'PNPClass' 'Name' 'Status' #'Availability' 'ConfigManagerErrorCode' #'ConfigManagerUserConfig' 'DeviceID' 'ErrorCleared' 'ErrorDescription' 'LastErrorCode' #'PNPDeviceID' #'PowerManagementCapabilities' #'PowerManagementSupported' 'StatusInfo' #'SystemName' 'ClassGuid' 'CompatibleID' 'HardwareID' 'Manufacturer' #'Present' #'Service' 'PSComputerName' ) } $ConfigManagerErrorCode = @{ '0' = "This device is working properly." '1' = 'This device is not configured correctly.' '2' = 'Windows cannot load the driver for this device.' '3' = "The driver for this device might be corrupted, or your system may be running low on memory or other resources." '4' = "This device is not working properly. One of its drivers or your registry might be corrupted." '5' = "The driver for this device needs a resource that Windows cannot manage." '6' = "The boot configuration for this device conflicts with other devices." '7' = "Cannot filter." '8' = "The driver loader for the device is missing." '9' = "This device is not working properly because the controlling firmware is reporting the resources for the device incorrectly." '10' = "This device cannot start." '11' = "This device failed." '12' = "This device cannot find enough free resources that it can use." '13' = "Windows cannot verify this device's resources." '14' = "This device cannot work properly until you restart your computer." '15' = "This device is not working properly because there is probably a re-enumeration problem." '16' = "Windows cannot identify all the resources this device uses." '17' = "This device is asking for an unknown resource type." '18' = "Reinstall the drivers for this device." '19' = "Failure using the VxD loader." '20' = "Your registry might be corrupted." '21' = "System failure: Try changing the driver for this device. If that does not work, see your hardware documentation. Windows is removing this device." '22' = "This device is disabled." '23' = "System failure: Try changing the driver for this device. If that doesn't work, see your hardware documentation." '24' = "This device is not present, is not working properly, or does not have all its drivers installed." '25' = "Windows is still setting up this device." '26' = "Windows is still setting up this device." '27' = "This device does not have valid log configuration." '28' = "The drivers for this device are not installed." '29' = "This device is disabled because the firmware of the device did not give it the required resources." '30' = "This device is using an Interrupt Request (IRQ) resource that another device is using." '31' = "This device is not working properly because Windows cannot load the drivers required for this device." } $Information = Get-CimData -ComputerName $ComputerName -Protocol $Protocol -Class $Class -Properties $Properties if ($All) { $Information } else { foreach ($Info in $Information) { foreach ($Data in $Info) { # # Remember to expand if changing properties above $Device = [ordered]@{ ComputerName = if ($Data.PSComputerName) { $Data.PSComputerName } else { $Env:COMPUTERNAME } #'Caption' = $Data.Caption #'Description' = $Data.Description #'InstallDate' = $Data.InstallDate 'DeviceClass' = $Data.PNPClass 'Name' = $Data.Name 'Status' = $Data.Status #'Availability' = $Data.Availability 'ErrorCode' = $ConfigManagerErrorCode["$($Data.ConfigManagerErrorCode)"] 'DeviceID' = $Data.DeviceID } if ($Extended) { $DeviceUpgrade = [ordered]@{ #'ConfigManagerUserConfig' = $Data.ConfigManagerUserConfig 'ErrorCleared' = $Data.ErrorCleared 'ErrorDescription' = $Data.ErrorDescription 'LastErrorCode' = $Data.LastErrorCode #'PNPDeviceID' = $Data.PNPDeviceID #'PowerManagementCapabilities' = $Data.PowerManagementCapabilities #'PowerManagementSupported' = $Data.PowerManagementSupported 'StatusInfo' = $Data.StatusInfo #'SystemName' = $Data.SystemName 'ClassGuid' = $Data.ClassGuid 'CompatibleID' = $Data.CompatibleID 'HardwareID' = $Data.HardwareID 'Manufacturer' = if ($Data.Manufacturer) { $Data.Manufacturer.Replace('(', '').Replace(')', '') } else { } #'Present' = $Data.Present #'Service' = $Data.Service } [PSCustomObject] ($Device + $DeviceUpgrade) } else { [PSCustomObject] $Device } } } } } #Get-ComputerDevice | Format-Table * #Get-ComputerDevice -ComputerName AD1 -Extended | Format-Table * function Get-ComputerDisk { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER ComputerName Parameter description .PARAMETER Protocol Parameter description .PARAMETER All Parameter description .EXAMPLE Get-ComputerDisk -ComputerName AD1, AD2, EVO1, AD2019 | Format-Table -AutoSize * Output: WARNING: Get-ComputerSystem - No data for computer AD2019. Most likely an error on receiving side. ComputerName Index Model Caption SerialNumber Description MediaType FirmwareRevision Partitions SizeGB PNPDeviceID ------------ ----- ----- ------- ------------ ----------- --------- ---------------- ---------- ------ ----------- AD1 0 Microsoft Virtual Disk Microsoft Virtual Disk Disk drive Fixed hard disk media 1.0 3 127 SCSI\DISK&VEN_MSFT&PROD_VIRTUAL_DISK\000000 AD2 0 Microsoft Virtual Disk Microsoft Virtual Disk Disk drive Fixed hard disk media 1.0 3 127 SCSI\DISK&VEN_MSFT&PROD_VIRTUAL_DISK\000000 EVO1 0 WDC WD30EFRX-68AX9N0 WDC WD30EFRX-68AX9N0 WD-WMC1T2351095 Disk drive Fixed hard disk media 80.00A80 1 2795 SCSI\DISK&VEN_WDC&PROD_WD30EFRX-68AX9N0\4&191557A4&0&000000 EVO1 2 Samsung SSD 950 PRO 512GB Samsung SSD 950 PRO 512GB 0025_3857_61B0_0EF2. Disk drive Fixed hard disk media 2B0Q 3 477 SCSI\DISK&VEN_NVME&PROD_SAMSUNG_SSD_950\5&35365596&0&000000 EVO1 1 Samsung SSD 860 EVO 500GB Samsung SSD 860 EVO 500GB S3Z2NB0K176976A Disk drive Fixed hard disk media RVT01B6Q 1 466 SCSI\DISK&VEN_SAMSUNG&PROD_SSD\4&191557A4&0&000100 .NOTES General notes #> [CmdletBinding()] param( [string[]] $ComputerName = $Env:COMPUTERNAME, [ValidateSet('Default', 'Dcom', 'Wsman')][string] $Protocol = 'Default', [switch] $All ) [string] $Class = 'win32_diskdrive' if ($All) { [string] $Properties = '*' } else { [string[]] $Properties = 'Index', 'Model', 'Caption', 'SerialNumber', 'Description', 'MediaType', 'FirmwareRevision', 'Partitions', 'Size', 'PNPDeviceID', 'PSComputerName' } $Information = Get-CimData -ComputerName $ComputerName -Protocol $Protocol -Class $Class -Properties $Properties if ($All) { $Information } else { foreach ($Info in $Information) { foreach ($Data in $Info) { # # Remember to expand if changing properties above [PSCustomObject] @{ ComputerName = if ($Data.PSComputerName) { $Data.PSComputerName } else { $Env:COMPUTERNAME } Index = $Data.Index Model = $Data.Model Caption = $Data.Caption SerialNumber = if ($Data.SerialNumber) { $Data.SerialNumber.Trim() } else { '' } Description = $Data.Description MediaType = $Data.MediaType FirmwareRevision = $Data.FirmwareRevision Partitions = $Data.Partitions SizeGB = $Data.Size / 1Gb -as [int] PNPDeviceID = $Data.PNPDeviceID } } } } } function Get-ComputerDiskLogical { <# .SYNOPSIS Getting drive space .DESCRIPTION Long description .PARAMETER ComputerName Parameter description .PARAMETER Protocol Parameter description .PARAMETER RoundingPlaceRoundingPlace .PARAMETER RoundingPlace .PARAMETER OnlyLocalDisk Parameter description .PARAMETER All Parameter description .EXAMPLE Get-ComputerDiskLogical -ComputerName AD1, AD2, EVOWIN -OnlyLocalDisk | ft -AutoSize Output: ComputerName DeviceID DriveType ProviderName FreeSpace UsedSpace TotalSpace FreePercent UsedPercent VolumeName ------------ -------- --------- ------------ --------- --------- ---------- ----------- ----------- ---------- AD2 C: Local Disk 96,96 29,49 126,45 76,68 23,32 AD1 C: Local Disk 103,17 23,28 126,45 81,59 18,41 EVOWIN C: Local Disk 133,31 343,03 476,34 27,99 72,01 EVOWIN D: Local Disk 2433 361,4 2794,39 87,07 12,93 Media EVOWIN E: Local Disk 66,05 399,7 465,75 14,18 85,82 Testing Environment .NOTES General notes #> [CmdletBinding()] param( [string[]] $ComputerName = $Env:COMPUTERNAME, [ValidateSet('Default', 'Dcom', 'Wsman')][string] $Protocol = 'Default', [string][ValidateSet('GB', 'TB', 'MB')] $Size = 'GB', [int] $RoundingPlace = 2, [int] $RoundingPlacePercent = 2, [switch] $OnlyLocalDisk, [switch] $All ) [string] $Class = 'win32_logicalDisk' if ($All) { [string] $Properties = '*' } else { [string[]] $Properties = 'DeviceID', 'DriveType', 'ProviderName', 'FreeSpace', 'Size', 'VolumeName', 'PSComputerName' } $DriveType = @{ '0' = 'Unknown' '1' = 'No Root Directory' '2' = 'Removable Disk' '3' = 'Local Disk' '4' = 'Network Drive' '5' = 'Compact Disc' '6' = 'RAM Disk' } $Divider = "1$Size" $Information = Get-CimData -ComputerName $ComputerName -Protocol $Protocol -Class $Class -Properties $Properties if ($All) { $Information } else { $Output = foreach ($Info in $Information) { foreach ($Data in $Info) { # # Remember to expand if changing properties above [PSCustomObject] @{ ComputerName = if ($Data.PSComputerName) { $Data.PSComputerName } else { $Env:COMPUTERNAME } DeviceID = $Data.DeviceID DriveType = $DriveType["$($Data.DriveType)"] ProviderName = $Data.ProviderName #SerialNumber = if ($Data.SerialNumber) { $Data.SerialNumber.Trim() } else { '' } FreeSpace = [Math]::Round($Data.FreeSpace / $Divider, $RoundingPlace) UsedSpace = [Math]::Round(($Data.Size - $Data.FreeSpace) / $Divider, $RoundingPlace) TotalSpace = [Math]::Round($Data.Size / $Divider, $RoundingPlace) FreePercent = if ($Data.Size -gt 0 ) { [Math]::round(($Data.FreeSpace / $Data.Size) * 100, $RoundingPlacePercent) } else { '0' } UsedPercent = if ($Data.Size -gt 0 ) { [Math]::round((($Data.Size - $Data.FreeSpace) / $Data.Size) * 100, $RoundingPlacePercent) } else { '0' } VolumeName = $Data.VolumeName # Partitions = $Data.Partitions # SizeGB = $Data.Size / 1Gb -as [int] # PNPDeviceID = $Data.PNPDeviceID # UsedSpace(in GB) FreeSpace(in GB) TotalSpace(in GB) } } } if ($OnlyLocalDisk) { $Output | Where-Object { $_.DriveType -eq 'Local Disk' } } else { $Output } } } function Get-ComputerMissingDrivers { [CmdletBinding()] param( [string] $ComputerName = $Env:COMPUTERNAME ) $Data = Get-WmiObject Win32_PNPEntity -ComputerName $ComputerName | Where-Object { $_.Configmanagererrorcode -ne 0 } | Select-Object Caption, ConfigmanagererrorCode, Description, DeviceId, HardwareId, PNPDeviceID return $Data } function Get-ComputerNetwork { [alias('Get-ComputerNetworkCard')] <# .SYNOPSIS .DESCRIPTION Long description .PARAMETER ComputerName Parameter description .PARAMETER NetworkFirewallOnly Parameter description .PARAMETER NetworkFirewallSummaryOnly Parameter description .EXAMPLE Get-ComputerNetworkCard -ComputerName AD1, AD2, AD3 Output Name NetworkCardName NetworkCardIndex FirewallProfile FirewallStatus IPv4Connectivity IPv6Connectivity Caption Description ElementName DefaultInboundAction DefaultOutboundAction AllowInboundRules AllowLocalFirewallRules AllowLocalIPsecRules AllowUserApps AllowUserPorts AllowUnicastResponseToMulticast NotifyOnListen EnableStealthModeForIPsec LogFileName LogMaxSizeKilobytes LogAllowed LogBlo cked ---- --------------- ---------------- --------------- -------------- ---------------- ---------------- ------- ----------- ----------- -------------------- --------------------- ----------------- ----------------------- -------------------- ------------- -------------- ------------------------------- -------------- ------------------------- ----------- ------------------- ---------- ------ ad.evotec.xyz vEthernet (External Switch) 13 DomainAuthenticated True Internet NoTraffic NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured True NotConfigured %systemroot%\system32\LogFiles\Firewall\pfirewall.log 4096 False False Network 2 Ethernet 2 2 Private True Internet NoTraffic Block Allow NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured False NotConfigured %systemroot%\system32\LogFiles\Firewall\pfirewall.log 4096 False False Network Ethernet 2 Private True LocalNetwork NoTraffic NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured False NotConfigured %systemroot%\system32\LogFiles\Firewall\pfirewall.log 4096 False False ad.evotec.xyz Ethernet 5 3 DomainAuthenticated False Internet NoTraffic NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured False NotConfigured %systemroot%\system32\LogFiles\Firewall\pfirewall.log 4096 False False Network 2 Ethernet 4 12 Private False LocalNetwork NoTraffic NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured False NotConfigured %systemroot%\system32\LogFiles\Firewall\pfirewall.log 4096 False False .EXAMPLE Get-ComputerNetworkCard -ComputerName EVOWIN -NetworkFirewallOnly PSComputerName Profile Enabled DefaultInboundAction DefaultOutboundAction AllowInboundRules AllowLocalFirewallRules AllowLocalIPsecRules AllowUserApps AllowUserPorts AllowUnicastResponseToMulticast NotifyOnListen EnableStealthModeForIPsec LogMaxSizeKilobytes LogAllowed LogBlocked LogIgnored Caption Description ElementName InstanceID DisabledInterfaceAliases LogFileName Name CimClass -------------- ------- ------- -------------------- --------------------- ----------------- ----------------------- -------------------- ------------- -------------- ------------------------------- -------------- ------------------------- ------------------- ---------- ---------- ---------- ------- ----------- ----------- ---------- ------------------------ ----------- ---- -------- EVOWIN Domain True NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured True NotConfigured 4096 False False NotConfigured MSFT|FW|FirewallProfile|Domain {NotConfigured} %systemroot%\system32\LogFiles\Firewall\pfirewall.log Domain root/stand... EVOWIN Private True NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured True NotConfigured 4096 False False NotConfigured MSFT|FW|FirewallProfile|Private {NotConfigured} %systemroot%\system32\LogFiles\Firewall\pfirewall.log Private root/stand... EVOWIN Public True NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured NotConfigured True NotConfigured 4096 False False NotConfigured MSFT|FW|FirewallProfile|Public {NotConfigured} %systemroot%\system32\LogFiles\Firewall\pfirewall.log Public root/stand... .NOTES General notes #> [CmdletBinding()] param( [string[]] $ComputerName = $Env:COMPUTERNAME, [switch] $NetworkFirewallOnly, [switch] $NetworkFirewallSummaryOnly, [alias('Joiner')][string] $Splitter ) [Array] $CollectionComputers = $ComputerName.Where( { $_ -eq $Env:COMPUTERNAME }, 'Split') $Firewall = @{ } $NetworkFirewall = @( if ($CollectionComputers[0].Count -gt 0) { $Firewall[$Env:COMPUTERNAME] = @{ } $Output = Get-NetFirewallProfile foreach ($_ in $Output) { Add-Member -InputObject $_ -Name 'PSComputerName' -Value $Env:COMPUTERNAME -Type NoteProperty -Force $_ if ($_.Name -eq 'Domain') { $Firewall[$Env:COMPUTERNAME]['DomainAuthenticated'] = $_ } else { $Firewall[$Env:COMPUTERNAME][$($_.Name)] = $_ } } } if ($CollectionComputers[1].Count -gt 0) { foreach ($_ in $CollectionComputers[1]) { $Firewall[$_] = @{ } } $Output = Get-NetFirewallProfile -CimSession $CollectionComputers[1] foreach ($_ in $Output) { if ($_.Name -eq 'Domain') { $Firewall[$_.PSComputerName]['DomainAuthenticated'] = $_ # $Firewall[$_.PSComputerName]["DomainAuthenticatedEverything"] = $_ } else { $Firewall[$_.PSComputerName][$($_.Name)] = $_ #$Firewall[$_.PSComputerName]["$($_.Name)Everything"] = $_ } } } ) if ($NetworkFirewallOnly) { return $NetworkFirewall } if ($NetworkFirewallSummaryOnly) { <# Name Value ---- ----- AD1 {Private, DomainAuthenticated, Public} DC1 {Private, DomainAuthenticated, Public} AD2 {Private, DomainAuthenticated, Public} EVOWIN {Private, DomainAuthenticated, Public} AD3 {Private, DomainAuthenticated, Public} Where $T['EvoWin']['Private'] Name : Private Enabled : True DefaultInboundAction : NotConfigured DefaultOutboundAction : NotConfigured AllowInboundRules : NotConfigured AllowLocalFirewallRules : NotConfigured AllowLocalIPsecRules : NotConfigured AllowUserApps : NotConfigured AllowUserPorts : NotConfigured AllowUnicastResponseToMulticast : NotConfigured NotifyOnListen : True EnableStealthModeForIPsec : NotConfigured LogFileName : %systemroot%\system32\LogFiles\Firewall\pfirewall.log LogMaxSizeKilobytes : 4096 LogAllowed : False LogBlocked : False LogIgnored : NotConfigured DisabledInterfaceAliases : {NotConfigured} #> return $Firewall } $NetworkCards = @( if ($CollectionComputers[0].Count -gt 0) { $Output = Get-NetConnectionProfile foreach ($_ in $Output) { Add-Member -InputObject $_ -Name 'PSComputerName' -Value $Env:COMPUTERNAME -Type NoteProperty -Force $_ } } if ($CollectionComputers[1].Count -gt 0) { Get-NetConnectionProfile -CimSession $CollectionComputers[1] } ) foreach ($_ in $NetworkCards) { $NetworkCardsConfiguration = Get-CimData -ComputerName $ComputerName -Class 'Win32_NetworkAdapterConfiguration' $CurrentCard = foreach ($Configuration in $NetworkCardsConfiguration) { if ($_.PSComputerName -eq $Configuration.PSComputerName) { if ($Configuration.InterfaceIndex -eq $_.InterfaceIndex) { $Configuration } } } $NetbiosTCPIP = @{ '0' = 'Default' '1' = 'Enabled' '2' = 'Disabled' } [PSCustomObject] @{ Name = $_.Name NetworkCardName = $_.InterfaceAlias NetworkCardIndex = $_.InterfaceIndex FirewallProfile = $_.NetworkCategory FirewallStatus = $Firewall[$_.PSComputerName]["$($_.NetworkCategory)"].'Enabled' IPAddress = $CurrentCard.IPAddress IPGateway = $CurrentCard.DefaultIPGateway IPSubnet = $CurrentCard.IPSubnet IPv4Connectivity = $_.IPv4Connectivity IPv6Connectivity = $_.IPv6Connectivity DNSServerSearchOrder = $CurrentCard.DNSServerSearchOrder DNSDomainSuffixSearchOrder = $CurrentCard.DNSDomainSuffixSearchOrder FullDNSRegistrationEnabled = $CurrentCard.FullDNSRegistrationEnabled DHCPEnabled = $CurrentCard.DHCPEnabled DHCPServer = $CurrentCard.DHCPServer DHCPLeaseObtained = $CurrentCard.DHCPLeaseObtained NetBIOSOverTCPIP = $NetBiosTCPIP["$($CurrentCard.TcpipNetbiosOptions)"] Caption = $_.Caption Description = $_.Description ElementName = $_.ElementName # Firewall based fields DefaultInboundAction = $Firewall[$_.PSComputerName]["$($_.NetworkCategory)"].DefaultInboundAction DefaultOutboundAction = $Firewall[$_.PSComputerName]["$($_.NetworkCategory)"].DefaultOutboundAction AllowInboundRules = $Firewall[$_.PSComputerName]["$($_.NetworkCategory)"].AllowInboundRules AllowLocalFirewallRules = $Firewall[$_.PSComputerName]["$($_.NetworkCategory)"].AllowLocalFirewallRules AllowLocalIPsecRules = $Firewall[$_.PSComputerName]["$($_.NetworkCategory)"].AllowLocalIPsecRules AllowUserApps = $Firewall[$_.PSComputerName]["$($_.NetworkCategory)"].AllowUserApps AllowUserPorts = $Firewall[$_.PSComputerName]["$($_.NetworkCategory)"].AllowUserPorts AllowUnicastResponseToMulticast = $Firewall[$_.PSComputerName]["$($_.NetworkCategory)"].AllowUnicastResponseToMulticast NotifyOnListen = $Firewall[$_.PSComputerName]["$($_.NetworkCategory)"].NotifyOnListen EnableStealthModeForIPsec = $Firewall[$_.PSComputerName]["$($_.NetworkCategory)"].EnableStealthModeForIPsec LogFileName = $Firewall[$_.PSComputerName]["$($_.NetworkCategory)"].LogFileName LogMaxSizeKilobytes = $Firewall[$_.PSComputerName]["$($_.NetworkCategory)"].LogMaxSizeKilobytes LogAllowed = $Firewall[$_.PSComputerName]["$($_.NetworkCategory)"].LogAllowed LogBlocked = $Firewall[$_.PSComputerName]["$($_.NetworkCategory)"].LogBlocked LogIgnored = $Firewall[$_.PSComputerName]["$($_.NetworkCategory)"].LogIgnored ComputerName = $_.PSComputerName } } } #Get-ComputerNetwork -ComputerName AD1, AD2 | Format-Table -a * #Get-CimData -ComputerName AD1 -Class 'Win32_NetworkAdapterConfiguration' function Get-ComputerOemInformation { [CmdletBinding()] param( [string] $ComputerName = $Env:COMPUTERNAME ) $ScriptBlock = { Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\OEMInformation | Select-Object Model, Manufacturer, Logo, SupportPhone, SupportURL, SupportHours } if ($ComputerName -eq $Env:COMPUTERNAME) { $Data = Invoke-Command -ScriptBlock $ScriptBlock } else { $Data = Invoke-Command -ComputerName $ComputerName -ScriptBlock $ScriptBlock } return $Data } function Get-ComputerOperatingSystem { [CmdletBinding()] param( [string[]] $ComputerName = $Env:COMPUTERNAME, [ValidateSet('Default', 'Dcom', 'Wsman')][string] $Protocol = 'Default', [switch] $All ) [string] $Class = 'win32_operatingsystem' if ($All) { [string] $Properties = '*' } else { [string[]] $Properties = 'Caption', 'Manufacturer', 'InstallDate', 'OSArchitecture', 'Version', 'SerialNumber', 'BootDevice', 'WindowsDirectory', 'CountryCode', 'OSLanguage', 'OSProductSuite', 'PSComputerName', 'LastBootUpTime', 'LocalDateTime' } $Information = Get-CimData -ComputerName $ComputerName -Protocol $Protocol -Class $Class -Properties $Properties if ($All) { $Information } else { foreach ($Info in $Information) { foreach ($Data in $Info) { # # Remember to expand if changing properties above [PSCustomObject] @{ ComputerName = if ($Data.PSComputerName) { $Data.PSComputerName } else { $Env:COMPUTERNAME } OperatingSystem = $Data.Caption OperatingSystemVersion = ConvertTo-OperatingSystem -OperatingSystem $Data.Caption -OperatingSystemVersion $Data.Version OperatingSystemBuild = $Data.Version Manufacturer = $Data.Manufacturer OSArchitecture = $Data.OSArchitecture OSLanguage = ConvertFrom-LanguageCode -LanguageCode $Data.OSLanguage OSProductSuite = [Microsoft.PowerShell.Commands.OSProductSuite] $($Data.OSProductSuite) InstallDate = $Data.InstallDate LastBootUpTime = $Data.LastBootUpTime LocalDateTime = $Data.LocalDateTime SerialNumber = $Data.SerialNumber BootDevice = $Data.BootDevice WindowsDirectory = $Data.WindowsDirectory CountryCode = $Data.CountryCode } } } } } #Get-ComputerOperatingSystem -ComputerName AD1, AD2, AD3, DC1 -All #| ft -a * #Get-CimInstance SoftwareLicensingProduct -ComputerName AD1 -Filter "ApplicationID = '55c92734-d682-4d71-983e-d6ec3f16059f'" | where licensestatus -eq 1 | ` # select name, description, @{Label = 'computer'; Expression = { $_.PscomputerName } } | Format-List name, description, computer #Get-CimInstance -class SoftwareLicensingProduct | where { $_.name -match 'office' -AND $_.licensefamily } | ft -a * #$L = Get-CimData -Class 'SoftwareLicensingProduct' -ComputerName AD1,AD2,EVOWIN #$L | Where-Object { $_.Name -match 'windows' } | ft -a Name, ApplicationID,Description,ExtendedGrace,graceperiodremaining, GenuineStatus,LicenseFamily,LicenseStatus,LicenseStatusReason,PSComputerName <# $T = foreach ($_ in $L) { $LicensStatus = switch ($_.LicenseStatus) { 0 { "Unlicensed" } 1 { "Licensed" } 2 { "Out-Of-Box Grace Period" } 3 { "Out-Of-Tolerance Grace Period" } 4 { "Non-Genuine Grace Period" } 5 { 'Evaluation' } } [PSCustomObject] @{ Name = $_.Name Description = $_.Description LicenseFamily = $_.LicenseFamily LicenseStatus = $LicensStatus LicenseStatusReason = $_.LicenseStatusReason GracePeriodRemainingDays = $_.graceperiodremaining / 1440 ComputerName = $_.PSComputerName } } $T | ft -a #> function Get-ComputerRAM { [CmdletBinding()] param( [string] $ComputerName = $Env:COMPUTERNAME, [ValidateSet('Default', 'Dcom', 'Wsman')][string] $Protocol = 'Default', [switch] $All, [switch] $Extended ) [string] $Class = 'Win32_physicalmemory ' if ($All) { [string] $Properties = '*' } else { [string[]] $Properties = @( 'InstallDate' #'Name' # 'Status' 'Manufacturer' 'Model' 'OtherIdentifyingInfo' 'PartNumber' 'PoweredOn' 'SerialNumber' 'SKU' 'Tag' 'Version' 'HotSwappable' 'Removable' 'Replaceable' 'FormFactor' 'BankLabel' 'Capacity' #'DataWidth' 'InterleavePosition' 'MemoryType' #'PositionInRow' 'Speed' #'TotalWidth' #'Attributes' 'ConfiguredClockSpeed' 'ConfiguredVoltage' 'DeviceLocator' #'InterleaveDataDepth' 'MaxVoltage' 'MinVoltage' 'SMBIOSMemoryType' 'TypeDetail' 'PSComputerName' ) } $FormFactor = @{ '0' = 'Unknown' '1' = 'Other' '2' = 'SIP' '3' = 'DIP' '4' = 'ZIP' '5' = 'SOJ' '6' = 'Proprietary' '7' = 'SIMM' '8' = 'DIMM' '9' = 'TSOP' '10' = 'PGA' '11' = 'RIMM' '12' = 'SODIMM' '13' = 'SRIMM' '14' = 'SMD' '15' = 'SSMP' '16' = 'QFP' '17' = 'TQFP' '18' = 'SOIC' '19' = 'LCC' '20' = 'PLCC' '21' = 'BGA' '22' = 'FPBGA' '23' = 'LGA' } $TypeDetails = @{ '1' = 'Reserved' '2' = 'Other' '4' = 'Unknown' '8' = 'Fast-paged' '16' = 'Static column' '32' = 'Pseudo-static' '64' = 'RAMBUS' '128' = 'Synchronous' '256' = 'CMOS' '512' = 'EDO' '1024' = 'Window DRAM' '2048' = 'Cache DRAM' '4096' = 'Non-volatile' } $InterleavePosition = @{ '0' = "Non-Interleaved" '1' = "First Position" '2' = "Second Position" } $MemoryType = @{ '0' = "Unknown" '1' = "Other" '2' = "DRAM" '3' = "Synchronous DRAM" '4' = "Cache DRAM" '5' = "EDO" '6' = "EDRAM" '7' = "VRAM" '8' = "SRAM" '9' = "ROM" '10' = "ROM" '11' = "FLASH" '12' = "EEPROM" '13' = "FEPROM" '14' = "EPROM" '15' = "CDRAM" '16' = "3DRAM" '17' = "SDRAM" '18' = "SGRAM" '19' = "RDRAM" '20' = "DDR" } $MemoryTypeSMBIOS = @{ '0' = 'Unknown' '1' = 'Other' '2' = 'DRAM' '3' = 'Synchronous DRAM' '4' = 'Cache DRAM' '5' = 'EDO' '6' = 'EDRAM' '7' = 'VRAM' #(7) '8' = 'SRAM' #(8) '9' = 'RAM' #(9) '10' = 'ROM' #(10) '11' = 'Flash' #(11) '12' = 'EEPROM' #(12) '13' = 'FEPROM' #(13) '14' = 'EPROM' #(14) '15' = 'CDRAM' #(15) '16' = '3DRAM' #(16) '17' = 'SDRAM' #(17) '18' = 'SGRAM' #(18) '19' = 'RDRAM' #(19) '20' = 'DDR' #(20) '21' = 'DDR2' #(21) # DDR2—May not be available. '22' = 'DDR2 FB-DIMM' #(22) # DDR2—FB-DIMM, May not be available. '24' = 'DDR3' #—May not be available. '25' = 'FBD2' '26' = 'DDR4' } $Information = Get-CimData -ComputerName $ComputerName -Protocol $Protocol -Class $Class -Properties $Properties if ($All) { $Information } else { foreach ($Info in $Information) { foreach ($Data in $Info) { # # Remember to expand if changing properties above $Ram = [ordered] @{ ComputerName = if ($Data.PSComputerName) { $Data.PSComputerName } else { $Env:COMPUTERNAME } Manufacturer = $Data.Manufacturer #: 04CD FormFactor = $FormFactor["$($Data.FormFactor)"] #: 8 SMBIOSMemoryType = $MemoryTypeSMBIOS["$($Data.SMBIOSMemoryType)"] #: 26 Size = [math]::round($Data.Capacity / 1GB, 2) #: 17179869184 Speed = $Data.Speed #: 3200 InterleavePosition = $InterleavePosition["$($Data.InterleavePosition)"] #: 2 MemoryType = $MemoryType["$($Data.MemoryType)"] #: 0 TypeDetail = $TypeDetails["$($Data.TypeDetail)"] #: 128 PartNumber = $Data.PartNumber #: F4 - 3200C16-16GVK DeviceLocator = $Data.DeviceLocator #: ChannelB-DIMM0 } if ($Extended) { $RamExtended = [ordered] @{ InstallDate = $Data.InstallDate #Name = $Data.Name #: Physical Memory #Status = $Data.Status #: Model = $Data.Model #: OtherIdentifyingInfo = $Data.OtherIdentifyingInfo #: PoweredOn = $Data.PoweredOn #: SerialNumber = $Data.SerialNumber #: 00000000 SKU = $Data.SKU #: Tag = $Data.Tag #: Physical Memory 1 Version = $Data.Version #: HotSwappable = $Data.HotSwappable #: Removable = $Data.Removable #: Replaceable = $Data.Replaceable #: BankLabel = $Data.BankLabel #: BANK 2 #DataWidth = $Data.DataWidth #: 64 #PositionInRow = $Data.PositionInRow #: 1 #TotalWidth = $Data.TotalWidth #: 64 #Attributes = $Data.Attributes #: 2 ConfiguredClockSpeed = $Data.ConfiguredClockSpeed #: 3200 ConfiguredVoltage = $Data.ConfiguredVoltage #: 1200 #InterleaveDataDepth = $Data.InterleaveDataDepth #: 1 MaxVoltage = $Data.MaxVoltage #: 1200 MinVoltage = $Data.MinVoltage #: 1200 } [PSCustomObject] ($Ram + $RamExtended) } else { [PSCustomObject] $Ram } } } } } #Get-ComputerRAM | Format-Table * #Get-ComputerRAM -ComputerName AD1 | Format-Table * function Get-ComputerRDP { [alias('Get-RDPSecurity')] [cmdletbinding()] param( [string[]] $ComputerName ) <# Caption : Description : InstallDate : Name : Status : TerminalName : RDP-Tcp CertificateName : Certificates : {0, 0, 0, 0...} Comment : MinEncryptionLevel : 2 PolicySourceMinEncryptionLevel : 0 PolicySourceSecurityLayer : 0 PolicySourceUserAuthenticationRequired : 0 SecurityLayer : 2 SSLCertificateSHA1Hash : 696F767CD537FA3A172D11EF949C44779030464C SSLCertificateSHA1HashType : 1 TerminalProtocol : Microsoft RDP 8.0 Transport : tcp UserAuthenticationRequired : 1 WindowsAuthentication : 0 PSComputerName : AD2 #> # https://docs.microsoft.com/en-us/windows/win32/termserv/win32-tsgeneralsetting $Output = Get-CimData -Class 'Win32_TSGeneralSetting' -NameSpace 'root\cimv2\terminalservices' -ComputerName $ComputerName foreach ($_ in $Output) { #Low level of encryption. Only data sent from the client to the server is encrypted using 56-bit encryption. Be aware that data sent from the server to the client is not encrypted. #Client compatible level of encryption. All data sent from client to server and from server to client is encrypted at the maximum key strength supported by the client. #High level of encryption. All data sent from client to server and from server to client is encrypted using strong 128-bit encryption. Clients that do not support this level of encryption cannot connect. #FIPS compliant encryption. All data sent from client to server and from server to client is encrypted and decrypted with the Federal Information Processing Standard (FIPS) encryption algorithms using the Microsoft cryptographic modules. FIPS is a standard entitled "Security Requirements for Cryptographic Modules". FIPS 140-1 (1994) and FIPS 140-2 (2001) describe government requirements for hardware and software cryptographic modules used within the U.S. government. $EncryptionLevels = @{ '1' = 'Low' '2' = 'Medium / Client Compatible' '3' = 'High' '4' = 'FIPS Compliant' } $PolicyConfiguredBy = @{ '0' = 'Server' '1' = 'Group policy' '2' = 'Default' } #Qualifiers: RDPSecurityLayer ("RDP Security Layer: Communication between the serverand the client will use native RDP encryption."), #Negotiate ("The most secure layer that is supported by the client will be used.If supported, TLS 1.0 will be used."), #SSL ("SSL (TLS 1.0) will be used for server authentication as well as forencrypting all data transferred between the server and the client.This setting requires the server to have an SSL compatible certificate."), #NEWTBD ("A NEW SECURITY LAYER in LONGHORN.") #Specifies the security layer used between the client and server. $SecurityLayers = @{ '1' = 'RDP Security Layer' # Communication between the server and the client uses native RDP encryption. '2' = 'Negotiate' # The most secure layer that is supported by the client is used. If supported, SSL (TLS 1.0) is used. '3' = 'SSL' # SSL (TLS 1.0) is used for server authentication and for encrypting all data transferred between the server and the client. This setting requires the server to have an SSL-compatible certificate. This setting is not compatible with a MinEncryptionLevel value of 1. '4' = 'NEWTBD' # A new security layer. } $HashType = @{ '0' = 'Not valid' '1' = 'Self-signed' '2' = 'Custom' } $Connectivity = Test-ComputerPort -ComputerName $_.PSComputerName -PortTCP 3389 -WarningAction SilentlyContinue [PSCustomObject] @{ ComputerName = $_.PSComputerName Name = $_.TerminalName Connectivity = $Connectivity.Status ConnectivitySummary = $Connectivity.Summary SecurityLayer = $SecurityLayers["$($_.SecurityLayer)"] MinimalEncryptionLevel = $EncryptionLevels["$($_.MinEncryptionLevel)"] MinimalEncryptionLevelValue = $_.MinEncryptionLevel PolicySourceUserAuthenticationRequired = $PolicyConfiguredBy["$($_.PolicySourceUserAuthenticationRequired)"] PolicySourceMinimalEncryptionLevel = $PolicyConfiguredBy["$($_.PolicySourceMinEncryptionLevel)"] PolicySourceSecurityLayer = $PolicyConfiguredBy["$($_.PolicySourceSecurityLayer)"] CertificateName = $_.CertificateName CertificateThumbprint = $_.SSLCertificateSHA1Hash CertificateType = $HashType["$($_.SSLCertificateSHA1HashType)"] Transport = $_.Transport Protocol = $_.TerminalProtocol # Specifies the type of user authentication used for remote connections UserAuthenticationRequired = [bool] $_.UserAuthenticationRequired # Specifies whether the connection defaults to the standard Windows authentication process or to another authentication package that has been installed on the system. WindowsAuthentication = [bool] $_.WindowsAuthentication } } } # #Get-RDPSecurity -ComputerName AD1 #, AD2 function Get-ComputerRoles { <# .SYNOPSIS Get Computer/Server Roles .DESCRIPTION Get Computer/Server Roles .PARAMETER ComputerName Parameter description .PARAMETER FeatureType Display all or limited types. Choices are Role, Role Service and Feature. .PARAMETER EnabledOnly Display only enabled/installed features or roles .EXAMPLE Get-ComputerRoles -ComputerName AD1 -EnabledOnly -FeatureType Role | Format-Table .NOTES General notes #> [alias('Get-ServerRoles')] [CmdletBinding()] param ( [string[]] $ComputerName = $env:COMPUTERNAME, [ValidateSet('Role', 'Role Service', 'Feature')] $FeatureType, [switch] $EnabledOnly ) if ($Global:ProgressPreference -ne 'SilentlyContinue') { $TemporaryProgress = $Global:ProgressPreference $Global:ProgressPreference = 'SilentlyContinue' } foreach ($Computer in $ComputerName) { try { $Output = Get-WindowsFeature -ComputerName $Computer -ErrorAction Stop } #The request could not be processed against a server below Windows Server 2012. Use Invoke-Command and Import-Module catch [System.Exception] { if ($_.FullyQualifiedErrorId -like 'UnSupportedTargetDevice,*') { $output = Invoke-Command -ComputerName $computer { Import-Module ServerManager Get-WindowsFeature } } } #$ | Where-Object { $_.installed -eq $true -and $_.featuretype -eq 'Role' } | Select-Object name, installed -ExcludeProperty subfeatures #$Output | Select-Object Name, Installed , @{name = 'Server Name'; expression = { $Computer } } foreach ($Data in $Output) { if ($EnabledOnly -and $Data.Installed -eq $false) { continue } if ($FeatureType) { if ($Data.FeatureType -notin $FeatureType) { continue } } [PSCustomObject] @{ ComputerName = $Computer Name = $Data.Name DisplayName = $Data.DisplayName FeatureType = $Data.FeatureType Installed = $Data.Installed #MajorVersion = $Data.AdditionalInfo.MajorVersion #MinorVersion = $Data.AdditionalInfo.MinorVersion #InstallName = $Data.AdditionalInfo.InstallName #SubFeatures = $Data.SubFeatures Description = $Data.Description } } } # Bring back setting as per default if ($TemporaryProgress) { $Global:ProgressPreference = $TemporaryProgress } } function Get-ComputerService { [alias('Get-ComputerServices')] [CmdletBinding()] param( [string[]] $ComputerName = $Env:COMPUTERNAME ) Process { foreach ($Computer in $ComputerName) { $Services = Get-PSService -ComputerName $Computer | Select-Object ComputerName, Name, Displayname, Status, StartType $Services } } } function Get-ComputerSMB { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER ComputerName Parameter description .EXAMPLE Get-ComputerSMB -ComputerName $ENV:COMPUTERNAME .NOTES General notes #> [CmdletBinding()] param( [string[]] $ComputerName ) [Array] $CollectionComputers = $ComputerName.Where( { $_ -eq $Env:COMPUTERNAME }, 'Split') $SMB = @( if ($CollectionComputers[0].Count -gt 0) { $Output = Get-SmbServerConfiguration foreach ($_ in $Output) { [PSCustomObject] @{ ComputerName = $Env:COMPUTERNAME AnnounceComment = $_.AnnounceComment AnnounceServer = $_.AnnounceServer AsynchronousCredits = $_.AsynchronousCredits AuditSmb1Access = $_.AuditSmb1Access AutoDisconnectTimeout = $_.AutoDisconnectTimeout AutoShareServer = $_.AutoShareServer AutoShareWorkstation = $_.AutoShareWorkstation CachedOpenLimit = $_.CachedOpenLimit DurableHandleV2TimeoutInSeconds = $_.DurableHandleV2TimeoutInSeconds EnableAuthenticateUserSharing = $_.EnableAuthenticateUserSharing EnableDownlevelTimewarp = $_.EnableDownlevelTimewarp EnableForcedLogoff = $_.EnableForcedLogoff EnableLeasing = $_.EnableLeasing EnableMultiChannel = $_.EnableMultiChannel EnableOplocks = $_.EnableOplocks EnableSecuritySignature = $_.EnableSecuritySignature EnableSMB1Protocol = $_.EnableSMB1Protocol EnableSMB2Protocol = $_.EnableSMB2Protocol EnableStrictNameChecking = $_.EnableStrictNameChecking EncryptData = $_.EncryptData IrpStackSize = $_.IrpStackSize KeepAliveTime = $_.KeepAliveTime MaxChannelPerSession = $_.MaxChannelPerSession MaxMpxCount = $_.MaxMpxCount MaxSessionPerConnection = $_.MaxSessionPerConnection MaxThreadsPerQueue = $_.MaxThreadsPerQueue MaxWorkItems = $_.MaxWorkItems NullSessionPipes = $_.NullSessionPipes NullSessionShares = $_.NullSessionShares OplockBreakWait = $_.OplockBreakWait PendingClientTimeoutInSeconds = $_.PendingClientTimeoutInSeconds RejectUnencryptedAccess = $_.RejectUnencryptedAccess RequireSecuritySignature = $_.RequireSecuritySignature ServerHidden = $_.ServerHidden Smb2CreditsMax = $_.Smb2CreditsMax Smb2CreditsMin = $_.Smb2CreditsMin SmbServerNameHardeningLevel = $_.SmbServerNameHardeningLevel TreatHostAsStableStorage = $_.TreatHostAsStableStorage ValidateAliasNotCircular = $_.ValidateAliasNotCircular ValidateShareScope = $_.ValidateShareScope ValidateShareScopeNotAliased = $_.ValidateShareScopeNotAliased ValidateTargetName = $_.ValidateTargetName } } } if ($CollectionComputers[1].Count -gt 0) { $Output = Get-SmbServerConfiguration -CimSession $CollectionComputers[1] foreach ($_ in $Output) { [PSCustomObject] @{ ComputerName = $_.PSComputerName AnnounceComment = $_.AnnounceComment AnnounceServer = $_.AnnounceServer AsynchronousCredits = $_.AsynchronousCredits AuditSmb1Access = $_.AuditSmb1Access AutoDisconnectTimeout = $_.AutoDisconnectTimeout AutoShareServer = $_.AutoShareServer AutoShareWorkstation = $_.AutoShareWorkstation CachedOpenLimit = $_.CachedOpenLimit DurableHandleV2TimeoutInSeconds = $_.DurableHandleV2TimeoutInSeconds EnableAuthenticateUserSharing = $_.EnableAuthenticateUserSharing EnableDownlevelTimewarp = $_.EnableDownlevelTimewarp EnableForcedLogoff = $_.EnableForcedLogoff EnableLeasing = $_.EnableLeasing EnableMultiChannel = $_.EnableMultiChannel EnableOplocks = $_.EnableOplocks EnableSecuritySignature = $_.EnableSecuritySignature EnableSMB1Protocol = $_.EnableSMB1Protocol EnableSMB2Protocol = $_.EnableSMB2Protocol EnableStrictNameChecking = $_.EnableStrictNameChecking EncryptData = $_.EncryptData IrpStackSize = $_.IrpStackSize KeepAliveTime = $_.KeepAliveTime MaxChannelPerSession = $_.MaxChannelPerSession MaxMpxCount = $_.MaxMpxCount MaxSessionPerConnection = $_.MaxSessionPerConnection MaxThreadsPerQueue = $_.MaxThreadsPerQueue MaxWorkItems = $_.MaxWorkItems NullSessionPipes = $_.NullSessionPipes NullSessionShares = $_.NullSessionShares OplockBreakWait = $_.OplockBreakWait PendingClientTimeoutInSeconds = $_.PendingClientTimeoutInSeconds RejectUnencryptedAccess = $_.RejectUnencryptedAccess RequireSecuritySignature = $_.RequireSecuritySignature ServerHidden = $_.ServerHidden Smb2CreditsMax = $_.Smb2CreditsMax Smb2CreditsMin = $_.Smb2CreditsMin SmbServerNameHardeningLevel = $_.SmbServerNameHardeningLevel TreatHostAsStableStorage = $_.TreatHostAsStableStorage ValidateAliasNotCircular = $_.ValidateAliasNotCircular ValidateShareScope = $_.ValidateShareScope ValidateShareScopeNotAliased = $_.ValidateShareScopeNotAliased ValidateTargetName = $_.ValidateTargetName } } } ) $SMB } function Get-ComputerSMBShare { [CmdletBinding()] param( [string[]] $ComputerName, [switch] $Translated ) [Array] $CollectionComputers = Get-ComputerSplit -ComputerName $ComputerName if ($CollectionComputers[0].Count -gt 0) { $Output = Get-SmbShare foreach ($O in $Output) { if (-not $Translated) { Add-Member -InputObject $_ -Name 'PSComputerName' -Value $Env:COMPUTERNAME -MemberType NoteProperty -Force $O } else { [PSCustomObject] @{ Name = $O.Name #: ADMIN$ ScopeName = $O.ScopeName #: * Path = $O.Path #: C:\Windows Description = $O.Description #: Remote Admin ComputerName = $O.PSComputerName #: AD1 PresetPathAcl = $O.PresetPathAcl #: ShareState = $O.ShareState.ToString() #: Online AvailabilityType = $O.AvailabilityType.ToString() #: NonClustered ShareType = $O.ShareType.ToString() #: FileSystemDirectory FolderEnumerationMode = $O.FolderEnumerationMode.ToString() #: Unrestricted CachingMode = $O.CachingMode.ToString() #: Manual LeasingMode = $O.LeasingMode.ToString() #: QoSFlowScope = $O.QoSFlowScope #: SmbInstance = $O.SmbInstance.ToString() #: Default CATimeout = $O.CATimeout #: 0 ConcurrentUserLimit = $O.ConcurrentUserLimit #: 0 ContinuouslyAvailable = $O.ContinuouslyAvailable #: False CurrentUsers = $O.CurrentUsers #: 0 EncryptData = $O.EncryptData #: False Scoped = $O.Scoped #: False SecurityDescriptor = $O.SecurityDescriptor #: O:SYG:SYD:(A;;GA;;;BA)(A;;GA;;;BO)(A;;GA;;;IU) ShadowCopy = $O.ShadowCopy #: False Special = $O.Special #: True Temporary = $O.Temporary #: False Volume = $O.Volume #: \\?\Volume{2014dd39-5b27-44a6-be88-1d650346016d}\ } } } } if ($CollectionComputers[1].Count -gt 0) { $Output = Get-SmbShare -CimSession $CollectionComputers[1] foreach ($O in $Output) { if (-not $Translated) { $O } else { [PSCustomObject] @{ Name = $O.Name #: ADMIN$ ScopeName = $O.ScopeName #: * Path = $O.Path #: C:\Windows Description = $O.Description #: Remote Admin ComputerName = $O.PSComputerName #: AD1 PresetPathAcl = $O.PresetPathAcl #: ShareState = $O.ShareState.ToString() #: Online AvailabilityType = $O.AvailabilityType.ToString() #: NonClustered ShareType = $O.ShareType.ToString() #: FileSystemDirectory FolderEnumerationMode = $O.FolderEnumerationMode.ToString() #: Unrestricted CachingMode = $O.CachingMode.ToString() #: Manual LeasingMode = $O.LeasingMode #.ToString() #: QoSFlowScope = $O.QoSFlowScope #: SmbInstance = $O.SmbInstance.ToString() #: Default CATimeout = $O.CATimeout #: 0 ConcurrentUserLimit = $O.ConcurrentUserLimit #: 0 ContinuouslyAvailable = $O.ContinuouslyAvailable #: False CurrentUsers = $O.CurrentUsers #: 0 EncryptData = $O.EncryptData #: False Scoped = $O.Scoped #: False SecurityDescriptor = $O.SecurityDescriptor #: O:SYG:SYD:(A;;GA;;;BA)(A;;GA;;;BO)(A;;GA;;;IU) ShadowCopy = $O.ShadowCopy #: False Special = $O.Special #: True Temporary = $O.Temporary #: False Volume = $O.Volume #: \\?\Volume{2014dd39-5b27-44a6-be88-1d650346016d}\ } } } } } function Get-ComputerSMBSharePermissions { [CmdletBinding()] param( [string[]] $ComputerName, [Parameter(Mandatory = $true)][alias('Name')][string[]] $ShareName, [switch] $Translated ) [Array] $Computers = Get-ComputerSplit -ComputerName $ComputerName if ($Computers[0].Count -gt 0) { foreach ($Share in $ShareName) { try { $Output = Get-SmbShareAccess -Name $Share -ErrorAction Stop } catch { $ErrorMessage = $_.Exception.Message Write-Warning -Message "Get-ComputerSMBSharePermissions - Computer $Env:COMPUTERNAME, Share $Share, Error: $ErrorMessage" } foreach ($O in $Output) { if (-not $Translated) { $O | Add-Member -Name 'PSComputerName' -Value $Env:COMPUTERNAME -MemberType NoteProperty -Force $O } else { [PSCustomObject] @{ Name = $O.Name #: NETLOGON ScopeName = $O.ScopeName #: * AccountName = $O.AccountName.ToString() #: Everyone AccessControlType = $O.AccessControlType.ToString() #: Allow AccessRight = $O.AccessRight.ToString() #: Read ComputerName = $Env:COMPUTERNAME #: AD2.AD.EVOTEC.XYZ } } } } } if ($Computers[1].Count -gt 0) { foreach ($Share in $ShareName) { try { $Output = Get-SmbShareAccess -CimSession $Computers[1] -Name $Share -ErrorAction Stop } catch { $ErrorMessage = $_.Exception.Message Write-Warning -Message "Get-ComputerSMBSharePermissions - Computer $($Computers[1]), Share $Share, Error: $ErrorMessage" } foreach ($O in $Output) { if (-not $Translated) { $O } else { [PSCustomObject] @{ Name = $O.Name #: NETLOGON ScopeName = $O.ScopeName #: * AccountName = $O.AccountName.ToString() #: Everyone AccessControlType = $O.AccessControlType.ToString() #: Allow AccessRight = $O.AccessRight.ToString() #: Read ComputerName = $O.PSComputerName #: AD2.AD.EVOTEC.XYZ } } } } } } function Get-ComputerStartup { [CmdletBinding()] param( [string[]] $ComputerName = $Env:COMPUTERNAME, [ValidateSet('Default', 'Dcom', 'Wsman')][string] $Protocol = 'Default', [switch] $All ) [string] $Class = 'win32_startupCommand' if ($All) { [string] $Properties = '*' } else { [string[]] $Properties = 'Caption', 'Description', 'Command', 'Location', 'Name', 'User', 'UserSID', 'PSComputerName' #, 'SettingID' } $Information = Get-CimData -ComputerName $ComputerName -Protocol $Protocol -Class $Class -Properties $Properties if ($All) { $Information } else { foreach ($Info in $Information) { foreach ($Data in $Info) { # # Remember to expand if changing properties above [PSCustomObject] @{ ComputerName = if ($Data.PSComputerName) { $Data.PSComputerName } else { $Env:COMPUTERNAME } Caption = $Data.Caption Description = $Data.Description #SettingID = $Data.SettingID Command = $Data.Command Location = $Data.Location Name = $Data.Name User = $Data.User UserSID = $Data.UserSID } } } } } function Get-ComputerSystem { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER ComputerName Parameter description .PARAMETER Protocol Parameter description .PARAMETER All Parameter description .EXAMPLE Get-ComputerSystem -ComputerName AD1, AD2, EVO1, ADFFS | ft -a * Output: WARNING: Get-ComputerSystem - No data for computer ADFFS. Most likely an error on receiving side. ComputerName Name Manufacturer Domain Model Systemtype PrimaryOwnerName PCSystemType PartOfDomain CurrentTimeZone BootupState SystemFamily Roles ------------ ---- ------------ ------ ----- ---------- ---------------- ------------ ------------ --------------- ----------- ------------ ----- AD1 AD1 Microsoft Corporation ad.evotec.xyz Virtual Machine x64-based PC Windows User 1 True 60 Normal boot Virtual Machine LM_Workstation, LM_Server, Primary_Domain_Controller, Timesource, NT, DFS AD2 AD2 Microsoft Corporation ad.evotec.xyz Virtual Machine x64-based PC Windows User 1 True 60 Normal boot Virtual Machine LM_Workstation, LM_Server, Backup_Domain_Controller, Timesource, NT, DFS EVO1 EVO1 MSI ad.evotec.xyz MS-7980 x64-based PC 1 True 60 Normal boot Default string LM_Workstation, LM_Server, SQLServer, NT, Potential_Browser, Master_Browser .NOTES General notes #> [CmdletBinding()] param( [string[]] $ComputerName = $Env:COMPUTERNAME, [ValidateSet('Default', 'Dcom', 'Wsman')][string] $Protocol = 'Default', [switch] $All ) [string] $Class = 'Win32_ComputerSystem' if ($All) { $Properties = '*' } else { $Properties = 'PSComputerName', 'Name', 'Manufacturer' , 'Domain', 'Model' , 'Systemtype', 'PrimaryOwnerName', 'PCSystemType', 'PartOfDomain', 'CurrentTimeZone', 'BootupState', 'Roles', 'SystemFamily' } $Information = Get-CimData -ComputerName $ComputerName -Protocol $Protocol -Class $Class -Properties $Properties if ($All) { $Information } else { foreach ($Info in $Information) { foreach ($Data in $Info) { # # Remember to expand if changing properties above [PSCustomObject] @{ ComputerName = if ($Data.PSComputerName) { $Data.PSComputerName } else { $Env:COMPUTERNAME } Name = $Data.Name Manufacturer = $Data.Manufacturer Domain = $Data.Domain Model = $Data.Model Systemtype = $Data.Systemtype PrimaryOwnerName = $Data.PrimaryOwnerName PCSystemType = [Microsoft.PowerShell.Commands.PCSystemType] $Data.PCSystemType PartOfDomain = $Data.PartOfDomain CurrentTimeZone = $Data.CurrentTimeZone BootupState = $Data.BootupState SystemFamily = $Data.SystemFamily Roles = $Data.Roles -join ', ' } } } } } function Get-ComputerTask { <# .SYNOPSIS Get Task Schedule information .DESCRIPTION Get Task Schedule information .PARAMETER ComputerName Specifies computer on which you want to run the operation. .EXAMPLE Get-ComputerTask | Format-Table .NOTES General notes #> [alias('Get-ComputerTasks')] [cmdletbinding()] param( [string[]] $ComputerName = $Env:COMPUTERNAME ) foreach ($Computer in $ComputerName) { # Querying CIM locally usually doesn't work. This means if you're querying same computer you neeed to skip CimSession/ComputerName if it's local query try { $LocalComputerDNSName = [System.Net.Dns]::GetHostByName($Env:COMPUTERNAME).HostName } catch { $LocalComputerDNSName = $Computer } if ($Computer -eq $Env:COMPUTERNAME -or $Computer -eq $LocalComputerDNSName) { $TaskParameters = @{} } else { $TaskParameters = @{ CimSession = $Computer } } # Full code $Tasks = Get-ScheduledTask @TaskParameters foreach ($Task in $Tasks) { $Info = $Task | Get-ScheduledTaskInfo @TaskParameters $Actions = foreach ($_ in $Task.Actions) { -join ($_.Execute, $_.Arguments) } [PSCustomObject] @{ ComputerName = $Computer TaskName = $Task.TaskName TaskPath = $Task.TaskPath State = $Task.State Actions = $Actions Author = $Task.Author Date = $Task.Date Description = $Task.Description Documentation = $Task.Documentation PrincipalDisplayName = $Task.Principal.DisplayName PrincipalUserID = $Task.Principal.UserID PrincipalGroupID = $Task.Principal.GroupID PrincipalLogonType = $Task.Principal.LogonType PrincipalRunLevel = $Task.Principal.RunLevel PrincipalProcessTokenSidType = $Task.Principal.ProcessTokenSidType PrincipalRequiredPrivilege = $Task.Principal.RequiredPrivilege #SecurityDescriptor = $Task.SecurityDescriptor # | ConvertFrom-SddlString #Settings = $Task.Settings SettingsAllowDemandStart = $Task.Settings.AllowDemandStart SettingsAllowHardTerminate = $Task.Settings.AllowHardTerminate SettingsCompatibility = $Task.Settings.Compatibility SettingsDeleteExpiredTaskAfter = $Task.Settings.DeleteExpiredTaskAfter SettingsDisallowStartIfOnBatteries = $Task.Settings.DisallowStartIfOnBatteries SettingsEnabled = $Task.Settings.Enabled SettingsExecutionTimeLimit = $Task.Settings.ExecutionTimeLimit SettingsHidden = $Task.Settings.Hidden SettingsIdleSettings = $Task.Settings.IdleSettings SettingsMultipleInstances = $Task.Settings.MultipleInstances SettingsNetworkSettings = $Task.Settings.NetworkSettings SettingsPriority = $Task.Settings.Priority SettingsRestartCount = $Task.Settings.RestartCount SettingsRestartInterval = $Task.Settings.RestartInterval SettingsRunOnlyIfIdle = $Task.Settings.RunOnlyIfIdle SettingsRunOnlyIfNetworkAvailable = $Task.Settings.RunOnlyIfNetworkAvailable SettingsStartWhenAvailable = $Task.Settings.StartWhenAvailable SettingsStopIfGoingOnBatteries = $Task.Settings.StopIfGoingOnBatteries SettingsWakeToRun = $Task.Settings.WakeToRun SettingsDisallowStartOnRemoteAppSession = $Task.Settings.DisallowStartOnRemoteAppSession SettingsUseUnifiedSchedulingEngine = $Task.Settings.UseUnifiedSchedulingEngine SettingsMaintenanceSettings = $Task.Settings.MaintenanceSettings SettingsVolatile = $Task.Settings.volatile Source = $Task.Source #Triggers = $Task.Triggers URI = $Task.URI Version = $Task.Version LastRunTime = $Info.LastRunTime LastTaskResult = $Info.LastTaskResult NextRunTime = $Info.NextRunTime NumberOfMissedRuns = $Info.NumberOfMissedRuns } } } } #$Tasks = Get-ComputerTasks -ComputerName $Env:COMPUTERNAME #| Format-Table -AutoSize #$Tasks.SecurityDescriptor | ConvertFrom-SddlString function Get-ComputerTime { <# .SYNOPSIS # .DESCRIPTION Long description .PARAMETER TimeSource Parameter description .PARAMETER Domain Parameter description .PARAMETER TimeTarget Parameter description .PARAMETER ForceCIM .PARAMETER ToLocal .EXAMPLE Get-ComputerTime -TimeTarget AD2, AD3, EVOWin | Format-Table -AutoSize Get-ComputerTime -TimeSource AD1 -TimeTarget AD2, AD3, EVOWin | Format-Table -AutoSize Get-ComputerTime -TimeSource 'pool.ntp.org' -TimeTarget AD2, AD3, EVOWin | Format-Table -AutoSize Output Name LocalDateTime RemoteDateTime InstallTime LastBootUpTime TimeDifferenceMinutes TimeDifferenceSeconds TimeDifferenceMilliseconds TimeSourceName ---- ------------- -------------- ----------- -------------- --------------------- --------------------- -------------------------- -------------- AD2 13.08.2019 23:40:26 13.08.2019 23:40:26 30.05.2018 18:30:48 09.08.2019 18:40:31 8,33333333333333E-05 0,005 5 AD1.ad.evotec.xyz AD3 13.08.2019 23:40:26 13.08.2019 17:40:26 26.05.2019 17:30:17 09.08.2019 18:40:30 0,000266666666666667 0,016 16 AD1.ad.evotec.xyz EVOWin 13.08.2019 23:40:26 13.08.2019 23:40:26 24.05.2019 22:46:45 09.08.2019 18:40:06 6,66666666666667E-05 0,004 4 AD1.ad.evotec.xyz .NOTES General notes #> [CmdletBinding()] param( [string] $TimeSource, [string] $Domain = $Env:USERDNSDOMAIN, [alias('ComputerName')][string[]] $TimeTarget = $ENV:COMPUTERNAME, [switch] $ForceCIM ) if (-not $TimeSource) { $TimeSource = (Get-ADDomainController -Discover -Service PrimaryDC -DomainName $Domain).HostName } if ($ForceCIM) { $TimeSourceInformation = Get-CimData -ComputerName $TimeSource -Class 'win32_operatingsystem' if ($TimeSourceInformation.LocalDateTime) { $TimeSourceInformation = $TimeSourceInformation.LocalDateTime } else { $TimeSourceInformation = $null } } else { $TimeSourceInformation = Get-ComputerTimeNtp -Server $TimeSource -ToLocal } $TimeTargetInformationCache = @{ } $TimeTargetInformation = Get-CimData -ComputerName $TimeTarget -Class 'win32_operatingsystem' foreach ($_ in $TimeTargetInformation) { $TimeTargetInformationCache[$_.PSComputerName] = $_ } $TimeLocalCache = @{ } $TimeLocal = Get-CimData -ComputerName $TimeTarget -Class 'Win32_LocalTime' foreach ($_ in $TimeLocal) { $TimeLocalCache[$_.PSComputerName] = $_ } $AllResults = foreach ($Computer in $TimeTarget) { $WMIComputerTime = $TimeLocalCache[$Computer] $WMIComputerTarget = $TimeTargetInformationCache[$Computer] if ($WMIComputerTime -and $WMIComputerTime.Year -and $WMIComputerTime.Month) { $RemoteDateTime = Get-Date -Year $WMIComputerTime.Year -Month $WMIComputerTime.Month -Day $WMIComputerTime.Day -Hour $WMIComputerTime.Hour -Minute $WMIComputerTime.Minute -Second $WMIComputerTime.Second } else { $RemoteDateTIme = '' } if ($WMIComputerTarget.LocalDateTime -and $TimeSourceInformation) { $Result = New-TimeSpan -Start $TimeSourceInformation -End $WMIComputerTarget.LocalDateTime [PSCustomObject] @{ Name = $Computer LocalDateTime = $WMIComputerTarget.LocalDateTime RemoteDateTime = $RemoteDateTime InstallTime = $WMIComputerTarget.InstallDate LastBootUpTime = $WMIComputerTarget.LastBootUpTime TimeDifferenceMinutes = if ($Result.TotalMinutes -lt 0) { ($Result.TotalMinutes * -1) } else { $Result.TotalMinutes } TimeDifferenceSeconds = if ($Result.TotalSeconds -lt 0) { ($Result.TotalSeconds * -1) } else { $Result.TotalSeconds } TimeDifferenceMilliseconds = if ($Result.TotalMilliseconds -lt 0) { ($Result.TotalMilliseconds * -1) } else { $Result.TotalMilliseconds } TimeSourceName = $TimeSource Status = '' } } else { [PSCustomObject] @{ Name = $Computer LocalDateTime = $WMIComputerTarget.LocalDateTime RemoteDateTime = $RemoteDateTime InstallTime = $WMIComputerTarget.InstallDate LastBootUpTime = $WMIComputerTarget.LastBootUpTime TimeDifferenceMinutes = $null TimeDifferenceSeconds = $null TimeDifferenceMilliseconds = $null TimeSourceName = $TimeSource Status = 'Unable to get time difference.' } } } $AllResults } function Get-ComputerTimeNtp { <# .Synopsis Gets (Simple) Network Time Protocol time (SNTP/NTP, rfc-1305, rfc-2030) from a specified server .DESCRIPTION This function connects to an NTP server on UDP port 123 and retrieves the current NTP time. Selected components of the returned time information are decoded and returned in a PSObject. .PARAMETER Server The NTP Server to contact. Uses pool.ntp.org by default. .EXAMPLE Get-NtpTime uk.pool.ntp.org Gets time from the specified server. .EXAMPLE Get-NtpTime | fl * Get time from default server (pool.ntp.org) and displays all output object attributes. .FUNCTIONALITY Gets NTP time from a specified server. .NOTES Author https://github.com/ChrisWarwick/PowerShell-NTP-Time Slightly simplified for different usage scenarios #> [CmdletBinding()] Param ( [String]$Server = 'pool.ntp.org', [switch]$ToLocal ) # NTP Times are all UTC and are relative to midnight on 1/1/1900 $StartOfEpoch = New-Object DateTime(1900, 1, 1, 0, 0, 0, [DateTimeKind]::Utc) # Construct a 48-byte client NTP time packet to send to the specified server # (Request Header: [00=No Leap Warning; 011=Version 3; 011=Client Mode]; 00011011 = 0x1B) [Byte[]]$NtpData = , 0 * 48 $NtpData[0] = 0x1B # NTP Request header in first byte $Socket = [Net.Sockets.Socket]::new([Net.Sockets.AddressFamily]::InterNetwork, [Net.Sockets.SocketType]::Dgram, [Net.Sockets.ProtocolType]::Udp) $Socket.SendTimeOut = 2000 # ms $Socket.ReceiveTimeOut = 2000 # ms Try { $Socket.Connect($Server, 123) } Catch { $_.Error Write-Warning "Get-ComputerTimeNtp - Failed to connect to server $Server" return } # NTP Transaction ------------------------------------------------------- $t1 = Get-Date # t1, Start time of transaction... Try { [Void]$Socket.Send($NtpData) [Void]$Socket.Receive($NtpData) } Catch { Write-Warning "Get-ComputerTimeNtp - Failed to communicate with server $Server" return } $t4 = Get-Date # End of NTP transaction time # End of NTP Transaction ------------------------------------------------ $Socket.Shutdown("Both") $Socket.Close() $LI = ($NtpData[0] -band 0xC0) -shr 6 # Leap Second indicator If ($LI -eq 3) { Write-Warning 'Get-ComputerTimeNtp - Alarm condition from server (clock not synchronized)' return } # Convert Integer and Fractional parts of the (64-bit) t3 NTP time from the byte array $IntPart = [BitConverter]::ToUInt32($NtpData[43..40], 0) $FracPart = [BitConverter]::ToUInt32($NtpData[47..44], 0) # Convert to Millseconds (convert fractional part by dividing value by 2^32) $t3ms = $IntPart * 1000 + ($FracPart * 1000 / 0x100000000) # Perform the same calculations for t2 (in bytes [32..39]) $IntPart = [BitConverter]::ToUInt32($NtpData[35..32], 0) $FracPart = [BitConverter]::ToUInt32($NtpData[39..36], 0) $t2ms = $IntPart * 1000 + ($FracPart * 1000 / 0x100000000) # Calculate values for t1 and t4 as milliseconds since 1/1/1900 (NTP format) $t1ms = ([TimeZoneInfo]::ConvertTimeToUtc($t1) - $StartOfEpoch).TotalMilliseconds $t4ms = ([TimeZoneInfo]::ConvertTimeToUtc($t4) - $StartOfEpoch).TotalMilliseconds # Calculate the NTP Offset and Delay values $Offset = (($t2ms - $t1ms) + ($t3ms - $t4ms)) / 2 #$Delay = ($t4ms - $t1ms) - ($t3ms - $t2ms) [DateTime] $NTPDateTime = $StartOfEpoch.AddMilliseconds($t4ms + $Offset) if ($ToLocal) { $NTPDateTime.ToLocalTime() } else { $NTPDateTime } } function Get-ComputerWindowsFeatures { <# .SYNOPSIS Get Windows Features status on one or more computers/servers .DESCRIPTION Get Windows Features status on one or more computers/servers .PARAMETER ComputerName ComputerName to provide when executing query remotly. By default current computer name is used. .PARAMETER Protocol Protocol to use when gathering data. Choices are Default, Dcom, WSMan .PARAMETER EnabledOnly Returns only data if Windows Feature is enabled .PARAMETER All Gets all properties without any preprocessing .EXAMPLE Get-ComputerWindowsFeatures -EnabledOnly | Format-Table .NOTES General notes #> [CmdletBinding()] param( [string] $ComputerName = $Env:COMPUTERNAME, [ValidateSet('Default', 'Dcom', 'Wsman')][string] $Protocol = 'Default', [switch] $EnabledOnly, [switch] $All ) [string] $Class = 'Win32_OptionalFeature' if ($All) { [string] $Properties = '*' } else { [string[]] $Properties = 'Name', 'Caption' , 'Status', 'InstallState', 'InstallDate', 'PSComputerName' } $State = @{ '1' = 'Enabled' '2' = 'Disabled' '3' = 'Absent' '4' = 'Unknown' } $Information = Get-CimData -ComputerName $ComputerName -Protocol $Protocol -Class $Class -Properties $Properties if ($All) { $Information } else { foreach ($Info in $Information) { foreach ($Data in $Info) { # # Remember to expand if changing properties above $InstallState = $State["$($Data.InstallState)"] if ($EnabledOnly -and $InstallState -ne 'Enabled') { continue } [PSCustomObject] @{ ComputerName = if ($Data.PSComputerName) { $Data.PSComputerName } else { $Env:COMPUTERNAME } Name = $Data.Name Caption = $Data.Caption InstallState = $InstallState #InstallDate = $Data.InstallDate #Status = $Data.Status } } } } } function Get-ComputerWindowsUpdates { [CmdletBinding()] param( [string[]] $ComputerName = $Env:COMPUTERNAME ) # Get-CimInstance -Class win32_quickfixengineering | Where-Object { $_.InstalledOn -gt (Get-Date).AddMonths(-3) } $Data = Get-HotFix -ComputerName $ComputerName | Select-Object Description , HotFixId , InstallDate, InstalledBy, InstalledOn, Caption, PSComputerName, Status, FixComments, ServicePackInEffect, Name, Site, Containerr return $Data } #$Hotfix = Get-ComputerWindowsUpdates -ComputerName EVOWIN, AD1 #| ft -a * #$Hotfix | ft -a * #$Hotfix[0] | fl * #Get-CimData -Class 'Win32_QuickFixEngineering' -ComputerName EVOWIN,AD1 | ft -AutoSize * function Get-OperatingSystem { [cmdletbinding()] param( [string] $Version ) $ListOperatingSystems = [ordered] @{ '10.0 (19043)' = [PSCustomObject] @{ Name = 'Windows 10 21H1'; Version = '10.0 (19043)'; CodeName = '21H1'; MarketingName = 'May 2021 Update'; BuildNumber = '19043'; ReleaseDate = (Get-Date -Year 2021 -Month 5 -Day 18 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndPro = (Get-Date -Year 2022 -Month 12 -Day 13 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndEnterprise = (Get-Date -Year 2022 -Month 12 -Day 13 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); LTSC = $null } '10.0 (19042)' = [PSCustomObject] @{ Name = 'Windows 10 20H2'; Version = '10.0 (19042)'; CodeName = '20H2'; MarketingName = 'October 2020 Update'; BuildNumber = '19042'; ReleaseDate = (Get-Date -Year 2020 -Month 9 -Day 20 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndPro = (Get-Date -Year 2022 -Month 5 -Day 10 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndEnterprise = (Get-Date -Year 2023 -Month 5 -Day 9 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); LTSC = $null } '10.0 (19041)' = [PSCustomObject] @{ Name = 'Windows 10 2004'; Version = '10.0 (19041)'; CodeName = '20H1'; MarketingName = 'May 2020 Update'; BuildNumber = '19041'; ReleaseDate = (Get-Date -Year 2020 -Month 5 -Day 27 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndPro = (Get-Date -Year 2021 -Month 12 -Day 14 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndEnterprise = (Get-Date -Year 2021 -Month 12 -Day 14 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); LTSC = $null } '10.0 (18363)' = [PSCustomObject] @{ Name = "Windows 10 1909"; Version = '10.0 (18363)'; CodeName = '19H2'; MarketingName = 'November 2019 Update'; BuildNumber = '18363'; ReleaseDate = (Get-Date -Year 2019 -Month 11 -Day 12 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndPro = (Get-Date -Year 2021 -Month 5 -Day 11 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndEnterprise = (Get-Date -Year 2022 -Month 5 -Day 10 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); LTSC = $null } '10.0 (18362)' = [PSCustomObject] @{ Name = "Windows 10 1903"; Version = '10.0 (18362)'; CodeName = '19H1'; MarketingName = 'May 2019 Update'; BuildNumber = '18362'; ReleaseDate = (Get-Date -Year 2019 -Month 5 -Day 21 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndPro = (Get-Date -Year 2020 -Month 12 -Day 8 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndEnterprise = (Get-Date -Year 2020 -Month 12 -Day 8 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); LTSC = $null } '10.0 (17763)' = [PSCustomObject] @{ Name = "Windows 10 1809"; Version = '10.0 (17763)'; CodeName = 'Redstone 5'; MarketingName = 'October 2018 Update'; BuildNumber = '17763'; ReleaseDate = (Get-Date -Year 2018 -Month 11 -Day 13 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndPro = (Get-Date -Year 2020 -Month 11 -Day 10 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndEnterprise = (Get-Date -Year 2021 -Month 5 -Day 11 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); LTSC = (Get-Date -Year 2029 -Month 1 -Day 9 -Second 1 -Minute 1 -Hour 1 -Millisecond 1) } '10.0 (17134)' = [PSCustomObject] @{ Name = "Windows 10 1803"; Version = '10.0 (17134)'; CodeName = 'Redstone 4'; MarketingName = 'April 2018 Update'; BuildNumber = '17134'; ReleaseDate = (Get-Date -Year 2018 -Month 4 -Day 30 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndPro = (Get-Date -Year 2020 -Month 11 -Day 12 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndEnterprise = (Get-Date -Year 2021 -Month 5 -Day 11 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); LTSC = $null } '10.0 (16299)' = [PSCustomObject] @{ Name = "Windows 10 1709"; Version = '10.0 (16299)'; CodeName = 'Redstone 3'; MarketingName = 'Fall Creators Update'; BuildNumber = '16299'; ReleaseDate = (Get-Date -Year 2017 -Month 9 -Day 17 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndPro = (Get-Date -Year 2019 -Month 4 -Day 9 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndEnterprise = (Get-Date -Year 2020 -Month 10 -Day 13 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); ; LTSC = $null } '10.0 (15063)' = [PSCustomObject] @{ Name = "Windows 10 1703"; Version = '10.0 (15063)'; CodeName = 'Redstone 2'; MarketingName = 'Creators Update'; BuildNumber = '15063'; ReleaseDate = (Get-Date -Year 2017 -Month 4 -Day 5 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndPro = (Get-Date -Year 2018 -Month 10 -Day 9 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndEnterprise = (Get-Date -Year 2019 -Month 10 -Day 8 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); ; LTSC = $null } '10.0 (14393)' = [PSCustomObject] @{ Name = "Windows 10 1607"; Version = '10.0 (14393)'; CodeName = 'Redstone 1'; MarketingName = 'Anniversary Update'; BuildNumber = '14393'; ReleaseDate = (Get-Date -Year 2016 -Month 8 -Day 2 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndPro = (Get-Date -Year 2018 -Month 4 -Day 10 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndEnterprise = (Get-Date -Year 2019 -Month 4 -Day 9 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); LTSC = (Get-Date -Year 2026 -Month 10 -Day 13 -Second 1 -Minute 1 -Hour 1 -Millisecond 1) } '10.0 (10586)' = [PSCustomObject] @{ Name = "Windows 10 1511"; Version = '10.0 (10586)'; CodeName = 'Threshold 2'; MarketingName = 'November Update'; BuildNumber = '10586'; ReleaseDate = (Get-Date -Year 2015 -Month 11 -Day 10 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndPro = (Get-Date -Year 2017 -Month 10 -Day 10 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndEnterprise = (Get-Date -Year 2018 -Month 4 -Day 10 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); LTSC = $null } '10.0 (10240)' = [PSCustomObject] @{ Name = "Windows 10 1507"; Version = '10.0 (10240)' ; CodeName = 'Threshold 1'; MarketingName = 'N/A'; BuildNumber = '10240'; ReleaseDate = (Get-Date -Year 2015 -Month 7 -Day 29 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndPro = (Get-Date -Year 2017 -Month 5 -Day 9 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); SupportEndEnterprise = (Get-Date -Year 2017 -Month 5 -Day 9 -Second 1 -Minute 1 -Hour 1 -Millisecond 1); LTSC = (Get-Date -Year 2025 -Month 10 -Day 14 -Second 1 -Minute 1 -Hour 1 -Millisecond 1) } } if ($Version) { $ListOperatingSystems[$Version] } else { $ListOperatingSystems.Values } } function Get-IPAddressInformation { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER IP Parameter description .EXAMPLE Get-IpAddressInformation -IP 1.1.1.1 .NOTES General notes #> [cmdletbinding()] param( [string] $IP ) try { $Information = Invoke-RestMethod -Method get -Uri "http://ip-api.com/json/$ip" } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " Write-Warning "Get-IPAddressInformation - Error occured on IP $IP`: $ErrorMessage" } return $Information } function Get-MyIpAddress { [alias('Get-MyIP')] [CmdletBinding()] param() $DNSParam = @{ Name = 'myip.opendns.com' Server = 'resolver1.opendns.com' DnsOnly = $true } return Resolve-DnsName @DNSParam | ForEach-Object IPAddress } function Set-PasswordRemotely { [CmdletBinding(DefaultParameterSetName = 'Secure')] param( [Parameter(ParameterSetName = 'Secure', Mandatory)][string] $UserName, [Parameter(ParameterSetName = 'Secure', Mandatory)][securestring] $OldPassword, [Parameter(ParameterSetName = 'Secure', Mandatory)][securestring] $NewPassword, [Parameter(ParameterSetName = 'Secure')][alias('DC', 'Server', 'ComputerName')][string] $DomainController ) Begin { $DllImport = @' [DllImport("netapi32.dll", CharSet = CharSet.Unicode)] public static extern bool NetUserChangePassword(string domain, string username, string oldpassword, string newpassword); '@ $NetApi32 = Add-Type -MemberDefinition $DllImport -Name 'NetApi32' -Namespace 'Win32' -PassThru if (-not $DomainController) { if ($env:computername -eq $env:userdomain) { # not joined to domain, lets prompt for DC $DomainController = Read-Host -Prompt 'Domain Controller DNS name or IP Address' } else { $Domain = $Env:USERDNSDOMAIN $Context = [System.DirectoryServices.ActiveDirectory.DirectoryContext]::new([System.DirectoryServices.ActiveDirectory.DirectoryContextType]::Domain, $Domain) $DomainController = ([System.DirectoryServices.ActiveDirectory.DomainController]::FindOne($Context)).Name } } } Process { if ($DomainController -and $OldPassword -and $NewPassword -and $UserName) { $OldPasswordPlain = [System.Net.NetworkCredential]::new([string]::Empty, $OldPassword).Password $NewPasswordPlain = [System.Net.NetworkCredential]::new([string]::Empty, $NewPassword).Password $result = $NetApi32::NetUserChangePassword($DomainController, $UserName, $OldPasswordPlain, $NewPasswordPlain) if ($result) { Write-Host -Object "Set-PasswordRemotely - Password change for account $UserName failed on $DomainController. Please try again." -ForegroundColor Red } else { Write-Host -Object "Set-PasswordRemotely - Password change for account $UserName succeeded on $DomainController." -ForegroundColor Cyan } } else { Write-Warning "Set-PasswordRemotely - Password change for account failed. All parameters are required. " } } End { $OldPassword = $null $NewPassword = $null $OldPasswordPlain = $null $NewPasswordPlain = $null [System.GC]::Collect() } } 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-Object { "{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) } } function Convert-Color { <# .Synopsis This color converter gives you the hexadecimal values of your RGB colors and vice versa (RGB to HEX) .Description This color converter gives you the hexadecimal values of your RGB colors and vice versa (RGB to HEX). Use it to convert your colors and prepare your graphics and HTML web pages. .Parameter RBG Enter the Red Green Blue value comma separated. Red: 51 Green: 51 Blue: 204 for example needs to be entered as 51,51,204 .Parameter HEX Enter the Hex value to be converted. Do not use the '#' symbol. (Ex: 3333CC converts to Red: 51 Green: 51 Blue: 204) .Example .\convert-color -hex FFFFFF Converts hex value FFFFFF to RGB .Example .\convert-color -RGB 123,200,255 Converts Red = 123 Green = 200 Blue = 255 to Hex value #> param( [Parameter(ParameterSetName = "RGB", Position = 0)] [ValidateScript( { $_ -match '^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$' })] $RGB, [Parameter(ParameterSetName = "HEX", Position = 0)] [ValidateScript( { $_ -match '[A-Fa-f0-9]{6}' })] [string] $HEX ) switch ($PsCmdlet.ParameterSetName) { "RGB" { if ($null -eq $RGB[2]) { Write-Error "Value missing. Please enter all three values seperated by comma." } $red = [convert]::Tostring($RGB[0], 16) $green = [convert]::Tostring($RGB[1], 16) $blue = [convert]::Tostring($RGB[2], 16) if ($red.Length -eq 1) { $red = '0' + $red } if ($green.Length -eq 1) { $green = '0' + $green } if ($blue.Length -eq 1) { $blue = '0' + $blue } Write-Output $red$green$blue } "HEX" { $red = $HEX.Remove(2, 4) $Green = $HEX.Remove(4, 2) $Green = $Green.remove(0, 2) $Blue = $hex.Remove(0, 4) $Red = [convert]::ToInt32($red, 16) $Green = [convert]::ToInt32($green, 16) $Blue = [convert]::ToInt32($blue, 16) Write-Output $red, $Green, $blue } } } function Convert-CountryCodeToCountry { <# .SYNOPSIS Converts a country code to a country name, or when used with a switch to full culture information .DESCRIPTION Converts a country code to a country name, or when used with a switch to full culture information .PARAMETER CountryCode Country code .PARAMETER All Provide full culture information rather than just the country name .EXAMPLE Convert-CountryCodeToCountry -CountryCode 'PL' .EXAMPLE Convert-CountryCodeToCountry -CountryCode 'PL' -All .EXAMPLE $Test = Convert-CountryCodeToCountry $Test['PL']['Culture'] | fl $Test['PL']['RegionInformation'] .EXAMPLE Convert-CountryCodeToCountry -CountryCode 'PL' Convert-CountryCodeToCountry -CountryCode 'POL' .NOTES General notes #> [cmdletBinding()] param( [string] $CountryCode, [switch] $All ) if ($Script:QuickSearch) { if ($PSBoundParameters.ContainsKey('CountryCode')) { if ($All) { $Script:QuickSearch[$CountryCode] } else { $Script:QuickSearch[$CountryCode].RegionInformation.EnglishName } } else { $Script:QuickSearch } } else { $Script:QuickSearch = [ordered] @{} $AllCultures = [cultureinfo]::GetCultures([System.Globalization.CultureTypes]::SpecificCultures) foreach ($Culture in $AllCultures) { $RegionInformation = [System.Globalization.RegionInfo]::new($Culture) $Script:QuickSearch[$RegionInformation.TwoLetterISORegionName] = @{ 'Culture' = $Culture 'RegionInformation' = $RegionInformation } $Script:QuickSearch[$RegionInformation.ThreeLetterISORegionName] = @{ 'Culture' = $Culture 'RegionInformation' = $RegionInformation } } if ($PSBoundParameters.ContainsKey('CountryCode')) { if ($All) { $Script:QuickSearch[$CountryCode] } else { $Script:QuickSearch[$CountryCode].RegionInformation.EnglishName } } else { $Script:QuickSearch } } } function Convert-CountryToContinent { <# .SYNOPSIS Convert country to continent .DESCRIPTION Convert country to continent or return a hashtable of countries and their corresponding continent. If the country is not found (for example empty), it will return "Unknown" .PARAMETER Country Country to convert. If country is not given it will return a hashtable of countries and their corresponding continent. .EXAMPLE Convert-CountryToContinent -Country "Poland" .EXAMPLE Convert-CountryToContinent .NOTES General notes #> [CmdletBinding()] param( [string] $Country, [switch] $ReturnHashTable ) $CountryToContinent = [ordered] @{ "Afghanistan" = "Asia" "Albania" = "Europe" "Algeria" = "Africa" "Andorra" = "Europe" "Angola" = "Africa" "Antigua and Barbuda" = "North America" "Argentina" = "South America" "Armenia" = "Asia" "Australia" = "Australia/Oceania" "Austria" = "Europe" "Azerbaijan" = "Asia" "Bahamas" = "North America" "Bahrain" = "Asia" "Bangladesh" = "Asia" "Barbados" = "North America" "Belarus" = "Europe" "Belgium" = "Europe" "Belize" = "North America" "Benin" = "Africa" "Bhutan" = "Asia" "Bolivia" = "South America" "Bosnia and Herzegovina" = "Europe" "Botswana" = "Africa" "Brazil" = "South America" "Brunei" = "Asia" "Bulgaria" = "Europe" "Burkina Faso" = "Africa" "Burundi" = "Africa" "Cabo Verde" = "Africa" "Cambodia" = "Asia" "Cameroon" = "Africa" "Canada" = "North America" "Central African Republic" = "Africa" "Chad" = "Africa" "Chile" = "South America" "China" = "Asia" "Colombia" = "South America" "Comoros" = "Africa" "Congo, Democratic Republic of the" = "Africa" "Congo, Republic of the" = "Africa" "Costa Rica" = "North America" "Cote d'Ivoire" = "Africa" "Croatia" = "Europe" "Cuba" = "North America" "Cyprus" = "Asia" "Czechia" = "Europe" "Denmark" = "Europe" "Djibouti" = "Africa" "Dominica" = "North America" "Dominican Republic" = "North America" "Ecuador" = "South America" "Egypt" = "Africa" "El Salvador" = "North America" "Equatorial Guinea" = "Africa" "Eritrea" = "Africa" "Estonia" = "Europe" "Eswatini" = "Africa" "Ethiopia" = "Africa" "Fiji" = "Australia/Oceania" "Finland" = "Europe" "France" = "Europe" "Gabon" = "Africa" "Gambia" = "Africa" "Georgia" = "Asia" "Germany" = "Europe" "Ghana" = "Africa" "Greece" = "Europe" "Grenada" = "North America" "Guatemala" = "North America" "Guinea" = "Africa" "Guinea-Bissau" = "Africa" "Guyana" = "South America" "Haiti" = "North America" "Honduras" = "North America" "Hungary" = "Europe" "Iceland" = "Europe" "India" = "Asia" "Indonesia" = "Asia" "Iran" = "Asia" "Iraq" = "Asia" "Ireland" = "Europe" "Israel" = "Asia" "Italy" = "Europe" "Jamaica" = "North America" "Japan" = "Asia" "Jordan" = "Asia" "Kazakhstan" = "Asia" "Kenya" = "Africa" "Kiribati" = "Australia/Oceania" "Kosovo" = "Europe" "Kuwait" = "Asia" "Kyrgyzstan" = "Asia" "Laos" = "Asia" "Latvia" = "Europe" "Lebanon" = "Asia" "Lesotho" = "Africa" "Liberia" = "Africa" "Libya" = "Africa" "Liechtenstein" = "Europe" "Lithuania" = "Europe" "Luxembourg" = "Europe" "Madagascar" = "Africa" "Malawi" = "Africa" "Malaysia" = "Asia" "Maldives" = "Asia" "Mali" = "Africa" "Malta" = "Europe" "Marshall Islands" = "Australia/Oceania" "Mauritania" = "Africa" "Mauritius" = "Africa" "Mexico" = "North America" "Micronesia" = "Australia/Oceania" "Moldova" = "Europe" "Monaco" = "Europe" "Mongolia" = "Asia" "Montenegro" = "Europe" "Morocco" = "Africa" "Mozambique" = "Africa" "Myanmar" = "Asia" "Namibia" = "Africa" "Nauru" = "Australia/Oceania" "Nepal" = "Asia" "Netherlands" = "Europe" "New Zealand" = "Australia/Oceania" "Nicaragua" = "North America" "Niger" = "Africa" "Nigeria" = "Africa" "North Korea" = "Asia" "North Macedonia" = "Europe" "Norway" = "Europe" "Oman" = "Asia" "Pakistan" = "Asia" "Palau" = "Australia/Oceania" "Panama" = "North America" "Papua New Guinea" = "Australia/Oceania" "Paraguay" = "South America" "Peru" = "South America" "Philippines" = "Asia" "Poland" = "Europe" "Portugal" = "Europe" "Qatar" = "Asia" "Romania" = "Europe" "Russia" = "Asia" "Rwanda" = "Africa" "Saint Kitts and Nevis" = "North America" "Saint Lucia" = "North America" "Saint Vincent and the Grenadines" = "North America" "Samoa" = "Australia/Oceania" "San Marino" = "Europe" "Sao Tome and Principe" = "Africa" "Saudi Arabia" = "Asia" "Senegal" = "Africa" "Serbia" = "Europe" "Seychelles" = "Africa" "Sierra Leone" = "Africa" "Singapore" = "Asia" "Slovakia" = "Europe" "Slovenia" = "Europe" "Solomon Islands" = "Australia/Oceania" "Somalia" = "Africa" "South Africa" = "Africa" "South Korea" = "Asia" "South Sudan" = "Africa" "Spain" = "Europe" "Sri Lanka" = "Asia" "Sudan" = "Africa" "Suriname" = "South America" "Sweden" = "Europe" "Switzerland" = "Europe" "Syria" = "Asia" "Taiwan" = "Asia" "Tajikistan" = "Asia" "Tanzania" = "Africa" "Thailand" = "Asia" "Timor-Leste" = "Asia" "Togo" = "Africa" "Tonga" = "Australia/Oceania" "Trinidad and Tobago" = "North America" "Tunisia" = "Africa" "Turkey" = "Asia" "Turkmenistan" = "Asia" "Tuvalu" = "Australia/Oceania" "Uganda" = "Africa" "Ukraine" = "Europe" "United Arab Emirates" = "Asia" "United Kingdom" = "Europe" "United States of America" = "North America" "Uruguay" = "South America" "Uzbekistan" = "Asia" "Vanuatu" = "Australia/Oceania" "Vatican City (Holy See)" = "Europe" "Venezuela" = "South America" "Vietnam" = "Asia" "Yemen" = "Asia" "Zambia" = "Africa" "Zimbabwe" = "Africa" } if ($PSBoundParameters.ContainsKey('Country')) { if ($CountryToContinent[$Country]) { $CountryToContinent[$Country] } else { "Unknown" } } else { $CountryToContinent } } function Convert-CountryToCountryCode { <# .SYNOPSIS Converts a country name to a country code, or when used with a switch to full culture information .DESCRIPTION Converts a country name to a country code, or when used with a switch to full culture information .PARAMETER CountryName Country name in it's english name .PARAMETER All Provide full culture information rather than just the country code .EXAMPLE Convert-CountryToCountryCode -CountryName 'Poland' .EXAMPLE Convert-CountryToCountryCode -CountryName 'Poland' -All .EXAMPLE $Test = Convert-CountryToCountryCode $Test['India']['Culture'] $Test['India']['RegionInformation'] .EXAMPLE $Test = Convert-CountryToCountryCode $Test['Poland']['Culture'] $Test['Poland']['RegionInformation'] .EXAMPLE Convert-CountryToCountryCode -CountryName 'Polska' Convert-CountryToCountryCode -CountryName 'Poland' Convert-CountryToCountryCode -CountryName 'CZECH REPUBLIC' Convert-CountryToCountryCode -CountryName 'USA' .NOTES General notes #> [cmdletBinding()] param( [string] $CountryName, [switch] $All ) if ($Script:QuickSearchCountries) { if ($CountryName) { if ($All) { $Script:QuickSearchCountries[$CountryName] } else { if ($Script:QuickSearchCountries[$CountryName]) { $Script:QuickSearchCountries[$CountryName].RegionInformation.TwoLetterISORegionName.ToUpper() } else { if ($PSBoundParameters.ErrorAction -eq 'Stop') { throw "Country $CountryName not found" } else { Write-Warning -Message "Convert-CountryToCountryCode - Country $CountryName name not found" } } } } else { $Script:QuickSearchCountries } } else { $AllCultures = [cultureinfo]::GetCultures([System.Globalization.CultureTypes]::SpecificCultures) $Script:QuickSearchCountries = [ordered] @{ # This is for custom countries that are not in the list by their english name. # there also seems to be a difference between PS7 and PS5.1 # Net framework vs NET Core 'Czech Republic' = @{ 'Culture' = [cultureinfo] 'CZ' 'RegionInformation' = [System.Globalization.RegionInfo] 'CZ' } 'Korea, REPUBLIC OF' = @{ 'Culture' = [cultureinfo] 'KR' 'RegionInformation' = [System.Globalization.RegionInfo] 'KR' } 'VIET NAM' = @{ 'Culture' = [cultureinfo] 'VN' 'RegionInformation' = [System.Globalization.RegionInfo] 'VN' } } foreach ($Culture in $AllCultures) { $RegionInformation = [System.Globalization.RegionInfo]::new($Culture) $Script:QuickSearchCountries[$RegionInformation.EnglishName] = @{ 'Culture' = $Culture 'RegionInformation' = $RegionInformation } $Script:QuickSearchCountries[$RegionInformation.DisplayName] = @{ 'Culture' = $Culture 'RegionInformation' = $RegionInformation } $Script:QuickSearchCountries[$RegionInformation.NativeName] = @{ 'Culture' = $Culture 'RegionInformation' = $RegionInformation } $Script:QuickSearchCountries[$RegionInformation.ThreeLetterISORegionName] = @{ 'Culture' = $Culture 'RegionInformation' = $RegionInformation } } if ($CountryName) { if ($All) { $Script:QuickSearchCountries[$CountryName] } else { if ($Script:QuickSearchCountries[$CountryName]) { $Script:QuickSearchCountries[$CountryName].RegionInformation.TwoLetterISORegionName.ToUpper() } else { if ($PSBoundParameters.ErrorAction -eq 'Stop') { throw "Country $CountryName not found" } else { Write-Warning -Message "Convert-CountryToCountryCode - Country $CountryName name not found" } } } } else { $Script:QuickSearchCountries } } } Function Convert-DomainFqdnToNetBIOS { <# .SYNOPSIS Converts FQDN to NetBIOS name for Active Directory Domain .DESCRIPTION Converts FQDN to NetBIOS name for Active Directory Domain .PARAMETER DomainName DomainName for current forest or trusted forest .EXAMPLE Convert-DomainFqdnToNetBIOS -Domain 'ad.evotec.xyz' .EXAMPLE Convert-DomainFqdnToNetBIOS -Domain 'ad.evotec.pl' .NOTES General notes #> [cmdletBinding()] param ( [string] $DomainName ) if (-not $Script:CacheFQDN) { $Script:CacheFQDN = @{} } if ($Script:CacheFQDN[$DomainName]) { $Script:CacheFQDN[$DomainName] } else { $objRootDSE = [System.DirectoryServices.DirectoryEntry] "LDAP://$DomainName/RootDSE" $ConfigurationNC = $objRootDSE.configurationNamingContext $Searcher = [System.DirectoryServices.DirectorySearcher] @{ SearchScope = "subtree" SearchRoot = "LDAP://cn=Partitions,$ConfigurationNC" Filter = "(&(objectcategory=Crossref)(dnsRoot=$DomainName)(netbiosname=*))" } $null = $Searcher.PropertiesToLoad.Add("netbiosname") $Script:CacheFQDN[$DomainName] = ($Searcher.FindOne()).Properties.Item("netbiosname") $Script:CacheFQDN[$DomainName] } } function Convert-DomainToSid { <# .SYNOPSIS Converts Domain Name to SID .DESCRIPTION Converts Domain Name to SID .PARAMETER DomainName DomainName for current forest or trusted forest .EXAMPLE Convert-DomainToSid -DomainName 'test.evotec.pl' .NOTES General notes #> [cmdletBinding()] param( [parameter(Mandatory)][string] $DomainName ) try { $BinarySID = ([ADSI]"LDAP://$DomainName").objectsid $DomainSidValue = [System.Security.Principal.SecurityIdentifier]::new($BinarySID.Value, 0).Value $DomainSidValue } catch { Write-Warning -Message "Convert-DomainToSid - Failed conversion with error $($_.Exception.Message)" } } function Convert-ExchangeEmail { <# .SYNOPSIS Function that helps converting Exchange email address list into readable, exportable format. .DESCRIPTION Function that helps converting Exchange email address list into readable, exportable format. .PARAMETER Emails List of emails as available in Exchange or Exchange Online, otherwise known as proxy addresses list .PARAMETER Separator .PARAMETER RemoveDuplicates .PARAMETER RemovePrefix .PARAMETER AddSeparator .EXAMPLE $Emails = @() $Emails += 'SIP:test@email.com' $Emails += 'SMTP:elo@maiu.com' $Emails += 'sip:elo@maiu.com' $Emails += 'Spo:dfte@sdsd.com' $Emails += 'SPO:myothertest@sco.com' Convert-ExchangeEmail -Emails $Emails -RemovePrefix -RemoveDuplicates -AddSeparator .NOTES General notes #> [CmdletBinding()] param( [string[]] $Emails, [string] $Separator = ', ', [switch] $RemoveDuplicates, [switch] $RemovePrefix, [switch] $AddSeparator ) if ($RemovePrefix) { #$Emails = $Emails.Replace('SMTP:', '').Replace('SIP:', '').Replace('smtp:', '').Replace('sip:', '').Replace('spo:','') $Emails = $Emails -replace 'smtp:', '' -replace 'sip:', '' -replace 'spo:', '' } 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 } } function Convert-ExchangeRecipient { <# .SYNOPSIS Convert msExchRemoteRecipientType, msExchRecipientDisplayType, msExchRecipientTypeDetails to their respective name .DESCRIPTION Convert msExchRemoteRecipientType, msExchRecipientDisplayType, msExchRecipientTypeDetails to their respective name .PARAMETER RecipientTypeDetails RecipientTypeDetails to convert .PARAMETER RecipientType RecipientType to convert .PARAMETER RemoteRecipientType Parameter description .EXAMPLE $Users = Get-ADUser -Filter * -Properties Mail, ProxyAddresses, msExchRemoteRecipientType, msExchRecipientDisplayType, msExchRecipientTypeDetails, MailNickName $UsersModified = foreach ($User in $Users) { [PSCUstomObject] @{ Name = $User.Name Mail = $User.Mail MailNickName = $User.MailNickName msExchRemoteRecipientType = Convert-ExchangeRecipient -msExchRemoteRecipientType $User.msExchRemoteRecipientType msExchRecipientDisplayType = Convert-ExchangeRecipient -msExchRecipientDisplayType $User.msExchRecipientDisplayType msExchRecipientTypeDetails = Convert-ExchangeRecipient -msExchRecipientTypeDetails $User.msExchRecipientTypeDetails ProxyAddresses = Convert-ExchangeEmail -AddSeparator -RemovePrefix -RemoveDuplicates -Separator ',' -Emails $User.ProxyAddresses } } $UsersModified | Out-HtmlView -Filtering -ScrollX .EXAMPLE Convert-ExchangeRecipient -msExchRemoteRecipientType 17 Convert-ExchangeRecipient -msExchRecipientDisplayType 17 Convert-ExchangeRecipient -msExchRecipientTypeDetails 17 .NOTES Based on: - https://granikos.eu/exchange-recipient-type-values/ - https://answers.microsoft.com/en-us/msoffice/forum/all/recipient-type-values/7c2620e5-9870-48ba-b5c2-7772c739c651 - https://www.undocumented-features.com/2020/05/06/every-last-msexchrecipientdisplaytype-and-msexchrecipienttypedetails-value/ #> [alias('Convert-ExchangeRecipientDetails')] [cmdletbinding(DefaultParameterSetName = 'msExchRecipientTypeDetails')] param( [parameter(ParameterSetName = 'msExchRecipientTypeDetails')][alias('RecipientTypeDetails')][string] $msExchRecipientTypeDetails, [parameter(ParameterSetName = 'msExchRecipientDisplayType')][alias('RecipientType')][string] $msExchRecipientDisplayType, [parameter(ParameterSetName = 'msExchRemoteRecipientType')][alias('RemoteRecipientType')][string] $msExchRemoteRecipientType, [parameter(ParameterSetName = 'msExchRecipientTypeDetails')] [parameter(ParameterSetName = 'msExchRecipientDisplayType')] [parameter(ParameterSetName = 'msExchRemoteRecipientType')] [switch] $All ) if ($PSBoundParameters.ContainsKey('msExchRecipientTypeDetails')) { $ListMsExchRecipientTypeDetails = [ordered] @{ '0' = 'None' '1' = 'UserMailbox' '2' = 'LinkedMailbox' '4' = 'SharedMailbox' '8' = 'LegacyMailbox' '16' = 'RoomMailbox' '32' = 'EquipmentMailbox' '64' = 'MailContact' '128' = 'MailUser' '256' = 'MailUniversalDistributionGroup' '512' = 'MailNonUniversalGroup' '1024' = 'MailUniversalSecurityGroup' '2048' = 'DynamicDistributionGroup' '4096' = 'PublicFolder' '8192' = 'SystemAttendantMailbox' '16384' = 'SystemMailbox' '32768' = 'MailForestContact' '65536' = 'User' '131072' = 'Contact' '262144' = 'UniversalDistributionGroup' '524288' = 'UniversalSecurityGroup' '1048576' = 'NonUniversalGroup' '2097152' = 'Disable User' '4194304' = 'MicrosoftExchange' '8388608' = 'ArbitrationMailbox' '16777216' = 'MailboxPlan' '33554432' = 'LinkedUser' '268435456' = 'RoomList' '536870912' = 'DiscoveryMailbox' '1073741824' = 'RoleGroup' '2147483648' = 'RemoteUserMailbox' '4294967296' = 'Computer' '8589934592' = 'RemoteRoomMailbox' '17179869184' = 'RemoteEquipmentMailbox' '34359738368' = 'RemoteSharedMailbox' '68719476736' = 'PublicFolderMailbox' '137438953472' = 'Team Mailbox' '274877906944' = 'RemoteTeamMailbox' '549755813888' = 'MonitoringMailbox' '1099511627776' = 'GroupMailbox' '2199023255552' = 'LinkedRoomMailbox' '4398046511104' = 'AuditLogMailbox' '8796093022208' = 'RemoteGroupMailbox' '17592186044416' = 'SchedulingMailbox' '35184372088832' = 'GuestMailUser' '70368744177664' = 'AuxAuditLogMailbox' '140737488355328' = 'SupervisoryReviewPolicyMailbox' } if ($All) { $ListMsExchRecipientTypeDetails } else { if ($null -ne $ListMsExchRecipientTypeDetails[$msExchRecipientTypeDetails]) { $ListMsExchRecipientTypeDetails[$msExchRecipientTypeDetails] } else { $msExchRecipientTypeDetails } } } elseif ($PSBoundParameters.ContainsKey('msExchRecipientDisplayType')) { $ListMsExchRecipientDisplayType = [ordered] @{ '0' = 'MailboxUser' '1' = 'DistributionGroup' '2' = 'PublicFolder' '3' = 'DynamicDistributionGroup' '4' = 'Organization' '5' = 'PrivateDistributionList' '6' = 'RemoteMailUser' '7' = 'ConferenceRoomMailbox' '8' = 'EquipmentMailbox' '10' = 'ArbitrationMailbox' '11' = 'MailboxPlan' '12' = 'LinkedUser' '15' = 'RoomList' '17' = 'Microsoft365Group' # AT LEAST IT SEEMS SO '-2147483642' = 'SyncedMailboxUser' '-2147483391' = 'SyncedUDGasUDG' '-2147483386' = 'SyncedUDGasContact' '-2147483130' = 'SyncedPublicFolder' '-2147482874' = 'SyncedDynamicDistributionGroup' '-2147482106' = 'SyncedRemoteMailUser' '-2147481850' = 'SyncedConferenceRoomMailbox' '-2147481594' = 'SyncedEquipmentMailbox' '-2147481343' = 'SyncedUSGasUDG' '-2147481338' = 'SyncedUSGasContact' '-1073741818' = 'ACLableSyncedMailboxUser' '-1073740282' = 'ACLableSyncedRemoteMailUser' '-1073739514' = 'ACLableSyncedUSGasContact' '-1073739511' = 'SyncedUSGasUSG' '1043741833' = 'SecurityDistributionGroup' '1073739511' = 'SyncedUSGasUSG' '1073739514' = 'ACLableSyncedUSGasContact' '1073741824' = 'ACLableMailboxUser' # 'RBAC Role Group' '1073741830' = 'ACLableRemoteMailUser' } if ($All) { $ListMsExchRecipientDisplayType } else { if ($null -ne $ListMsExchRecipientDisplayType[$msExchRecipientDisplayType]) { $ListMsExchRecipientDisplayType[$msExchRecipientDisplayType] } else { $msExchRecipientDisplayType } } } elseif ($PSBoundParameters.ContainsKey('msExchRemoteRecipientType')) { $ListMsExchRemoteRecipientType = [ordered] @{ # RemoteRecipientType '1' = 'ProvisionMailbox' '2' = 'ProvisionArchive (On-Prem Mailbox)' '3' = 'ProvisionMailbox, ProvisionArchive' '4' = 'Migrated (UserMailbox)' '6' = 'ProvisionArchive, Migrated' '8' = 'DeprovisionMailbox' '10' = 'ProvisionArchive, DeprovisionMailbox' '16' = 'DeprovisionArchive (On-Prem Mailbox)' '17' = 'ProvisionMailbox, DeprovisionArchive' '20' = 'Migrated, DeprovisionArchive' '24' = 'DeprovisionMailbox, DeprovisionArchive' '33' = 'ProvisionMailbox, RoomMailbox' '35' = 'ProvisionMailbox, ProvisionArchive, RoomMailbox' '36' = 'Migrated, RoomMailbox' '38' = 'ProvisionArchive, Migrated, RoomMailbox' '49' = 'ProvisionMailbox, DeprovisionArchive, RoomMailbox' '52' = 'Migrated, DeprovisionArchive, RoomMailbox' '65' = 'ProvisionMailbox, EquipmentMailbox' '67' = 'ProvisionMailbox, ProvisionArchive, EquipmentMailbox' '68' = 'Migrated, EquipmentMailbox' '70' = 'ProvisionArchive, Migrated, EquipmentMailbox' '81' = 'ProvisionMailbox, DeprovisionArchive, EquipmentMailbox' '84' = 'Migrated, DeprovisionArchive, EquipmentMailbox' '100' = 'Migrated, SharedMailbox' '102' = 'ProvisionArchive, Migrated, SharedMailbox' '116' = 'Migrated, DeprovisionArchive, SharedMailbox' } if ($All) { $ListMsExchRemoteRecipientType } else { if ($null -ne $ListMsExchRemoteRecipientType[$msExchRemoteRecipientType]) { $ListMsExchRemoteRecipientType[$msExchRemoteRecipientType] } else { $msExchRemoteRecipientType } } } } # Add-Type @' # using System; # [Flags] # public enum RemoteRecipientType # { # Mailbox = 0x1, # ProvisionArchive = 0x2, # Migrated = 0x4, # DeprovisionMailbox = 0x8, # DeprovisionArchive = 0x10, # RoomMailbox = 0x20, # EquipmentMailbox = 0x40, # //SharedMailbox = RoomMailbox | EquipmentMailbox # } # public enum RecipientType # { # SharedMailbox = 0x0, # MailUniversalDistributionGroup = 0x1, # MailContact = 0x6, # RoomMailbox = 0x7, # EquipmentMailbox = 0x8, # ACLableMailboxUser = 1073741824, # MailUniversalSecurityGroup = 1073741833, # SyncedMailboxUser = -2147483642, # SyncedUDGasUDG = -2147483391, # SyncedUDGasContact = -2147483386, # SyncedPublicFolder = -2147483130, # SyncedDynamicDistributionGroup = -2147482874, # SyncedRemoteMailUser = -2147482106, # SyncedConferenceRoomMailbox = -2147481850, # SyncedEquipmentMailbox = -2147481594, # SyncedUSGasUDG = -2147481343, # SyncedUSGasContact = -2147481338, # ACLableSyncedMailboxUser = -1073741818, # ACLableSyncedRemoteMailUser = -1073740282, # ACLableSyncedUSGasContact = -1073739514, # SyncedUSGasUSG = -1073739511 # } # public enum RecipientTypeDetails: long # { # UserMailbox = 0x1, # LinkedMailbox = 0x2, # SharedMailbox = 0x4, # RoomMailbox = 0x10, # EquipmentMailbox = 0x20, # MailUser = 0x80, # RemoteUserMailbox = 2147483648, //hex does not work here # RemoteRoomMailbox = 8589934592, # RemoteEquipmentMailbox = 17179869184, # RemoteSharedMailbox = 34359738368 # } # '@ <# 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) } } function ConvertFrom-Color { [alias('Convert-FromColor')] [CmdletBinding()] param ( [ValidateScript( { if ($($_ -in $Script:RGBColors.Keys -or $_ -match "^#([A-Fa-f0-9]{6})$" -or $_ -eq "") -eq $false) { throw "The Input value is not a valid colorname nor an valid color hex code." } else { $true } })] [alias('Colors')][string[]] $Color, [switch] $AsDecimal, [switch] $AsDrawingColor ) $Colors = foreach ($C in $Color) { $Value = $Script:RGBColors."$C" if ($C -match "^#([A-Fa-f0-9]{6})$") { $C continue } if ($null -eq $Value) { continue } $HexValue = Convert-Color -RGB $Value Write-Verbose "Convert-FromColor - Color Name: $C Value: $Value HexValue: $HexValue" if ($AsDecimal) { [Convert]::ToInt64($HexValue, 16) } elseif ($AsDrawingColor) { [System.Drawing.Color]::FromArgb("#$($HexValue)") } else { "#$($HexValue)" } } $Colors } $ScriptBlockColors = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $Script:RGBColors.Keys | Where-Object { $_ -like "$wordToComplete*" } } Register-ArgumentCompleter -CommandName ConvertFrom-Color -ParameterName Color -ScriptBlock $ScriptBlockColors function Convert-HexToBinary { [CmdletBinding()] param( [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] [string] $Hex ) $return = for ($i = 0; $i -lt $Hex.Length ; $i += 2) { [Byte]::Parse($Hex.Substring($i, 2), [System.Globalization.NumberStyles]::HexNumber) } Write-Output $return -NoEnumerate } function Convert-Identity { <# .SYNOPSIS Small command that tries to resolve any given object .DESCRIPTION Small command that tries to resolve any given object - be it SID, DN, FSP or Netbiosname .PARAMETER Identity Type to resolve in form of Identity, DN, SID .PARAMETER SID Allows to pass SID directly, rather then going thru verification process .PARAMETER Name Allows to pass Name directly, rather then going thru verification process .PARAMETER Force Allows to clear cache, useful when you want to force refresh .EXAMPLE $Identity = @( 'S-1-5-4' 'S-1-5-4' 'S-1-5-11' 'S-1-5-32-549' 'S-1-5-32-550' 'S-1-5-32-548' 'S-1-5-64-10' 'S-1-5-64-14' 'S-1-5-64-21' 'S-1-5-18' 'S-1-5-19' 'S-1-5-32-544' 'S-1-5-20-20-10-51' # Wrong SID 'S-1-5-21-853615985-2870445339-3163598659-512' 'S-1-5-21-3661168273-3802070955-2987026695-512' 'S-1-5-21-1928204107-2710010574-1926425344-512' 'CN=Test Test 2,OU=Users,OU=Production,DC=ad,DC=evotec,DC=pl' 'Test Local Group' 'przemyslaw.klys@evotec.pl' 'test2' 'NT AUTHORITY\NETWORK' 'NT AUTHORITY\SYSTEM' 'S-1-5-21-853615985-2870445339-3163598659-519' 'TEST\some' 'EVOTECPL\Domain Admins' 'NT AUTHORITY\INTERACTIVE' 'INTERACTIVE' 'EVOTEC\Domain Admins' 'EVOTECPL\Domain Admins' 'Test\Domain Admins' 'CN=S-1-5-21-1928204107-2710010574-1926425344-512,CN=ForeignSecurityPrincipals,DC=ad,DC=evotec,DC=xyz' # Valid 'CN=S-1-5-21-1928204107-2710010574-512,CN=ForeignSecurityPrincipals,DC=ad,DC=evotec,DC=xyz' # not valid 'CN=S-1-5-21-1928204107-2710010574-1926425344-512,CN=ForeignSecurityPrincipals,DC=ad,DC=evotec,DC=xyz' # cached ) $TestOutput = Convert-Identity -Identity $Identity -Verbose Output: Name SID DomainName Type Error ---- --- ---------- ---- ----- NT AUTHORITY\INTERACTIVE S-1-5-4 WellKnownGroup NT AUTHORITY\INTERACTIVE S-1-5-4 WellKnownGroup NT AUTHORITY\Authenticated Users S-1-5-11 WellKnownGroup BUILTIN\Server Operators S-1-5-32-549 WellKnownGroup BUILTIN\Print Operators S-1-5-32-550 WellKnownGroup BUILTIN\Account Operators S-1-5-32-548 WellKnownGroup NT AUTHORITY\NTLM Authentication S-1-5-64-10 WellKnownGroup NT AUTHORITY\SChannel Authentication S-1-5-64-14 WellKnownGroup NT AUTHORITY\Digest Authentication S-1-5-64-21 WellKnownGroup NT AUTHORITY\SYSTEM S-1-5-18 WellKnownAdministrative NT AUTHORITY\NETWORK SERVICE S-1-5-19 WellKnownGroup BUILTIN\Administrators S-1-5-32-544 WellKnownAdministrative S-1-5-20-20-10-51 S-1-5-20-20-10-51 Unknown Exception calling "Translate" with "1" argument(s): "Some or all identity references could not be translated." EVOTEC\Domain Admins S-1-5-21-853615985-2870445339-3163598659-512 ad.evotec.xyz Administrative EVOTECPL\Domain Admins S-1-5-21-3661168273-3802070955-2987026695-512 ad.evotec.pl Administrative TEST\Domain Admins S-1-5-21-1928204107-2710010574-1926425344-512 test.evotec.pl Administrative EVOTECPL\TestingAD S-1-5-21-3661168273-3802070955-2987026695-1111 ad.evotec.pl NotAdministrative EVOTEC\Test Local Group S-1-5-21-853615985-2870445339-3163598659-3610 ad.evotec.xyz NotAdministrative EVOTEC\przemyslaw.klys S-1-5-21-853615985-2870445339-3163598659-1105 ad.evotec.xyz NotAdministrative test2 Unknown Exception calling "Translate" with "1" argument(s): "Some or all identity references could not be translated." NT AUTHORITY\NETWORK S-1-5-2 WellKnownGroup NT AUTHORITY\SYSTEM S-1-5-18 WellKnownAdministrative EVOTEC\Enterprise Admins S-1-5-21-853615985-2870445339-3163598659-519 ad.evotec.xyz Administrative TEST\some S-1-5-21-1928204107-2710010574-1926425344-1106 test.evotec.pl NotAdministrative EVOTECPL\Domain Admins S-1-5-21-3661168273-3802070955-2987026695-512 ad.evotec.pl Administrative NT AUTHORITY\INTERACTIVE S-1-5-4 WellKnownGroup NT AUTHORITY\INTERACTIVE S-1-5-4 WellKnownGroup EVOTEC\Domain Admins S-1-5-21-853615985-2870445339-3163598659-512 ad.evotec.xyz Administrative EVOTECPL\Domain Admins S-1-5-21-3661168273-3802070955-2987026695-512 ad.evotec.pl Administrative TEST\Domain Admins S-1-5-21-1928204107-2710010574-1926425344-512 test.evotec.pl Administrative TEST\Domain Admins S-1-5-21-1928204107-2710010574-1926425344-512 test.evotec.pl Administrative S-1-5-21-1928204107-2710010574-512 S-1-5-21-1928204107-2710010574-512 Unknown Exception calling "Translate" with "1" argument(s): "Some or all identity references could not be translated." TEST\Domain Admins S-1-5-21-1928204107-2710010574-1926425344-512 test.evotec.pl Administrative .NOTES General notes #> [cmdletBinding(DefaultParameterSetName = 'Identity')] param( [parameter(ParameterSetName = 'Identity', Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)][string[]] $Identity, [parameter(ParameterSetName = 'SID', Mandatory)][System.Security.Principal.SecurityIdentifier[]] $SID, [parameter(ParameterSetName = 'Name', Mandatory)][string[]] $Name, [switch] $Force ) Begin { # [System.Security.Principal.WellKnownSidType]::BuiltinAccountOperatorsSid if (-not $Script:GlobalCacheSidConvert -or $Force) { $Script:GlobalCacheSidConvert = @{ # We probably don't need to build it up because4 we will be able to find it, but sometimes some of them are not available 'NT AUTHORITY\SYSTEM' = [PSCustomObject] @{ Name = 'BUILTIN\Administrators' SID = 'S-1-5-18' DomainName = '' Type = 'WellKnownAdministrative' Error = '' } # 'NT AUTHORITY\NETWORK SERVICE' = [PSCustomObject] @{ # Name = 'NT AUTHORITY\NETWORK SERVICE' # SID = 'S-1-5-20' # or 'S-1-5-19' # DomainName = '' # Type = 'WellKnownAdministrative' # Error = '' #} 'BUILTIN\Administrators' = [PSCustomObject] @{ Name = 'BUILTIN\Administrators' SID = 'S-1-5-32-544' DomainName = '' Type = 'WellKnownAdministrative' Error = '' } 'BUILTIN\Users' = [PSCustomObject] @{ Name = 'BUILTIN\Users' SID = 'S-1-5-32-545' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Guests' = [PSCustomObject] @{ Name = 'BUILTIN\Guests' SID = 'S-1-5-32-546' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Power Users' = [PSCustomObject] @{ Name = 'BUILTIN\Power Users' SID = 'S-1-5-32-547' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Account Operators' = [PSCustomObject] @{ Name = 'BUILTIN\Account Operators' SID = 'S-1-5-32-548' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Server Operators' = [PSCustomObject] @{ Name = 'BUILTIN\Server Operators' SID = 'S-1-5-32-549' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Print Operators' = [PSCustomObject] @{ Name = 'BUILTIN\Print Operators' SID = 'S-1-5-32-550' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Backup Operators' = [PSCustomObject] @{ Name = 'BUILTIN\Backup Operators' SID = 'S-1-5-32-551' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Replicator' = [PSCustomObject] @{ Name = 'BUILTIN\Replicators' SID = 'S-1-5-32-552' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Pre-Windows 2000 Compatible Access' = [PSCustomObject] @{ Name = 'BUILTIN\Pre-Windows 2000 Compatible Access' SID = 'S-1-5-32-554' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Remote Desktop Users' = [PSCustomObject] @{ Name = 'BUILTIN\Remote Desktop Users' SID = 'S-1-5-32-555' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Network Configuration Operators' = [PSCustomObject] @{ Name = 'BUILTIN\Network Configuration Operators' SID = 'S-1-5-32-556' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Incoming Forest Trust Builders' = [PSCustomObject] @{ Name = 'BUILTIN\Incoming Forest Trust Builders' SID = 'S-1-5-32-557' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Performance Monitor Users' = [PSCustomObject] @{ Name = 'BUILTIN\Performance Monitor Users' SID = 'S-1-5-32-558' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Performance Log Users' = [PSCustomObject] @{ Name = 'BUILTIN\Performance Log Users' SID = 'S-1-5-32-559' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Windows Authorization Access Group' = [PSCustomObject] @{ Name = 'BUILTIN\Windows Authorization Access Group' SID = 'S-1-5-32-560' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Terminal Server License Servers' = [PSCustomObject] @{ Name = 'BUILTIN\Terminal Server License Servers' SID = 'S-1-5-32-561' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Distributed COM Users' = [PSCustomObject] @{ Name = 'BUILTIN\Distributed COM Users' SID = 'S-1-5-32-562' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\IIS_IUSRS' = [PSCustomObject] @{ Name = 'BUILTIN\IIS_IUSRS' SID = 'S-1-5-32-568' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Cryptographic Operators' = [PSCustomObject] @{ Name = 'BUILTIN\Cryptographic Operators' SID = 'S-1-5-32-569' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Event Log Readers' = [PSCustomObject] @{ Name = 'BUILTIN\Event Log Readers' SID = 'S-1-5-32-573' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Certificate Service DCOM Access' = [PSCustomObject] @{ Name = 'BUILTIN\Certificate Service DCOM Access' SID = 'S-1-5-32-574' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\RDS Remote Access Servers' = [PSCustomObject] @{ Name = 'BUILTIN\RDS Remote Access Servers' SID = 'S-1-5-32-575' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\RDS Endpoint Servers' = [PSCustomObject] @{ Name = 'BUILTIN\RDS Endpoint Servers' SID = 'S-1-5-32-576' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\RDS Management Servers' = [PSCustomObject] @{ Name = 'BUILTIN\RDS Management Servers' SID = 'S-1-5-32-577' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Hyper-V Administrators' = [PSCustomObject] @{ Name = 'BUILTIN\Hyper-V Administrators' SID = 'S-1-5-32-578' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Access Control Assistance Operators' = [PSCustomObject] @{ Name = 'BUILTIN\Access Control Assistance Operators' SID = 'S-1-5-32-579' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'BUILTIN\Remote Management Users' = [PSCustomObject] @{ Name = 'BUILTIN\Remote Management Users' SID = 'S-1-5-32-580' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'Window Manager\Window Manager Group' = [PSCustomObject] @{ Name = 'Window Manager\Window Manager Group' SID = 'S-1-5-90-0' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'NT SERVICE\WdiServiceHost' = [PSCustomObject] @{ Name = 'NT SERVICE\WdiServiceHost' SID = 'S-1-5-80-3139157870-2983391045-3678747466-658725712-1809340420' DomainName = '' Type = 'WellKnownGroup' Error = '' } } } } Process { if ($Identity) { foreach ($Ident in $Identity) { $MatchRegex = [Regex]::Matches($Ident, "S-\d-\d+-(\d+-|){1,14}\d+") if ($Script:GlobalCacheSidConvert[$Ident]) { # If we cached it, lets return it right away Write-Verbose "Convert-Identity - Processing $Ident (Cache)" $Script:GlobalCacheSidConvert[$Ident] } elseif ($MatchRegex.Success) { # regex check if SID .. do something #$MatchRegex = [Regex]::IsMatch($Ident, "S-\d-\d+-(\d+-){1,14}\d+") #$MatchRegex = [Regex]::Matches($Ident, "S-\d-\d+-(\d+-){1,4}\d+") Write-Verbose "Convert-Identity - Processing $Ident (SID)" if ($MatchRegex.Value -ne $Ident) { $Script:GlobalCacheSidConvert[$Ident] = ConvertFrom-SID -SID $MatchRegex.Value } else { $Script:GlobalCacheSidConvert[$Ident] = ConvertFrom-SID -SID $Ident } $Script:GlobalCacheSidConvert[$Ident] } elseif ($Ident -like '*DC=*') { # check if DN Write-Verbose "Convert-Identity - Processing $Ident (DistinguishedName)" try { $Object = [adsi]"LDAP://$($Ident)" $SIDValue = [System.Security.Principal.SecurityIdentifier]::new($Object.objectSid.Value, 0).Value $Script:GlobalCacheSidConvert[$Ident] = ConvertFrom-SID -SID $SIDValue } catch { $Script:GlobalCacheSidConvert[$Ident] = [PSCustomObject] @{ Name = $Ident SID = $null DomainName = '' Type = 'Unknown' Error = $_.Exception.Message -replace [environment]::NewLine, ' ' } } $Script:GlobalCacheSidConvert[$Ident] } else { # Other types Write-Verbose "Convert-Identity - Processing $Ident (Other)" try { $SIDValue = ([System.Security.Principal.NTAccount] $Ident).Translate([System.Security.Principal.SecurityIdentifier]).Value $Script:GlobalCacheSidConvert[$Ident] = ConvertFrom-SID -SID $SIDValue } catch { $Script:GlobalCacheSidConvert[$Ident] = [PSCustomObject] @{ Name = $Ident SID = $null DomainName = '' Type = 'Unknown' Error = $_.Exception.Message -replace [environment]::NewLine, ' ' } } $Script:GlobalCacheSidConvert[$Ident] } } } else { if ($SID) { foreach ($S in $SID) { if ($Script:GlobalCacheSidConvert[$S]) { $Script:GlobalCacheSidConvert[$S] } else { $Script:GlobalCacheSidConvert[$S] = ConvertFrom-SID -SID $S $Script:GlobalCacheSidConvert[$S] } } } else { foreach ($Ident in $Name) { if ($Script:GlobalCacheSidConvert[$Ident]) { $Script:GlobalCacheSidConvert[$Ident] } else { $Script:GlobalCacheSidConvert[$Ident] = ([System.Security.Principal.NTAccount] $Ident).Translate([System.Security.Principal.SecurityIdentifier]).Value $Script:GlobalCacheSidConvert[$Ident] } } } } } End {} } 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-Office365License { <# .SYNOPSIS This function helps converting Office 365 licenses from/to their SKU equivalent .DESCRIPTION This function helps converting Office 365 licenses from/to their SKU equivalent .PARAMETER License License SKU or License Name. Takes multiple values. .PARAMETER ToSku Converts license name to SKU .PARAMETER Separator .PARAMETER ReturnArray .EXAMPLE Convert-Office365License -License 'VISIOCLIENT','PROJECTONLINE_PLAN_1','test','tenant:VISIOCLIENT' .EXAMPLE Convert-Office365License -License "Office 365 (Plan A3) for Faculty","Office 365 (Enterprise Preview)", 'test' -ToSku #> [CmdletBinding()] param( [Parameter(Position = 0, ValueFromPipeline)][Array] $License, [alias('SKU')][switch] $ToSku, [string] $Separator = ', ', [switch] $ReturnArray ) Begin { $O365SKU = @{ "ADALLOM_S_STANDALONE" = "Microsoft Defender for Cloud Apps" "RMS_S_PREMIUM2" = "Azure Information Protection Premium P2" "RMS_S_PREMIUM" = "Azure Information Protection Premium P1" "MFA_PREMIUM" = "Microsoft Azure Multi-Factor Authentication" "MTP" = "Microsoft 365 Defender" "SAFEDOCS" = "Office 365 SafeDocs" "WINDEFATP" = "Microsoft Defender for Endpoint" "DYN365_CDS_VIRAL" = "Common Data Service" "FLOW_P2_VIRAL" = "Flow Free" "VIVAENGAGE_CORE" = "Viva Engage Core" "VIVA_LEARNING_SEEDED" = "Viva Learning Seeded" "POWER_VIRTUAL_AGENTS_O365_P2" = "Power Virtual Agents for Office 365" "CDS_O365_P2" = "Common Data Service for Teams" "PROJECT_O365_P2" = "Project for Office (Plan E3)" "DYN365_CDS_O365_P2" = "Common Data Service" "MICROSOFTBOOKINGS" = "Microsoft Bookings" "KAIZALA_O365_P3" = "Microsoft Kaizala Pro" "WHITEBOARD_PLAN2" = "Whiteboard (Plan 2)" "MIP_S_CLP1" = "Information Protection for Office 365 - Standard" "MYANALYTICS_P2" = "Insights by MyAnalytics" "BPOS_S_TODO_2" = "To-Do (Plan 2)" "FORMS_PLAN_E3" = "Microsoft Forms (Plan E3)" "STREAM_O365_E3" = "Microsoft Stream for Office 365 E3" "Deskless" = "Microsoft StaffHub" "FLOW_O365_P2" = "Power Automate for Office 365" "POWERAPPS_O365_P2" = "Power Apps for Office 365" "PROJECTWORKMANAGEMENT" = "Microsoft Planner" "SWAY" = "Sway" "RMS_S_ENTERPRISE" = "Azure Rights Management" "BI_AZURE_P0" = "Power BI (free)" "BI_AZURE_P2" = "Power BI Pro" "AAD_BASIC" = "Azure Active Directory Basic" "AAD_PREMIUM" = "Azure Active Directory Premium P1" "AAD_PREMIUM_P2" = "Azure Active Directory Premium P2" "ADALLOM_O365" = "Office 365 Cloud App Security" "ADALLOM_STANDALONE" = "Microsoft Cloud App Security" "ADV_COMMS" = "Advanced Communications" "ATA" = "Microsoft Defender for Identity" "ATP_ENTERPRISE" = "Microsoft Defender for Office 365 (Plan 1)" "ATP_ENTERPRISE_FACULTY" = "Microsoft Defender for Office 365 (Plan 1) Faculty" "AX7_USER_TRIAL" = "Microsoft Dynamics AX7 User Trial" "BI_AZURE_P1" = "Power BI Reporting and Analytics" "BUSINESS_VOICE_DIRECTROUTING" = "Microsoft 365 Business Voice (without calling plan)" "BUSINESS_VOICE_DIRECTROUTING_MED" = "Microsoft 365 Business Voice (without Calling Plan) for US" "BUSINESS_VOICE_MED2_TELCO" = "Microsoft 365 Business Voice (US)" "CCIBOTS_PRIVPREV_VIRAL" = "Power Virtual Agents Viral Trial" "CDS_DB_CAPACITY" = "Common Data Service Database Capacity" "CDS_LOG_CAPACITY" = "Common Data Service Log Capacity" "CDSAICAPACITY" = "AI Builder Capacity add-on" "CPC_B_2C_4RAM_64GB" = "Windows 365 Business 2 vCPU 4 GB 64 GB" "CPC_B_2C_4RAM_64GB_WHB" = "Windows 365 Business 2 vCPU, 4 GB, 64 GB (with Windows Hybrid Benefit)" "CPC_B_4C_16RAM_128GB_WHB" = "Windows 365 Business 4 vCPU 16 GB 128 GB (with Windows Hybrid Benefit)" "CPC_E_2C_4GB_64GB" = "Windows 365 Enterprise 2 vCPU 4 GB 64 GB" "CRM_ONLINE_PORTAL" = "Dynamics 365 Enterprise Edition - Additional Portal (Qualified Offer)" "CRMINSTANCE" = "Dynamics 365 - Additional Production Instance (Qualified Offer)" "CRMIUR" = "CMRIUR" "CRMPLAN2" = "Dynamics CRM Online Plan 2" "CRMSTANDARD" = "Microsoft Dynamics CRM Online Professional" "CRMSTORAGE" = "Dynamics 365 - Additional Database Storage (Qualified Offer)" "CRMTESTINSTANCE" = "Dynamics 365 - Additional Non-Production Instance (Qualified Offer)" "D365_SALES_ENT_ATTACH" = "Dynamics 365 Sales Enterprise Attach to Qualifying Dynamics 365 Base Offer" "DEFENDER_ENDPOINT_P1" = "Microsoft Defender for Endpoint P1" "DESKLESSPACK" = "OFFICE 365 F3" "DESKLESSPACK_GOV" = "Microsoft Office 365 (Plan F1) for Government" "DESKLESSPACK_YAMMER" = "Office 365 Enterprise F1 with Yammer" "DESKLESSWOFFPACK" = "Office 365 (Plan F2)" "DEVELOPERPACK" = "OFFICE 365 E3 DEVELOPER" "DYN365_AI_SERVICE_INSIGHTS" = "Dynamics 365 Customer Service Insights Trial" "DYN365_ASSETMANAGEMENT" = "Dynamics 365 Asset Management Addl Assets" "DYN365_BUSCENTRAL_ADD_ENV_ADDON" = "Dynamics 365 Business Central Additional Environment Addon" "DYN365_BUSCENTRAL_DB_CAPACITY" = "Dynamics 365 Business Central Database Capacity" "DYN365_BUSCENTRAL_ESSENTIAL" = "Dynamics 365 Business Central Essentials" "DYN365_BUSCENTRAL_PREMIUM" = "Dynamics 365 Business Central Premium" "DYN365_CUSTOMER_SERVICE_PRO" = "Dynamics 365 Customer Service Professional" "DYN365_CUSTOMER_VOICE_ADDON" = "Dynamics 365 Customer Voice Additional Responses" "DYN365_ENTERPRISE_CUSTOMER_SERVICE" = "DYNAMICS 365 FOR CUSTOMER SERVICE ENTERPRISE EDITION" "DYN365_ENTERPRISE_P1_IW" = "Dynamics 365 P1 Trial for Information Workers" "DYN365_ENTERPRISE_PLAN1" = "Dynamics 365 Customer Engagement Plan" "DYN365_ENTERPRISE_SALES" = "Dynamics 365 for Enterprise Sales Edition" "DYN365_ENTERPRISE_SALES_CUSTOMERSERVICE" = "DYNAMICS 365 FOR SALES AND CUSTOMER SERVICE ENTERPRISE EDITION" "DYN365_ENTERPRISE_TEAM_MEMBERS" = "Dynamics 365 For Team Members Enterprise Edition" "DYN365_FINANCE" = "Dynamics 365 Finance" "DYN365_FINANCIALS_ACCOUNTANT_SKU" = "Dynamics 365 Business Central External Accountant" "DYN365_FINANCIALS_BUSINESS_SKU" = "Dynamics 365 for Financials Business Edition" "DYN365_FINANCIALS_TEAM_MEMBERS_SKU" = "Dynamics 365 for Team Members Business Edition" "DYN365_IOT_INTELLIGENCE_ADDL_MACHINES" = "Sensor Data Intelligence Additional Machines Add-in for Dynamics 365 Supply Chain Management" "DYN365_IOT_INTELLIGENCE_SCENARIO" = "Sensor Data Intelligence Scenario Add-in for Dynamics 365 Supply Chain Management" "DYN365_SCM" = "DYNAMICS 365 FOR SUPPLY CHAIN MANAGEMENT" "DYN365_TEAM_MEMBERS" = "DYNAMICS 365 TEAM MEMBERS" "Dynamics_365_for_Operations" = "DYNAMICS 365 UNF OPS PLAN ENT EDITION" "Dynamics_365_for_Operations_Devices" = "Dynamics 365 Operations - Device" "Dynamics_365_for_Operations_Sandbox_Tier2_SKU" = "Dynamics 365 Operations - Sandbox Tier 2:Standard Acceptance Testing" "Dynamics_365_for_Operations_Sandbox_Tier4_SKU" = "Dynamics 365 Operations - Sandbox Tier 4:Standard Performance Testing" "Dynamics_365_Onboarding_SKU" = "Dynamics 365 Talent: Onboard" "ECAL_SERVICES" = "ECAL" "EMS" = "Enterprise Mobility + Security E3" "EMS_GOV" = "Enterprise Mobility + Security G3 GCC" "EMSPREMIUM" = "Enterprise Mobility + Security E5" "EMSPREMIUM_GOV" = "Enterprise Mobility + Security G5 GCC" "ENTERPRISEPACK" = "Office 365 E3" "ENTERPRISEPACK_B_PILOT" = "Office 365 (Enterprise Preview)" "ENTERPRISEPACK_FACULTY" = "Office 365 (Plan A3) for Faculty" "ENTERPRISEPACK_GOV" = "Microsoft Office 365 (Plan G3) for Government" "ENTERPRISEPACK_STUDENT" = "Office 365 (Plan A3) for Students" "ENTERPRISEPACK_USGOV_DOD" = "Office 365 E3_USGOV_DOD" "ENTERPRISEPACK_USGOV_GCCHIGH" = "Office 365 E3_USGOV_GCCHIGH" "ENTERPRISEPACKLRG" = "Office 365 Enterprise E3" "ENTERPRISEPACKPLUS_FACULTY" = "Office 365 A3 for faculty" "ENTERPRISEPACKPLUS_STUDENT" = "Office 365 A3 for students" "ENTERPRISEPACKWITHOUTPROPLUS" = "Office 365 Enterprise E3 without ProPlus Add-on" "ENTERPRISEPREMIUM" = "Office 365 E5" "ENTERPRISEPREMIUM_FACULTY" = "Office 365 A5 for faculty" "ENTERPRISEPREMIUM_GOV" = "Office 365 G5 GCC" "ENTERPRISEPREMIUM_NOPSTNCONF" = "Office 365 E5 (without Audio Conferencing)" "ENTERPRISEPREMIUM_STUDENT" = "Office 365 A5 for students" "ENTERPRISEWITHSCAL" = "Office 365 E4" "ENTERPRISEWITHSCAL_FACULTY" = "Office 365 (Plan A4) for Faculty" "ENTERPRISEWITHSCAL_GOV" = "Microsoft Office 365 (Plan G4) for Government" "ENTERPRISEWITHSCAL_STUDENT" = "Office 365 (Plan A4) for Students" "EOP_ENTERPRISE_FACULTY" = "Exchange Online Protection for Faculty" "EOP_ENTERPRISE_PREMIUM" = "Exchange Enterprise CAL Services (EOP DLP)" "EQUIVIO_ANALYTICS" = "Office 365 Advanced Compliance" "EQUIVIO_ANALYTICS_FACULTY" = "Office 365 Advanced Compliance for faculty" "ESKLESSWOFFPACK_GOV" = "Microsoft Office 365 (Plan F2) for Government" "EXCHANGE_L_STANDARD" = "Exchange Online (Plan 1)" "EXCHANGE_S_ARCHIVE_ADDON_GOV" = "Exchange Online Archiving" "EXCHANGE_S_DESKLESS" = "Exchange Online Kiosk" "EXCHANGE_S_DESKLESS_GOV" = "Exchange Kiosk" "EXCHANGE_S_ENTERPRISE" = "Exchange Online (Plan 2)" "EXCHANGE_S_ENTERPRISE_GOV" = "Exchange Online (Plan 2) for Government" "EXCHANGE_S_ESSENTIALS" = "EXCHANGE ONLINE ESSENTIALS" "EXCHANGE_S_STANDARD" = "Exchange Online (Plan 2)" "EXCHANGE_S_STANDARD_MIDMARKET" = "Exchange Online (Plan 1)" "EXCHANGEARCHIVE" = "EXCHANGE ONLINE ARCHIVING FOR EXCHANGE SERVER" "EXCHANGEARCHIVE_ADDON" = "Exchange Online Archiving For Exchange Online" "EXCHANGEDESKLESS" = "Exchange Online Kiosk" "EXCHANGEENTERPRISE" = "Exchange Online (Plan 2)" "EXCHANGEENTERPRISE_FACULTY" = "Exchange Online Plan 2 for Faculty" "EXCHANGEENTERPRISE_GOV" = "Microsoft Office 365 Exchange Online (Plan 2) only for Government" "EXCHANGEESSENTIALS" = "EXCHANGE ONLINE ESSENTIALS (ExO P1 BASED)" "EXCHANGESTANDARD" = "Exchange Online (Plan 1)" "EXCHANGESTANDARD_GOV" = "Microsoft Office 365 Exchange Online (Plan 1) only for Government" "EXCHANGESTANDARD_STUDENT" = "Exchange Online (Plan 1) for Students" "EXCHANGETELCO" = "EXCHANGE ONLINE POP" "EXPERTS_ON_DEMAND" = "Microsoft Threat Experts - Experts on Demand" "FLOW_BUSINESS_PROCESS" = "Power Automate per flow plan" "FLOW_FREE" = "Microsoft Power Automate Free" "FLOW_P1" = "Microsoft Flow Plan 1" "FLOW_P2" = "MICROSOFT POWER AUTOMATE PLAN 2" "FLOW_PER_USER" = "Power Automate per user plan" "FLOW_PER_USER_DEPT" = "Power Automate per user plan dept" "FORMS_PRO" = "Dynamics 365 Customer Voice Trial" "Forms_Pro_AddOn" = "Dynamics 365 Customer Voice Additional Responses" "Forms_Pro_USL" = "Dynamics 365 Customer Voice USL" "GUIDES_USER" = "Dynamics 365 Guides" "IDENTITY_THREAT_PROTECTION" = "Microsoft 365 E5 Security" "IDENTITY_THREAT_PROTECTION_FOR_EMS_E5" = "Microsoft 365 E5 Security for EMS E5" "INFORMATION_PROTECTION_COMPLIANCE" = "Microsoft 365 E5 Compliance" "Intelligent_Content_Services" = "SharePoint Syntex" "INTUNE_A" = "Microsoft Intune" "INTUNE_A_D" = "Microsoft Intune Device" "INTUNE_A_D_GOV" = "Microsoft Intune Device FOR GOVERNMENT" "INTUNE_A_VL" = "Intune (Volume License)" "INTUNE_SMB" = "Microsoft Intune SMB" "IT_ACADEMY_AD" = "Microsoft Imagine Academy" "LITEPACK" = "Office 365 Small Business" "LITEPACK_P2" = "Office 365 Small Business Premium" "M365_E5_SUITE_COMPONENTS" = "Microsoft 365 E5 Suite features" "M365_F1" = "Microsoft 365 F1" "M365_F1_COMM" = "Microsoft 365 F1" "M365_G3_GOV" = "MICROSOFT 365 G3 GCC" "M365_SECURITY_COMPLIANCE_FOR_FLW" = "Microsoft 365 Security and Compliance for Firstline Workers" "M365EDU_A1" = "Microsoft 365 A1" "M365EDU_A3_FACULTY" = "Microsoft 365 A3 for Faculty" "M365EDU_A3_STUDENT" = "MICROSOFT 365 A3 FOR STUDENTS" "M365EDU_A3_STUUSEBNFT" = "Microsoft 365 A3 for students use benefit" "M365EDU_A3_STUUSEBNFT_RPA1" = "Microsoft 365 A3 - Unattended License for students use benefit" "M365EDU_A5_FACULTY" = "Microsoft 365 A5 for Faculty" "M365EDU_A5_NOPSTNCONF_STUUSEBNFT" = "Microsoft 365 A5 without Audio Conferencing for students use benefit" "M365EDU_A5_STUDENT" = "MICROSOFT 365 A5 FOR STUDENTS" "M365EDU_A5_STUUSEBNFT" = "Microsoft 365 A5 for students use benefit" "MCOCAP" = "Common Area Phone" "MCOCAP_GOV" = "Common Area Phone for GCC" "MCOEV" = "Microsoft 365 Phone System" "MCOEV_DOD" = "MICROSOFT 365 PHONE SYSTEM FOR DOD" "MCOEV_FACULTY" = "MICROSOFT 365 PHONE SYSTEM FOR FACULTY" "MCOEV_GCCHIGH" = "MICROSOFT 365 PHONE SYSTEM FOR GCCHIGH" "MCOEV_GOV" = "MICROSOFT 365 PHONE SYSTEM FOR GCC" "MCOEV_STUDENT" = "MICROSOFT 365 PHONE SYSTEM FOR STUDENTS" "MCOEV_TELSTRA" = "MICROSOFT 365 PHONE SYSTEM FOR TELSTRA" "MCOEV_USGOV_DOD" = "MICROSOFT 365 PHONE SYSTEM_USGOV_DOD" "MCOEV_USGOV_GCCHIGH" = "MICROSOFT 365 PHONE SYSTEM_USGOV_GCCHIGH" "MCOEVSMB_1" = "MICROSOFT 365 PHONE SYSTEM FOR SMALL AND MEDIUM BUSINESS" "MCOIMP" = "SKYPE FOR BUSINESS ONLINE (PLAN 1)" "MCOLITE" = "Skype for Business Online (Plan 1)" "MCOMEETADV" = "Microsoft 365 Audio Conferencing" "MCOMEETADV_GOC" = "MICROSOFT 365 AUDIO CONFERENCING FOR GCC" "MCOMEETADV_GOV" = "MICROSOFT 365 AUDIO CONFERENCING FOR GCC" "MCOPSTN_1_GOV" = "Microsoft 365 Domestic Calling Plan for GCC" "MCOPSTN_5" = "MICROSOFT 365 DOMESTIC CALLING PLAN (120 Minutes)" "MCOPSTN1" = "Microsoft 365 Domestic Calling Plan" "MCOPSTN2" = "Domestic and International Calling Plan" "MCOPSTN5" = "SKYPE FOR BUSINESS PSTN DOMESTIC CALLING (120 Minutes)" "MCOPSTNC" = "Communications Credits" "MCOPSTNEAU2" = "TELSTRA CALLING FOR O365" "MCOSTANDARD" = "Skype for Business Online (Plan 2)" "MCOSTANDARD_GOV" = "Skype for Business Online (Plan 2) Government" "MCOSTANDARD_MIDMARKET" = "Skype for Business Online (Plan 1)" "MDATP_Server" = "Microsoft Defender for Endpoint Server" "MDATP_XPLAT" = "Microsoft Defender for Endpoint P2" "MEE_FACULTY" = "Minecraft Education Edition Faculty" "MEE_STUDENT" = "Minecraft Education Edition Student" "MEETING_ROOM" = "Microsoft Teams Rooms Standard" "MFA_STANDALONE" = "Microsoft Azure Multi-Factor Authentication" "MICROSOFT_BUSINESS_CENTER" = "Microsoft Business Center" "MICROSOFT_REMOTE_ASSIST" = "Dynamics 365 Remote Assist" "MICROSOFT_REMOTE_ASSIST_HOLOLENS" = "Dynamics 365 Remote Assist HoloLens" "MIDSIZEPACK" = "Office 365 Midsize Business" "MS_TEAMS_IW" = "Microsoft Teams Trial" "MTR_PREM" = "Teams Rooms Premium" "O365_BUSINESS" = "Microsoft 365 Apps for Business" "O365_BUSINESS_ESSENTIALS" = "Microsoft 365 Business Basic" "O365_BUSINESS_PREMIUM" = "Microsoft 365 Business Standard" "OFFICE_PRO_PLUS_SUBSCRIPTION_SMBIZ" = "Office ProPlus" "OFFICE365_MULTIGEO" = "Multi-Geo Capabilities in Office 365" "OFFICESUBSCRIPTION" = "Microsoft 365 Apps for Enterprise" "OFFICESUBSCRIPTION_FACULTY" = "Office 365 ProPlus for Faculty" "OFFICESUBSCRIPTION_GOV" = "Office ProPlus" "OFFICESUBSCRIPTION_STUDENT" = "Office ProPlus Student Benefit" "PBI_PREMIUM_P1_ADDON" = "Power BI Premium P1" "PBI_PREMIUM_PER_USER" = "Power BI Premium Per User" "PBI_PREMIUM_PER_USER_ADDON" = "Power BI Premium Per User Add-On" "PBI_PREMIUM_PER_USER_DEPT" = "Power BI Premium Per User Dept" "PHONESYSTEM_VIRTUALUSER" = "MICROSOFT 365 PHONE SYSTEM - VIRTUAL USER" "PHONESYSTEM_VIRTUALUSER_GOV" = "Microsoft 365 Phone System - Virtual User for GCC" "PLANNERSTANDALONE" = "Planner Standalone" "POWERAPPS_DEV" = "Microsoft PowerApps for Developer" "POWER_BI_ADDON" = "Power BI for Office 365 Add-on" "POWER_BI_INDIVIDUAL_USER" = "Power BI" "POWER_BI_PRO" = "Power BI Pro" "POWER_BI_PRO_CE" = "Power BI Pro CE" "POWER_BI_PRO_DEPT" = "Power BI Pro Dept" "POWER_BI_STANDALONE" = "Power BI Stand Alone" "POWER_BI_STANDARD" = "Power BI (free)" "POWERAPPS_INDIVIDUAL_USER" = "Microsoft PowerApps and Logic flows" "POWERAPPS_PER_APP" = "Power Apps per app plan" "POWERAPPS_PER_APP_IW" = "PowerApps per app baseline access" "POWERAPPS_PER_USER" = "Power Apps per user plan" "POWERAPPS_VIRAL" = "Microsoft Power Apps Plan 2 Trial" "POWERAUTOMATE_ATTENDED_RPA" = "Power Automate per user with attended RPA plan" "POWERAUTOMATE_UNATTENDED_RPA" = "Power Automate unattended RPA add-on" "POWERFLOW_P2" = "Microsoft Power Apps Plan 2 (Qualified Offer)" "PROJECT_MADEIRA_PREVIEW_IW_SKU" = "Dynamics 365 Business Central for IWs" "PROJECT_P1" = "Project Plan 1" "PROJECT_PLAN1_DEPT" = "Project Plan 1 (for Department)" "PROJECT_PLAN3_DEPT" = "Project Plan 3 (for Department)" "PROJECTCLIENT" = "Project for Office 365" "PROJECTESSENTIALS" = "Project Online Essentials" "PROJECTONLINE_PLAN_1" = "Project Online Premium (without Project Client)" "PROJECTONLINE_PLAN_1_FACULTY" = "Project Online for Faculty Plan 1" "PROJECTONLINE_PLAN_1_STUDENT" = "Project Online for Students Plan 1" "PROJECTONLINE_PLAN_2" = "Project Online with Project for Office 365" "PROJECTONLINE_PLAN_2_FACULTY" = "Project Online for Faculty Plan 2" "PROJECTONLINE_PLAN_2_STUDENT" = "Project Online for Students Plan 2" "PROJECTPREMIUM" = "Project Plan 5" "PROJECTPREMIUM_GOV" = "Project Plan 5 for GCC" "PROJECTPROFESSIONAL" = "Project Plan 3" "PROJECTPROFESSIONAL_GOV" = "Project Plan 3 for GCC" "RIGHTSMANAGEMENT" = "Azure Information Protection Plan 1" "RIGHTSMANAGEMENT_ADHOC" = "Rights Management Adhoc" "RIGHTSMANAGEMENT_STANDARD_FACULTY" = "Information Rights Management for Faculty" "RIGHTSMANAGEMENT_STANDARD_STUDENT" = "Information Rights Management for Students" "RMS_S_ENTERPRISE_GOV" = "Windows Azure Active Directory Rights Management" "RMSBASIC" = "Rights Management Service Basic Content Protection" "SHAREPOINTDESKLESS" = "SharePoint Online Kiosk" "SHAREPOINTDESKLESS_GOV" = "SharePoint Online Kiosk" "SHAREPOINTENTERPRISE" = "SharePoint (Plan 2)" "SHAREPOINTENTERPRISE_GOV" = "SharePoint (Plan 2) Government" "SHAREPOINTENTERPRISE_MIDMARKET" = "SharePoint Online (Plan 1)" "SHAREPOINTLITE" = "SharePoint Online (Plan 1)" "SHAREPOINTSTANDARD" = "SharePoint Online Plan 1" "SHAREPOINTSTORAGE" = "Office 365 Extra File Storage" "SHAREPOINTSTORAGE_GOV" = "Office 365 Extra File Storage for GCC" "SHAREPOINTWAC" = "Office for the Web" "SHAREPOINTWAC_GOV" = "Office for the Web for Government" "SKU_Dynamics_365_for_HCM_Trial" = "Dynamics 365 for Talent" "SMB_APPS" = "Business Apps (free)" "SMB_BUSINESS" = "MICROSOFT 365 APPS FOR BUSINESS" "SMB_BUSINESS_ESSENTIALS" = "MICROSOFT 365 BUSINESS BASIC" "SMB_BUSINESS_PREMIUM" = "MICROSOFT 365 BUSINESS STANDARD - PREPAID LEGACY" "SPB" = "Microsoft 365 Business Premium" "SPE_E3" = "Microsoft 365 E3" "SPE_E3_RPA1" = "Microsoft 365 E3 - Unattended License" "SPE_E3_USGOV_DOD" = "Microsoft 365 E3_USGOV_DOD" "SPE_E3_USGOV_GCCHIGH" = "Microsoft 365 E3_USGOV_GCCHIGH" "SPE_E5" = "Microsoft 365 E5" "SPE_F1" = "Microsoft 365 F3" "SPZA_IW" = "App Connect IW" "STANDARD_B_PILOT" = "Office 365 (Small Business Preview)" "STANDARDPACK" = "Office 365 E1" "STANDARDPACK_FACULTY" = "Office 365 (Plan A1) for Faculty" "STANDARDPACK_GOV" = "Microsoft Office 365 (Plan G1) for Government" "STANDARDPACK_STUDENT" = "Office 365 (Plan A1) for Students" "STANDARDWOFFPACK" = "Office 365 E2" "STANDARDWOFFPACK_FACULTY" = "Office 365 Education E1 for Faculty" "STANDARDWOFFPACK_GOV" = "Microsoft Office 365 (Plan G2) for Government" "STANDARDWOFFPACK_IW_FACULTY" = "Office 365 Education for Faculty" "STANDARDWOFFPACK_IW_STUDENT" = "Office 365 Education for Students" "STANDARDWOFFPACK_STUDENT" = "Microsoft Office 365 (Plan A2) for Students" "STANDARDWOFFPACKPACK_FACULTY" = "Office 365 (Plan A2) for Faculty" "STANDARDWOFFPACKPACK_STUDENT" = "Office 365 (Plan A2) for Students" "STREAM" = "Microsoft Stream" "STREAM_P2" = "Microsoft Stream Plan 2" "STREAM_STORAGE" = "Microsoft Stream Storage Add-On (500 GB)" "TEAMS_COMMERCIAL_TRIAL" = "Microsoft Teams Commercial Cloud" "TEAMS_EXPLORATORY" = "Microsoft Teams Exploratory" "TEAMS_FREE" = "MICROSOFT TEAMS (FREE)" "TEAMS1" = "Microsoft Teams" "THREAT_INTELLIGENCE" = "Microsoft Defender for Office 365 (Plan 2)" "THREAT_INTELLIGENCE_GOV" = "Microsoft Defender for Office 365 (Plan 2) GCC" "TOPIC_EXPERIENCES" = "Viva Topics" "UNIVERSAL_PRINT" = "Universal Print" "VIDEO_INTEROP" = "Polycom Skype Meeting Video Interop for Skype for Business" "VIRTUAL_AGENT_BASE" = "Power Virtual Agent" "VISIO_PLAN1_DEPT" = "Visio Plan 1" "VISIO_PLAN2_DEPT" = "Visio Plan 2" "VISIOCLIENT" = "Visio Plan 2" "VISIOCLIENT_GOV" = "VISIO PLAN 2 FOR GCC" "VISIOONLINE_PLAN1" = "Visio Online Plan 1" "WACONEDRIVEENTERPRISE" = "OneDrive for Business (Plan 2)" "WACONEDRIVESTANDARD" = "ONEDRIVE FOR BUSINESS (PLAN 1)" "WACSHAREPOINTSTD" = "Office Online STD" "WIN_DEF_ATP" = "Microsoft Defender for Endpoint P1" "WIN10_ENT_A3_FAC" = "Windows 10 Enterprise A3 for faculty" "WIN10_ENT_A3_STU" = "Windows 10 Enterprise A3 for students" "WIN10_PRO_ENT_SUB" = "Windows 10 Enterprise E3" "Win10_VDA_E3" = "Windows 10 Enterprise E3" "WIN10_VDA_E5" = "Windows 10 Enterprise E5" "WINDOWS_STORE" = "Windows Store for Business" "WINE5_GCC_COMPAT" = "Windows 10 Enterprise E5 Commercial (GCC Compatible)" "WORKPLACE_ANALYTICS" = "Microsoft Workplace Analytics" "YAMMER_ENTERPRISE" = "Yammer Enterprise" "YAMMER_MIDSIZE" = "Yammer" } } Process { if (-not $ToSku) { $ConvertedLicenses = foreach ($LicenseToProcess in $License) { if ($LicenseToProcess -is [string]) { $L = $LicenseToProcess } elseif ($LicenseToProcess -is [Microsoft.Online.Administration.UserLicense]) { $L = $LicenseToProcess.AccountSkuId } else { continue } # Remove tenant from SKU #if ($L -match ':') { # $Split = $L -split ':' # $L = $Split[-1] #} # Removes : from tenant:VisioClient $L = $L -replace '.*(:)' $Conversion = $O365SKU[$L] if ($null -eq $Conversion) { $L } else { $Conversion } } } else { $ConvertedLicenses = :Outer foreach ($L in $License) { $Conversion = foreach ($_ in $O365SKU.GetEnumerator()) { if ($_.Value -eq $L) { $_.Name continue Outer } } if ($null -eq $Conversion) { $L } } } if ($ReturnArray) { $ConvertedLicenses } else { $ConvertedLicenses -join $Separator } } End {} } 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, # can't use this just yet, some old code uses strings in StartTime/EndTime. #[nullable[DateTime]] $EndTime, # After that's fixed will change this. [string] $Ignore = '*1601*' ) if ($null -ne $StartTime -and $null -ne $EndTime) { try { if ($StartTime -notlike $Ignore -and $EndTime -notlike $Ignore) { $Days = (New-TimeSpan -Start $StartTime -End $EndTime).Days } } catch {} } elseif ($null -ne $EndTime) { if ($StartTime -notlike $Ignore -and $EndTime -notlike $Ignore) { $Days = (New-TimeSpan -Start (Get-Date) -End ($EndTime)).Days } } elseif ($null -ne $StartTime) { if ($StartTime -notlike $Ignore -and $EndTime -notlike $Ignore) { $Days = (New-TimeSpan -Start $StartTime -End (Get-Date)).Days } } return $Days } function Convert-ToDateTime { [CmdletBinding()] param ( [string] $Timestring, [string] $Ignore = '*1601*' ) Try { $DateTime = ([datetime]::FromFileTime($Timestring)) } catch { $DateTime = $null } if ($null -eq $DateTime -or $DateTime -like $Ignore) { return $null } else { return $DateTime } } 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++) { "$($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 Convert-UserAccountControl { [cmdletBinding()] param( [alias('UAC')][int] $UserAccountControl, [string] $Separator ) $UserAccount = [ordered] @{ "SCRIPT" = 1 "ACCOUNTDISABLE" = 2 "HOMEDIR_REQUIRED" = 8 "LOCKOUT" = 16 "PASSWD_NOTREQD" = 32 "ENCRYPTED_TEXT_PWD_ALLOWED" = 128 "TEMP_DUPLICATE_ACCOUNT" = 256 "NORMAL_ACCOUNT" = 512 "INTERDOMAIN_TRUST_ACCOUNT" = 2048 "WORKSTATION_TRUST_ACCOUNT" = 4096 "SERVER_TRUST_ACCOUNT" = 8192 "DONT_EXPIRE_PASSWORD" = 65536 "MNS_LOGON_ACCOUNT" = 131072 "SMARTCARD_REQUIRED" = 262144 "TRUSTED_FOR_DELEGATION" = 524288 "NOT_DELEGATED" = 1048576 "USE_DES_KEY_ONLY" = 2097152 "DONT_REQ_PREAUTH" = 4194304 "PASSWORD_EXPIRED" = 8388608 "TRUSTED_TO_AUTH_FOR_DELEGATION" = 16777216 "PARTIAL_SECRETS_ACCOUNT" = 67108864 } $Output = foreach ($_ in $UserAccount.Keys) { $binaryAnd = $UserAccount[$_] -band $UserAccountControl if ($binaryAnd -ne "0") { $_ } } if ($Separator) { $Output -join $Separator } else { $Output } } function ConvertFrom-DistinguishedName { <# .SYNOPSIS Converts a Distinguished Name to CN, OU, Multiple OUs or DC .DESCRIPTION Converts a Distinguished Name to CN, OU, Multiple OUs or DC .PARAMETER DistinguishedName Distinguished Name to convert .PARAMETER ToOrganizationalUnit Converts DistinguishedName to Organizational Unit .PARAMETER ToDC Converts DistinguishedName to DC .PARAMETER ToDomainCN Converts DistinguishedName to Domain Canonical Name (CN) .PARAMETER ToCanonicalName Converts DistinguishedName to Canonical Name .EXAMPLE $DistinguishedName = 'CN=Przemyslaw Klys,OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz' ConvertFrom-DistinguishedName -DistinguishedName $DistinguishedName -ToOrganizationalUnit Output: OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz .EXAMPLE $DistinguishedName = 'CN=Przemyslaw Klys,OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz' ConvertFrom-DistinguishedName -DistinguishedName $DistinguishedName Output: Przemyslaw Klys .EXAMPLE ConvertFrom-DistinguishedName -DistinguishedName 'OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz' -ToMultipleOrganizationalUnit -IncludeParent Output: OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz OU=Production,DC=ad,DC=evotec,DC=xyz .EXAMPLE ConvertFrom-DistinguishedName -DistinguishedName 'OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz' -ToMultipleOrganizationalUnit Output: OU=Production,DC=ad,DC=evotec,DC=xyz .EXAMPLE $Con = @( 'CN=Windows Authorization Access Group,CN=Builtin,DC=ad,DC=evotec,DC=xyz' 'CN=Mmm,DC=elo,CN=nee,DC=RootDNSServers,CN=MicrosoftDNS,CN=System,DC=ad,DC=evotec,DC=xyz' 'CN=e6d5fd00-385d-4e65-b02d-9da3493ed850,CN=Operations,CN=DomainUpdates,CN=System,DC=ad,DC=evotec,DC=xyz' 'OU=Domain Controllers,DC=ad,DC=evotec,DC=pl' 'OU=Microsoft Exchange Security Groups,DC=ad,DC=evotec,DC=xyz' ) ConvertFrom-DistinguishedName -DistinguishedName $Con -ToLastName Output: Windows Authorization Access Group Mmm e6d5fd00-385d-4e65-b02d-9da3493ed850 Domain Controllers Microsoft Exchange Security Groups .EXAMPLEE ConvertFrom-DistinguishedName -DistinguishedName 'DC=ad,DC=evotec,DC=xyz' -ToCanonicalName ConvertFrom-DistinguishedName -DistinguishedName 'OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz' -ToCanonicalName ConvertFrom-DistinguishedName -DistinguishedName 'CN=test,OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz' -ToCanonicalName Output: ad.evotec.xyz ad.evotec.xyz\Production\Users ad.evotec.xyz\Production\Users\test .NOTES General notes #> [CmdletBinding(DefaultParameterSetName = 'Default')] param( [Parameter(ParameterSetName = 'ToOrganizationalUnit')] [Parameter(ParameterSetName = 'ToMultipleOrganizationalUnit')] [Parameter(ParameterSetName = 'ToDC')] [Parameter(ParameterSetName = 'ToDomainCN')] [Parameter(ParameterSetName = 'Default')] [Parameter(ParameterSetName = 'ToLastName')] [Parameter(ParameterSetName = 'ToCanonicalName')] [alias('Identity', 'DN')][Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0)][string[]] $DistinguishedName, [Parameter(ParameterSetName = 'ToOrganizationalUnit')][switch] $ToOrganizationalUnit, [Parameter(ParameterSetName = 'ToMultipleOrganizationalUnit')][alias('ToMultipleOU')][switch] $ToMultipleOrganizationalUnit, [Parameter(ParameterSetName = 'ToMultipleOrganizationalUnit')][switch] $IncludeParent, [Parameter(ParameterSetName = 'ToDC')][switch] $ToDC, [Parameter(ParameterSetName = 'ToDomainCN')][switch] $ToDomainCN, [Parameter(ParameterSetName = 'ToLastName')][switch] $ToLastName, [Parameter(ParameterSetName = 'ToCanonicalName')][switch] $ToCanonicalName ) Process { foreach ($Distinguished in $DistinguishedName) { if ($ToDomainCN) { $DN = $Distinguished -replace '.*?((DC=[^=]+,)+DC=[^=]+)$', '$1' $CN = $DN -replace ',DC=', '.' -replace "DC=" if ($CN) { $CN } } elseif ($ToOrganizationalUnit) { $Value = [Regex]::Match($Distinguished, '(?=OU=)(.*\n?)(?<=.)').Value if ($Value) { $Value } } elseif ($ToMultipleOrganizationalUnit) { if ($IncludeParent) { $Distinguished } while ($true) { #$dn = $dn -replace '^.+?,(?=CN|OU|DC)' $Distinguished = $Distinguished -replace '^.+?,(?=..=)' if ($Distinguished -match '^DC=') { break } $Distinguished } } elseif ($ToDC) { #return [Regex]::Match($DistinguishedName, '(?=DC=)(.*\n?)(?<=.)').Value # return [Regex]::Match($DistinguishedName, '.*?(DC=.*)').Value $Value = $Distinguished -replace '.*?((DC=[^=]+,)+DC=[^=]+)$', '$1' if ($Value) { $Value } #return [Regex]::Match($DistinguishedName, 'CN=.*?(DC=.*)').Groups[1].Value } elseif ($ToLastName) { # Would be best if it worked, but there is too many edge cases so hand splits seems to be the best solution # Feel free to change it back to regex if you know how ;) <# https://stackoverflow.com/questions/51761894/regex-extract-ou-from-distinguished-name $Regex = "^(?:(?<cn>CN=(?<name>.*?)),)?(?<parent>(?:(?<path>(?:CN|OU).*?),)?(?<domain>(?:DC=.*)+))$" $Found = $Distinguished -match $Regex if ($Found) { $Matches.name } #> $NewDN = $Distinguished -split ",DC=" if ($NewDN[0].Contains(",OU=")) { [Array] $ChangedDN = $NewDN[0] -split ",OU=" } elseif ($NewDN[0].Contains(",CN=")) { [Array] $ChangedDN = $NewDN[0] -split ",CN=" } else { [Array] $ChangedDN = $NewDN[0] } if ($ChangedDN[0].StartsWith('CN=')) { $ChangedDN[0] -replace 'CN=', '' } else { $ChangedDN[0] -replace 'OU=', '' } } elseif ($ToCanonicalName) { $Domain = $null $Rest = $null foreach ($O in $Distinguished -split '(?<!\\),') { if ($O -match '^DC=') { $Domain += $O.Substring(3) + '.' } else { $Rest = $O.Substring(3) + '\' + $Rest } } if ($Domain -and $Rest) { $Domain.Trim('.') + '\' + ($Rest.TrimEnd('\') -replace '\\,', ',') } elseif ($Domain) { $Domain.Trim('.') } elseif ($Rest) { $Rest.TrimEnd('\') -replace '\\,', ',' } } else { $Regex = '^CN=(?<cn>.+?)(?<!\\),(?<ou>(?:(?:OU|CN).+?(?<!\\),)+(?<dc>DC.+?))$' #$Output = foreach ($_ in $Distinguished) { $Found = $Distinguished -match $Regex if ($Found) { $Matches.cn } #} #$Output.cn } } } } #ConvertFrom-DistinguishedName -DistinguishedName 'DC=ad,DC=evotec,DC=xyz' -ToCanonicalName #ConvertFrom-DistinguishedName -DistinguishedName 'OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz' -ToCanonicalName #ConvertFrom-DistinguishedName -DistinguishedName 'CN=test,OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz' -ToCanonicalName #ConvertFrom-DistinguishedName -DistinguishedName 'OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz' -ToMultipleOrganizationalUnit -IncludeParent #ConvertFrom-DistinguishedName -DistinguishedName 'CN=test,OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz' <# $DistinguishedName = @( 'CN=Przemyslaw Klys,OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz' 'CN=ITR03_AD Admins,OU=Security,OU=Groups,OU=Production,DC=ad,DC=evotec,DC=xyz' 'CN=SADM Testing 2,OU=Special,OU=Accounts,OU=Production,DC=ad,DC=evotec,DC=xyz' ) ConvertFrom-DistinguishedName -ToOrganizationalUnit -DistinguishedName $DistinguishedName #> <# $Oops = 'cn={55FB3860-74C9-4262-AD77-30197EAB9999},cn=policies,cn=system,DC=ad,DC=evotec,DC=xyz' ConvertFrom-DistinguishedName -DistinguishedName $Oops -ToDomainCN | Format-Table -AutoSize $Con = @( 'CN=Windows Authorization Access Group,CN=Builtin,DC=ad,DC=evotec,DC=xyz' 'CN=Mmm,DC=elo,CN=nee,DC=RootDNSServers,CN=MicrosoftDNS,CN=System,DC=ad,DC=evotec,DC=xyz' 'CN=e6d5fd00-385d-4e65-b02d-9da3493ed850,CN=Operations,CN=DomainUpdates,CN=System,DC=ad,DC=evotec,DC=xyz' 'OU=Domain Controllers,DC=ad,DC=evotec,DC=pl' 'OU=Microsoft Exchange Security Groups,DC=ad,DC=evotec,DC=xyz' ) foreach ($_ in $Con) { ConvertFrom-DistinguishedName -DistinguishedName $_ -ToDC | Format-Table -AutoSize } foreach ($_ in $Con) { ConvertFrom-DistinguishedName -DistinguishedName $_ -ToOrganizationalUnit | Format-Table -AutoSize } return $Search = '*DC=RootDNSServers*' $Server = 'ad.evotec.xyz' $DomainStructure = Get-ADObject -Filter * -Properties canonicalName -SearchScope OneLevel -Server $Server | Sort-Object canonicalName #| Select-Object objectClass, canonicalName, DistinguishedName $Output = foreach ($Structure in $DomainStructure) { Get-ADObject -SearchBase $Structure.DistinguishedName -Filter * -Properties canonicalName -Server $Server -SearchScope Subtree | Sort-Object canonicalName } $Output | Where-Object { $_.DistinguishedName -like $Search } | ft -a #> # # 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-LanguageCode { [cmdletBinding()] param( [string] $LanguageCode ) $LanguageCodeDictionary = @{ '1' = "Arabic" '4' = "Chinese (Simplified)?? China" '9' = "English" '1025' = "Arabic (Saudi Arabia)" '1026' = "Bulgarian" '1027' = "Catalan" '1028' = "Chinese (Traditional) Taiwan" '1029' = "Czech" '1030' = "Danish" '1031' = "German (Germany)" '1032' = "Greek" '1033' = "English (United States)" '1034' = "Spanish (Traditional Sort)" '1035' = "Finnish" '1036' = "French (France)" '1037' = "Hebrew" '1038' = "Hungarian" '1039' = "Icelandic" '1040' = "Italian (Italy)" '1041' = "Japanese" '1042' = "Korean" '1043' = "Dutch (Netherlands)" '1044' = "Norwegian (Bokmal)" '1045' = "Polish" '1046' = "Portuguese (Brazil)" '1047' = "Rhaeto-Romanic" '1048' = "Romanian" '1049' = "Russian" '1050' = "Croatian" '1051' = "Slovak" '1052' = "Albanian" '1053' = "Swedish" '1054' = "Thai" '1055' = "Turkish" '1056' = "Urdu" '1057' = "Indonesian" '1058' = "Ukrainian" '1059' = "Belarusian" '1060' = "Slovenian" '1061' = "Estonian" '1062' = "Latvian" '1063' = "Lithuanian" '1065' = "Persian" '1066' = "Vietnamese" '1069' = "Basque (Basque)" '1070' = "Serbian" '1071' = "Macedonian (FYROM)" '1072' = "Sutu" '1073' = "Tsonga" '1074' = "Tswana" '1076' = "Xhosa" '1077' = "Zulu" '1078' = "Afrikaans" '1080' = "Faeroese" '1081' = "Hindi" '1082' = "Maltese" '1084' = "Scottish Gaelic (United Kingdom)" '1085' = "Yiddish" '1086' = "Malay (Malaysia)" '2049' = "Arabic (Iraq)" '2052' = "Chinese (Simplified) PRC" '2055' = "German (Switzerland)" '2057' = "English (United Kingdom)" '2058' = "Spanish (Mexico)" '2060' = "French (Belgium)" '2064' = "Italian (Switzerland)" '2067' = "Dutch (Belgium)" '2068' = "Norwegian (Nynorsk)" '2070' = "Portuguese (Portugal)" '2072' = "Romanian (Moldova)" '2073' = "Russian (Moldova)" '2074' = "Serbian (Latin)" '2077' = "Swedish (Finland)" '3073' = "Arabic (Egypt)" '3076' = "Chinese Traditional (Hong Kong SAR)" '3079' = "German (Austria)" '3081' = "English (Australia)" '3082' = "Spanish (International Sort)" '3084' = "French (Canada)" '3098' = "Serbian (Cyrillic)" '4097' = "Arabic (Libya)" '4100' = "Chinese Simplified (Singapore)" '4103' = "German (Luxembourg)" '4105' = "English (Canada)" '4106' = "Spanish (Guatemala)" '4108' = "French (Switzerland)" '5121' = "Arabic (Algeria)" '5127' = "German (Liechtenstein)" '5129' = "English (New Zealand)" '5130' = "Spanish (Costa Rica)" '5132' = "French (Luxembourg)" '6145' = "Arabic (Morocco)" '6153' = "English (Ireland)" '6154' = "Spanish (Panama)" '7169' = "Arabic (Tunisia)" '7177' = "English (South Africa)" '7178' = "Spanish (Dominican Republic)" '8193' = "Arabic (Oman)" '8201' = "English (Jamaica)" '8202' = "Spanish (Venezuela)" '9217' = "Arabic (Yemen)" '9226' = "Spanish (Colombia)" '10241' = "Arabic (Syria)" '10249' = "English (Belize)" '10250' = "Spanish (Peru)" '11265' = "Arabic (Jordan)" '11273' = "English (Trinidad)" '11274' = "Spanish (Argentina)" '12289' = "Arabic (Lebanon)" '12298' = "Spanish (Ecuador)" '13313' = "Arabic (Kuwait)" '13322' = "Spanish (Chile)" '14337' = "Arabic (U.A.E.)" '14346' = "Spanish (Uruguay)" '15361' = "Arabic (Bahrain)" '15370' = "Spanish (Paraguay)" '16385' = "Arabic (Qatar)" '16394' = "Spanish (Bolivia)" '17418' = "Spanish (El Salvador)" '18442' = "Spanish (Honduras)" '19466' = "Spanish (Nicaragua)" '20490' = "Spanish (Puerto Rico)" } $Output = $LanguageCodeDictionary[$LanguageCode] if ($Output) { $Output } else { "Unknown (Undocumented)" } } function ConvertFrom-NetbiosName { [cmdletBinding()] param( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0)] [string[]] $Identity ) process { foreach ($Ident in $Identity) { if ($Ident -like '*\*') { $NetbiosWithObject = $Ident -split "\\" if ($NetbiosWithObject.Count -eq 2) { $LDAPQuery = ([ADSI]"LDAP://$($NetbiosWithObject[0])") $DomainName = ConvertFrom-DistinguishedName -DistinguishedName $LDAPQuery.distinguishedName -ToDomainCN [PSCustomObject] @{ DomainName = $DomainName Name = $NetbiosWithObject[1] } } else { # we can't be sure what we got so lets push back what we got [PSCustomObject] @{ DomainName = '' Name = $Ident } } } else { # we can't be sure what we got so lets push back what we got [PSCustomObject] @{ DomainName = '' Name = $Ident } } } } } <# 'TEST\Domain Admins', 'EVOTEC\Domain Admins', 'EVOTECPL\Domain Admins' | ConvertFrom-NetbiosName ConvertFrom-NetbiosName -Identity 'TEST\Domain Admins', 'EVOTEC\Domain Admins', 'EVOTECPL\Domain Admins' #> function ConvertFrom-ObjectToString { <# .SYNOPSIS Helps with converting given objects to their string representation. .DESCRIPTION Helps with converting given objects to their string representation. .PARAMETER Objects Objects to convert to string representation. .PARAMETER IncludeProperties Properties to include in the string representation. .PARAMETER ExcludeProperties Properties to exclude from the string representation. .PARAMETER OutputType Type of the output object. Options are: Hashtable, Ordered, PSCustomObject. If not specified, the output type is hashtable (string) .PARAMETER NumbersAsString If specified, numbers are converted to strings. Default is number are presented in their (unquoted) numerica form .PARAMETER QuotePropertyNames If specified, all property names are quoted. Default: property names are quoted only if they contain spaces. .PARAMETER DateTimeFormat Format for DateTime values. Default: 'yyyy-MM-dd HH:mm:ss' .EXAMPLE Get-Process -Name "PowerShell" | ConvertFrom-ObjectToString -IncludeProperties 'ProcessName', 'Id', 'Handles' OUTPUT: @{ 'Handles' = '543' 'Id' = '8092' 'ProcessName' = 'powershell' } @{ 'Handles' = '636' 'Id' = '11360' 'ProcessName' = 'powershell' } .NOTES General notes #> [CmdletBinding()] param( [parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Mandatory)][Array] $Objects, [string[]] $IncludeProperties, [string[]] $ExcludeProperties, [ValidateSet('Hashtable', 'Ordered', 'PSCustomObject')][string] $OutputType = 'Hashtable', [switch] $NumbersAsString, [switch] $QuotePropertyNames, [string] $DateTimeFormat = 'yyyy-MM-dd HH:mm:ss' ) begin { if ($OutputType -eq 'Hashtable') { $Type = '' } elseif ($OutputType -eq 'Ordered') { $Type = '[Ordered] ' } else { $Type = '[PSCustomObject] ' } } process { filter IsNumeric() { return $_ -is [byte] -or $_ -is [int16] -or $_ -is [int32] -or $_ -is [int64] ` -or $_ -is [sbyte] -or $_ -is [uint16] -or $_ -is [uint32] -or $_ -is [uint64] ` -or $_ -is [float] -or $_ -is [double] -or $_ -is [decimal] } function GetFormattedPair () { # returns 'key' = <valuestring> or just <valuestring> if key is empty # valuestring is either $null, '<string>', or number param ( [string] $Key, [object] $Value ) if ($key -eq '') { $left = '' } elseif ($key -match '\s' -or $QuotePropertyNames) { $left = "'$Key' = " } else { $left = "$Key = " } if ($null -eq $value) { "$left`$null" } elseif ($Value -is [System.Collections.IList]) { $arrayStrings = foreach ($element in $Object.$Key) { GetFormattedPair -Key '' -Value $element } "$left@(" + ($arrayStrings -join ', ') + ")" } elseif ($Value -is [System.Collections.IDictionary]) { if ($IncludeProperties -and $Key -notin $IncludeProperties) { return } if ($Key -in $ExcludeProperties) { return } $propertyString = foreach ($Key in $Value.Keys) { GetFormattedPair -Key $key -Value $Value[$key] } "$left@{" + ($propertyString -join '; ') + "}" } elseif ($value -is [DateTime]) { "$left'$($Value.ToString($DateTimeFormat))'" } elseif (($value | IsNumeric) -and -not $NumbersAsString) { "$left$($Value)" } else { "$left'$($Value)'" } } foreach ($Object in $Objects) { if ($Object -is [System.Collections.IDictionary]) { Write-Host Write-Host -Object "$Type@{" foreach ($Key in $Object.Keys) { if ($IncludeProperties -and $Key -notin $IncludeProperties) { continue } if ($Key -in $ExcludeProperties) { continue } Write-Host -Object " $(GetFormattedPair -Key $Key -Value $Object.$Key)" -ForegroundColor Cyan } Write-Host -Object "}" } elseif ($Object -is [Object]) { Write-Host Write-Host -Object "$Type@{" foreach ($Key in $Object.PSObject.Properties.Name) { if ($IncludeProperties -and $Key -notin $IncludeProperties) { continue } if ($Key -in $ExcludeProperties) { continue } Write-Host -Object " $(GetFormattedPair -Key $Key -Value $Object.$Key)" -ForegroundColor Cyan } Write-Host -Object "}" } else { Write-Host -Object $Object } } } } 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-ScriptBlock { [CmdletBinding()] param( [ScriptBlock] $ScriptBlock ) [Array] $Output = foreach ($Line in $ScriptBlock.Ast.EndBlock.Statements.Extent) { [string] $Line + [System.Environment]::NewLine } return $Output } function ConvertFrom-SID { <# .SYNOPSIS Small command that can resolve SID values .DESCRIPTION Small command that can resolve SID values .PARAMETER SID Value to resolve .PARAMETER OnlyWellKnown Only resolve SID when it's well know SID. Otherwise return $null .PARAMETER OnlyWellKnownAdministrative Only resolve SID when it's administrative well know SID. Otherwise return $null .PARAMETER DoNotResolve Uses only dicrionary values without querying AD .EXAMPLE ConvertFrom-SID -SID 'S-1-5-8', 'S-1-5-9', 'S-1-5-11', 'S-1-5-18', 'S-1-1-0' -DoNotResolve .NOTES General notes #> [cmdletbinding(DefaultParameterSetName = 'Standard')] param( [Parameter(ParameterSetName = 'Standard')] [Parameter(ParameterSetName = 'OnlyWellKnown')] [Parameter(ParameterSetName = 'OnlyWellKnownAdministrative')] [string[]] $SID, [Parameter(ParameterSetName = 'OnlyWellKnown')][switch] $OnlyWellKnown, [Parameter(ParameterSetName = 'OnlyWellKnownAdministrative')][switch] $OnlyWellKnownAdministrative, [Parameter(ParameterSetName = 'Standard')][switch] $DoNotResolve ) # https://support.microsoft.com/en-au/help/243330/well-known-security-identifiers-in-windows-operating-systems $WellKnownAdministrative = @{ 'S-1-5-18' = [PSCustomObject] @{ Name = 'NT AUTHORITY\SYSTEM' SID = 'S-1-5-18' DomainName = '' Type = 'WellKnownAdministrative' Error = '' } 'S-1-5-32-544' = [PSCustomObject] @{ Name = 'BUILTIN\Administrators' SID = 'S-1-5-32-544' DomainName = '' Type = 'WellKnownAdministrative' Error = '' } } $wellKnownSIDs = @{ 'S-1-0' = [PSCustomObject] @{ Name = 'Null AUTHORITY' SID = 'S-1-0' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-0-0' = [PSCustomObject] @{ Name = 'NULL SID' SID = 'S-1-0-0' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-1' = [PSCustomObject] @{ Name = 'WORLD AUTHORITY' SID = 'S-1-1' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-1-0' = [PSCustomObject] @{ Name = 'Everyone' SID = 'S-1-1-0' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-2' = [PSCustomObject] @{ Name = 'LOCAL AUTHORITY' SID = 'S-1-2' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-2-0' = [PSCustomObject] @{ Name = 'LOCAL' SID = 'S-1-2-0' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-2-1' = [PSCustomObject] @{ Name = 'CONSOLE LOGON' SID = 'S-1-2-1' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-3' = [PSCustomObject] @{ Name = 'CREATOR AUTHORITY' SID = 'S-1-3' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-3-0' = [PSCustomObject] @{ Name = 'CREATOR OWNER' SID = 'S-1-3-0' DomainName = '' Type = 'WellKnownAdministrative' Error = '' } 'S-1-3-1' = [PSCustomObject] @{ Name = 'CREATOR GROUP' SID = 'S-1-3-1' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-3-2' = [PSCustomObject] @{ Name = 'CREATOR OWNER SERVER' SID = 'S-1-3-2' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-3-3' = [PSCustomObject] @{ Name = 'CREATOR GROUP SERVER' SID = 'S-1-3-3' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-3-4' = [PSCustomObject] @{ Name = 'OWNER RIGHTS' SID = 'S-1-3-4' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-80-0' = [PSCustomObject] @{ Name = 'NT SERVICE\ALL SERVICES' SID = 'S-1-5-80-0' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-4' = [PSCustomObject] @{ Name = 'Non-unique Authority' SID = 'S-1-4' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5' = [PSCustomObject] @{ Name = 'NT AUTHORITY' SID = 'S-1-5' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-1' = [PSCustomObject] @{ Name = 'NT AUTHORITY\DIALUP' SID = 'S-1-5-1' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-2' = [PSCustomObject] @{ Name = 'NT AUTHORITY\NETWORK' SID = 'S-1-5-2' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-3' = [PSCustomObject] @{ Name = 'NT AUTHORITY\BATCH' SID = 'S-1-5-3' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-4' = [PSCustomObject] @{ Name = 'NT AUTHORITY\INTERACTIVE' SID = 'S-1-5-4' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-6' = [PSCustomObject] @{ Name = 'NT AUTHORITY\SERVICE' SID = 'S-1-5-6' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-7' = [PSCustomObject] @{ Name = 'NT AUTHORITY\ANONYMOUS LOGON' SID = 'S-1-5-7' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-8' = [PSCustomObject] @{ Name = 'NT AUTHORITY\PROXY' SID = 'S-1-5-8' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-9' = [PSCustomObject] @{ Name = 'NT AUTHORITY\ENTERPRISE DOMAIN CONTROLLERS' SID = 'S-1-5-9' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-10' = [PSCustomObject] @{ Name = 'NT AUTHORITY\SELF' SID = 'S-1-5-10' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-11' = [PSCustomObject] @{ Name = 'NT AUTHORITY\Authenticated Users' SID = 'S-1-5-11' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-12' = [PSCustomObject] @{ Name = 'NT AUTHORITY\RESTRICTED' SID = 'S-1-5-12' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-13' = [PSCustomObject] @{ Name = 'NT AUTHORITY\TERMINAL SERVER USER' SID = 'S-1-5-13' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-14' = [PSCustomObject] @{ Name = 'NT AUTHORITY\REMOTE INTERACTIVE LOGON' SID = 'S-1-5-14' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-15' = [PSCustomObject] @{ Name = 'NT AUTHORITY\This Organization' SID = 'S-1-5-15' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-17' = [PSCustomObject] @{ Name = 'NT AUTHORITY\IUSR' SID = 'S-1-5-17' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-18' = [PSCustomObject] @{ Name = 'NT AUTHORITY\SYSTEM' SID = 'S-1-5-18' DomainName = '' Type = 'WellKnownAdministrative' Error = '' } 'S-1-5-19' = [PSCustomObject] @{ Name = 'NT AUTHORITY\LOCAL SERVICE' SID = 'S-1-5-19' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-20' = [PSCustomObject] @{ Name = 'NT AUTHORITY\NETWORK SERVICE' SID = 'S-1-5-20' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-544' = [PSCustomObject] @{ Name = 'BUILTIN\Administrators' SID = 'S-1-5-32-544' DomainName = '' Type = 'WellKnownAdministrative' Error = '' } 'S-1-5-32-545' = [PSCustomObject] @{ Name = 'BUILTIN\Users' SID = 'S-1-5-32-545' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-546' = [PSCustomObject] @{ Name = 'BUILTIN\Guests' SID = 'S-1-5-32-546' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-547' = [PSCustomObject] @{ Name = 'BUILTIN\Power Users' SID = 'S-1-5-32-547' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-548' = [PSCustomObject] @{ Name = 'BUILTIN\Account Operators' SID = 'S-1-5-32-548' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-549' = [PSCustomObject] @{ Name = 'BUILTIN\Server Operators' SID = 'S-1-5-32-549' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-550' = [PSCustomObject] @{ Name = 'BUILTIN\Print Operators' SID = 'S-1-5-32-550' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-551' = [PSCustomObject] @{ Name = 'BUILTIN\Backup Operators' SID = 'S-1-5-32-551' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-552' = [PSCustomObject] @{ Name = 'BUILTIN\Replicators' SID = 'S-1-5-32-552' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-64-10' = [PSCustomObject] @{ Name = 'NT AUTHORITY\NTLM Authentication' SID = 'S-1-5-64-10' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-64-14' = [PSCustomObject] @{ Name = 'NT AUTHORITY\SChannel Authentication' SID = 'S-1-5-64-14' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-64-21' = [PSCustomObject] @{ Name = 'NT AUTHORITY\Digest Authentication' SID = 'S-1-5-64-21' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-80' = [PSCustomObject] @{ Name = 'NT SERVICE' SID = 'S-1-5-80' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-83-0' = [PSCustomObject] @{ Name = 'NT VIRTUAL MACHINE\Virtual Machines' SID = 'S-1-5-83-0' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-16-0' = [PSCustomObject] @{ Name = 'Untrusted Mandatory Level' SID = 'S-1-16-0' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-16-4096' = [PSCustomObject] @{ Name = 'Low Mandatory Level' SID = 'S-1-16-4096' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-16-8192' = [PSCustomObject] @{ Name = 'Medium Mandatory Level' SID = 'S-1-16-8192' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-16-8448' = [PSCustomObject] @{ Name = 'Medium Plus Mandatory Level' SID = 'S-1-16-8448' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-16-12288' = [PSCustomObject] @{ Name = 'High Mandatory Level' SID = 'S-1-16-12288' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-16-16384' = [PSCustomObject] @{ Name = 'System Mandatory Level' SID = 'S-1-16-16384' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-16-20480' = [PSCustomObject] @{ Name = 'Protected Process Mandatory Level' SID = 'S-1-16-20480' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-16-28672' = [PSCustomObject] @{ Name = 'Secure Process Mandatory Level' SID = 'S-1-16-28672' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-554' = [PSCustomObject] @{ Name = 'BUILTIN\Pre-Windows 2000 Compatible Access' SID = 'S-1-5-32-554' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-555' = [PSCustomObject] @{ Name = 'BUILTIN\Remote Desktop Users' SID = 'S-1-5-32-555' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-556' = [PSCustomObject] @{ Name = 'BUILTIN\Network Configuration Operators' SID = 'S-1-5-32-556' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-557' = [PSCustomObject] @{ Name = 'BUILTIN\Incoming Forest Trust Builders' SID = 'S-1-5-32-557' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-558' = [PSCustomObject] @{ Name = 'BUILTIN\Performance Monitor Users' SID = 'S-1-5-32-558' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-559' = [PSCustomObject] @{ Name = 'BUILTIN\Performance Log Users' SID = 'S-1-5-32-559' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-560' = [PSCustomObject] @{ Name = 'BUILTIN\Windows Authorization Access Group' SID = 'S-1-5-32-560' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-561' = [PSCustomObject] @{ Name = 'BUILTIN\Terminal Server License Servers' SID = 'S-1-5-32-561' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-562' = [PSCustomObject] @{ Name = 'BUILTIN\Distributed COM Users' SID = 'S-1-5-32-562' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-568' = [PSCustomObject] @{ Name = 'BUILTIN\IIS_IUSRS' SID = 'S-1-5-32-568' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-569' = [PSCustomObject] @{ Name = 'BUILTIN\Cryptographic Operators' SID = 'S-1-5-32-569' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-573' = [PSCustomObject] @{ Name = 'BUILTIN\Event Log Readers' SID = 'S-1-5-32-573' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-574' = [PSCustomObject] @{ Name = 'BUILTIN\Certificate Service DCOM Access' SID = 'S-1-5-32-574' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-575' = [PSCustomObject] @{ Name = 'BUILTIN\RDS Remote Access Servers' SID = 'S-1-5-32-575' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-576' = [PSCustomObject] @{ Name = 'BUILTIN\RDS Endpoint Servers' SID = 'S-1-5-32-576' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-577' = [PSCustomObject] @{ Name = 'BUILTIN\RDS Management Servers' SID = 'S-1-5-32-577' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-578' = [PSCustomObject] @{ Name = 'BUILTIN\Hyper-V Administrators' SID = 'S-1-5-32-578' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-579' = [PSCustomObject] @{ Name = 'BUILTIN\Access Control Assistance Operators' SID = 'S-1-5-32-579' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-32-580' = [PSCustomObject] @{ Name = 'BUILTIN\Remote Management Users' SID = 'S-1-5-32-580' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-90-0' = [PSCustomObject] @{ Name = 'Window Manager\Window Manager Group' SID = 'S-1-5-90-0' DomainName = '' Type = 'WellKnownGroup' Error = '' } 'S-1-5-80-3139157870-2983391045-3678747466-658725712-1809340420' = [PSCustomObject] @{ Name = 'NT SERVICE\WdiServiceHost' SID = 'S-1-5-80-3139157870-2983391045-3678747466-658725712-1809340420' DomainName = '' Type = 'WellKnownGroup' Error = '' } # 'S-1-5-113' = [PSCustomObject] @{ # Name = 'NT AUTHORITY\Local account' # SID = 'S-1-5-113' # DomainName = '' # Type = 'WellKnownGroup' # Error = '' # } # 'S-1-5-114' = [PSCustomObject] @{ # Name = 'NT AUTHORITY\Local account and member of Administrators group' # SID = 'S-1-5-114' # DomainName = '' # Type = 'WellKnownGroup' # Error = '' # } } foreach ($S in $SID) { if ($OnlyWellKnownAdministrative) { # In this case we only return very few high permissions, otherwise nothing if ($WellKnownAdministrative[$S]) { $WellKnownAdministrative[$S] } } elseif ($OnlyWellKnown) { # In this case we only return well known cases, otherwise nothing if ($wellKnownSIDs[$S]) { $wellKnownSIDs[$S] } } else { # In this case we return WellKnown, or try to resolve stuff if ($wellKnownSIDs[$S]) { $wellKnownSIDs[$S] } else { if ($DoNotResolve) { if ($S -like "S-1-5-21-*-519" -or $S -like "S-1-5-21-*-512") { # Domain Admins / Enterprise Admins [PSCustomObject] @{ Name = $S SID = $S DomainName = '' # we don't know from SID which domain it is, without checking LDAP Type = 'Administrative' Error = '' } } else { # Return unchanged object [PSCustomObject] @{ Name = $S SID = $S DomainName = '' Error = '' Type = 'NotAdministrative' } } } else { if (-not $Script:LocalComputerSID) { $Script:LocalComputerSID = Get-LocalComputerSid } try { if ($S.Length -le 18) { $Type = 'NotAdministrative' $Name = (([System.Security.Principal.SecurityIdentifier]::new($S)).Translate([System.Security.Principal.NTAccount])).Value [PSCustomObject] @{ Name = $Name SID = $S DomainName = '' Type = $Type Error = '' } } else { if ($S -like "S-1-5-21-*-519" -or $S -like "S-1-5-21-*-512") { $Type = 'Administrative' } else { $Type = 'NotAdministrative' } $Name = (([System.Security.Principal.SecurityIdentifier]::new($S)).Translate([System.Security.Principal.NTAccount])).Value [PSCustomObject] @{ Name = $Name SID = $S DomainName = if ($S -like "$Script:LocalComputerSID*") { '' } else { (ConvertFrom-NetbiosName -Identity $Name).DomainName } Type = $Type Error = '' } } } catch { # Return unchanged object [PSCustomObject] @{ Name = $S SID = $S DomainName = '' Error = $_.Exception.Message -replace [environment]::NewLine, ' ' Type = 'Unknown' } } } } } } } function ConvertFrom-X500Address { <# .EXAMPLE By default returns string without @evotec.pl in the end. This is because the string from NDR needs domain name removed to be able to add it back to Exchange ConvertFrom-X500Address -IMCEAEXString 'IMCEAEX-_o=AD_ou=Exchange+20Administrative+20Group+20+28FYDIBOHF23SPDLT+29_cn=Recipients_cn=5209048016da47689b4421790ad1763f-EVOTEC+20PL+20Recepcja+20G@evotec.pl' ConvertFrom-X500Address -IMCEAEXString 'IMCEAEX-_o=AD_ou=Exchange+20Administrative+20Group+20+28FYDIBOHF23SPDLT+29_cn=Recipients_cn=8bcad655e07c46788fe1f796162cd87f-EVOTEC+20PL+20Recepcja+20G@evotec.pl' ConvertFrom-X500Address -IMCEAEXString 'IMCEAEX-_o=AD_ou=Exchange+20Administrative+20Group+20+28FYDIBOHF23SPDLT+29_cn=Recipients_cn=0d4540e9a8f845d798625c9c0ad753bf-Test-All-Group@evotec.pl' ConvertFrom-X500Address -IMCEAEXString 'IMCEAEX-_o=AD_ou=Exchange+20Administrative+20Group+20+28FYDIBOHF23SPDLT+29_cn=Recipients_cn=0d4540e9a8f845d798625c9c0ad753bf-Test-All-Group@evotec.pl' .EXAMPLE ConvertFrom-X500Address -IMCEAEXString 'IMCEAEX-_o=AD_ou=Exchange+20Administrative+20Group+20+28FYDIBOHF23SPDLT+29_cn=Recipients_cn=5209048016da47689b4421790ad1763f-EVOTEC+20PL+20Recepcja+20G@evotec.pl' -Full ConvertFrom-X500Address -IMCEAEXString 'IMCEAEX-_o=AD_ou=Exchange+20Administrative+20Group+20+28FYDIBOHF23SPDLT+29_cn=Recipients_cn=8bcad655e07c46788fe1f796162cd87f-EVOTEC+20PL+20Recepcja+20G@evotec.pl' -Full ConvertFrom-X500Address -IMCEAEXString 'IMCEAEX-_o=AD_ou=Exchange+20Administrative+20Group+20+28FYDIBOHF23SPDLT+29_cn=Recipients_cn=0d4540e9a8f845d798625c9c0ad753bf-Test-All-Group@evotec.pl' -Full ConvertFrom-X500Address -IMCEAEXString 'IMCEAEX-_o=AD_ou=Exchange+20Administrative+20Group+20+28FYDIBOHF23SPDLT+29_cn=Recipients_cn=0d4540e9a8f845d798625c9c0ad753bf-Test-All-Group@evotec.pl' -Full #> param( [string] $IMCEAEXString, [switch] $Full ) $Final = $IMCEAEXString.Replace("IMCEAEX-", "").Replace("_", "/").Replace("+20", " ").Replace("+28", "(").Replace("+29", ")").Replace("+2E", ".").Replace("+2C", ",").Replace("+5F", "_") if ($Full) { return $Final } else { return ($Final -split '@')[0] } } function ConvertTo-DistinguishedName { <# .SYNOPSIS Converts CanonicalName to DistinguishedName .DESCRIPTION Converts CanonicalName to DistinguishedName for 3 different options .PARAMETER CanonicalName One or multiple canonical names .PARAMETER ToOU Converts CanonicalName to OrganizationalUnit DistinguishedName .PARAMETER ToObject Converts CanonicalName to Full Object DistinguishedName .PARAMETER ToDomain Converts CanonicalName to Domain DistinguishedName .EXAMPLE $CanonicalObjects = @( 'ad.evotec.xyz/Production/Groups/Security/ITR03_AD Admins' 'ad.evotec.xyz/Production/Accounts/Special/SADM Testing 2' ) $CanonicalOU = @( 'ad.evotec.xyz/Production/Groups/Security/NetworkAdministration' 'ad.evotec.xyz/Production' ) $CanonicalDomain = @( 'ad.evotec.xyz/Production/Groups/Security/ITR03_AD Admins' 'ad.evotec.pl' 'ad.evotec.xyz' 'test.evotec.pl' 'ad.evotec.xyz/Production' ) $CanonicalObjects | ConvertTo-DistinguishedName -ToObject $CanonicalOU | ConvertTo-DistinguishedName -ToOU $CanonicalDomain | ConvertTo-DistinguishedName -ToDomain Output: CN=ITR03_AD Admins,OU=Security,OU=Groups,OU=Production,DC=ad,DC=evotec,DC=xyz CN=SADM Testing 2,OU=Special,OU=Accounts,OU=Production,DC=ad,DC=evotec,DC=xyz Output2: OU=NetworkAdministration,OU=Security,OU=Groups,OU=Production,DC=ad,DC=evotec,DC=xyz OU=Production,DC=ad,DC=evotec,DC=xyz Output3: DC=ad,DC=evotec,DC=xyz DC=ad,DC=evotec,DC=pl DC=ad,DC=evotec,DC=xyz DC=test,DC=evotec,DC=pl DC=ad,DC=evotec,DC=xyz .NOTES General notes #> [cmdletBinding(DefaultParameterSetName = 'ToDomain')] param( [Parameter(ParameterSetName = 'ToOU')] [Parameter(ParameterSetName = 'ToObject')] [Parameter(ParameterSetName = 'ToDomain')] [alias('Identity', 'CN')][Parameter(ValueFromPipeline, Mandatory, ValueFromPipelineByPropertyName, Position = 0)][string[]] $CanonicalName, [Parameter(ParameterSetName = 'ToOU')][switch] $ToOU, [Parameter(ParameterSetName = 'ToObject')][switch] $ToObject, [Parameter(ParameterSetName = 'ToDomain')][switch] $ToDomain ) Process { foreach ($CN in $CanonicalName) { if ($ToObject) { $ADObject = $CN.Replace(',', '\,').Split('/') [string]$DN = "CN=" + $ADObject[$ADObject.count - 1] for ($i = $ADObject.count - 2; $i -ge 1; $i--) { $DN += ",OU=" + $ADObject[$i] } $ADObject[0].split(".") | ForEach-Object { $DN += ",DC=" + $_ } } elseif ($ToOU) { $ADObject = $CN.Replace(',', '\,').Split('/') [string]$DN = "OU=" + $ADObject[$ADObject.count - 1] for ($i = $ADObject.count - 2; $i -ge 1; $i--) { $DN += ",OU=" + $ADObject[$i] } $ADObject[0].split(".") | ForEach-Object { $DN += ",DC=" + $_ } } else { $ADObject = $CN.Replace(',', '\,').Split('/') # Assemble the DN by replacing $DN = 'DC=' + $ADObject[0].Replace('.', ',DC=') } $DN } } } function ConvertTo-FlatHashtable { <# .SYNOPSIS Converts nested hashtable into flat hashtable using delimiter .DESCRIPTION Converts nested hashtable into flat hashtable using delimiter .PARAMETER InputObject Ordered Dictionary or Hashtable .PARAMETER Delimiter Delimiter for key name when merging nested hashtables. By default colon is used .EXAMPLE ConvertTo-FlatHashTable -InputObject ([ordered] @{ RootEntry = 'OK1' Parent = @{ Child1 = 'OK2' Child2 = 'Ok3' } ParentDifferent = @{ Child7 = 'NotOk' Child8 = @{ Child10 = 'OKLetsSee' Child11 = @{ SpecialCase = 'Oooop' } } } }) | Format-Table * .NOTES General notes #> [CmdletBinding()] param( [System.Collections.IDictionary] $InputObject, [string] $Delimiter = ':', [Parameter(DontShow)][string] $Name ) Begin { $Output = [ordered] @{} } Process { foreach ($Key in $InputObject.Keys) { if ($Name) { $MergedName = "$Name$($Delimiter)$Key" } else { $MergedName = $Key } if ($InputObject[$Key] -is [System.Collections.IDictionary]) { $Found = ConvertTo-FlatHashtable -InputObject $InputObject[$Key] -Name $MergedName $Output = $Output + $Found } else { $Output[$MergedName] = $InputObject[$Key] } } } End { $Output } } Function ConvertTo-FlatObject { <# .SYNOPSIS Flattends a nested object into a single level object. .DESCRIPTION Flattends a nested object into a single level object. .PARAMETER Objects The object (or objects) to be flatten. .PARAMETER Separator The separator used between the recursive property names .PARAMETER Base The first index name of an embedded array: - 1, arrays will be 1 based: <Parent>.1, <Parent>.2, <Parent>.3, … - 0, arrays will be 0 based: <Parent>.0, <Parent>.1, <Parent>.2, … - "", the first item in an array will be unnamed and than followed with 1: <Parent>, <Parent>.1, <Parent>.2, … .PARAMETER Depth The maximal depth of flattening a recursive property. Any negative value will result in an unlimited depth and could cause a infinitive loop. .PARAMETER Uncut The maximal depth of flattening a recursive property. Any negative value will result in an unlimited depth and could cause a infinitive loop. .PARAMETER ExcludeProperty The propertys to be excluded from the output. .EXAMPLE $Object3 = [PSCustomObject] @{ "Name" = "Przemyslaw Klys" "Age" = "30" "Address" = @{ "Street" = "Kwiatowa" "City" = "Warszawa" "Country" = [ordered] @{ "Name" = "Poland" } List = @( [PSCustomObject] @{ "Name" = "Adam Klys" "Age" = "32" } [PSCustomObject] @{ "Name" = "Justyna Klys" "Age" = "33" } [PSCustomObject] @{ "Name" = "Justyna Klys" "Age" = 30 } [PSCustomObject] @{ "Name" = "Justyna Klys" "Age" = $null } ) } ListTest = @( [PSCustomObject] @{ "Name" = "Sława Klys" "Age" = "33" } ) } $Object3 | ConvertTo-FlatObject .NOTES Based on https://powersnippets.com/convertto-flatobject/ #> [CmdletBinding()] Param ( [Parameter(ValueFromPipeLine)][Object[]]$Objects, [String]$Separator = ".", [ValidateSet("", 0, 1)]$Base = 1, [int]$Depth = 5, [string[]] $ExcludeProperty, [Parameter(DontShow)][String[]]$Path, [Parameter(DontShow)][System.Collections.IDictionary] $OutputObject ) Begin { $InputObjects = [System.Collections.Generic.List[Object]]::new() } Process { foreach ($O in $Objects) { if ($null -ne $O) { $InputObjects.Add($O) } } } End { If ($PSBoundParameters.ContainsKey("OutputObject")) { $Object = $InputObjects[0] $Iterate = [ordered] @{} if ($null -eq $Object) { #Write-Verbose -Message "ConvertTo-FlatObject - Object is null" } elseif ($Object.GetType().Name -in 'String', 'DateTime', 'TimeSpan', 'Version', 'Enum') { $Object = $Object.ToString() } elseif ($Depth) { $Depth-- If ($Object -is [System.Collections.IDictionary]) { $Iterate = $Object } elseif ($Object -is [Array] -or $Object -is [System.Collections.IEnumerable]) { $i = $Base foreach ($Item in $Object.GetEnumerator()) { $NewObject = [ordered] @{} If ($Item -is [System.Collections.IDictionary]) { foreach ($Key in $Item.Keys) { if ($Key -notin $ExcludeProperty) { $NewObject[$Key] = $Item[$Key] } } } elseif ($Item -isnot [Array] -and $Item -isnot [System.Collections.IEnumerable]) { foreach ($Prop in $Item.PSObject.Properties) { if ($Prop.IsGettable -and $Prop.Name -notin $ExcludeProperty) { $NewObject["$($Prop.Name)"] = $Item.$($Prop.Name) } } } else { $NewObject = $Item } $Iterate["$i"] = $NewObject $i += 1 } } else { foreach ($Prop in $Object.PSObject.Properties) { if ($Prop.IsGettable -and $Prop.Name -notin $ExcludeProperty) { $Iterate["$($Prop.Name)"] = $Object.$($Prop.Name) } } } } If ($Iterate.Keys.Count) { foreach ($Key in $Iterate.Keys) { if ($Key -notin $ExcludeProperty) { ConvertTo-FlatObject -Objects @(, $Iterate["$Key"]) -Separator $Separator -Base $Base -Depth $Depth -Path ($Path + $Key) -OutputObject $OutputObject -ExcludeProperty $ExcludeProperty } } } else { $Property = $Path -Join $Separator if ($Property) { # We only care if property is not empty if ($Object -is [System.Collections.IDictionary] -and $Object.Keys.Count -eq 0) { $OutputObject[$Property] = $null } else { $OutputObject[$Property] = $Object } } } } elseif ($InputObjects.Count -gt 0) { foreach ($ItemObject in $InputObjects) { $OutputObject = [ordered]@{} ConvertTo-FlatObject -Objects @(, $ItemObject) -Separator $Separator -Base $Base -Depth $Depth -Path $Path -OutputObject $OutputObject -ExcludeProperty $ExcludeProperty [PSCustomObject] $OutputObject } } } } function ConvertTo-Identity { [cmdletBinding()] param( [string] $Identity, [System.Collections.IDictionary] $ADAdministrativeGroups, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation ) Begin { if (-not $ExtendedForestInformation) { $ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation } else { $ForestInformation = $ExtendedForestInformation } if (-not $ADAdministrativeGroups) { $ADAdministrativeGroups = Get-ADADministrativeGroups -Type DomainAdmins, EnterpriseAdmins -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation } if (-not $Script:GlobalCacheIdentity) { $Script:GlobalCacheIdentity = @{ } } } Process { $AdministrativeGroup = $ADAdministrativeGroups['ByNetBIOS']["$($Identity)"] if ($AdministrativeGroup) { [PSCustomObject] @{ Name = $Identity SID = $AdministrativeGroup.SID.Value Type = 'Administrative' Class = $AdministrativeGroup.ObjectClass Error = '' } } else { if ($Identity -like '*@*') { Write-Warning "ConvertTo-Identity - Not implemented." #if ($Script:GlobalCacheIdentity[$Identity]) { # $Script:GlobalCacheIdentity[$Identity] # } else { # # $ADObject = Get-ADObject -Filter "SamAccountName -eq '$($MyIdentity[1])'" -Server $QueryServer -Properties AdminCount, CanonicalName, Name, sAMAccountName, DisplayName, DistinguishedName, ObjectClass, objectSid #} } elseif ($Identity -like '*\*') { if ($Script:GlobalCacheIdentity[$Identity]) { $Script:GlobalCacheIdentity[$Identity] } else { $MyIdentity = $Identity.Split("\") $DNSRoot = $ForestInformation['DomainsExtendedNetBIOS'][$($MyIdentity[0])]['DNSRoot'] $QueryServer = $ForestInformation['QueryServers'][$DNSRoot]['HostName'][0] $ADObject = Get-ADObject -Filter "SamAccountName -eq '$($MyIdentity[1])'" -Server $QueryServer -Properties AdminCount, CanonicalName, Name, sAMAccountName, DisplayName, DistinguishedName, ObjectClass, objectSid <# AdminCount : 1 CanonicalName : ad.evotec.xyz/Production/Users/Przemysław Kłys DisplayName : Przemysław Kłys DistinguishedName : CN=Przemysław Kłys,OU=Users,OU=Production,DC=ad,DC=evotec,DC=xyz Name : Przemysław Kłys ObjectClass : user ObjectGUID : b328932a-857f-4af1-b9d0-35578aa20d22 objectSid : S-1-5-21-853615985-2870445339-3163598659-1105 sAMAccountName : przemyslaw.klys #> if ($ADObject) { $Script:GlobalCacheIdentity[$Identity] = [PSCustomObject] @{ Name = $Identity SID = $ADObject.objectSid.Value Type = 'NotAdministrative' Class = $AdObject.ObjectClass Error = '' } $Script:GlobalCacheIdentity[$Identity] } else { [PSCustomObject] @{ Name = $Identity SID = $Identity Type = 'Unknown' Class = 'unknown' Error = 'Object not found.' } } } # } elseif ($Identity -like '*-*-*-*') { $Data = ConvertFrom-SID -SID $Identity if ($Data) { if ($Data.Error) { [PSCustomObject] @{ Name = $Data.Name SID = $Data.Sid Type = $Data.Type Class = 'unknown' Error = $Data.Error } } else { # If it's not an error we still need to check whether translated SID is administrative or not $AdministrativeGroup = $ADAdministrativeGroups['ByNetBIOS']["$($Data.Name)"] if ($AdministrativeGroup) { [PSCustomObject] @{ Name = $Data.Name SID = $AdministrativeGroup.SID.Value Type = 'Administrative' Class = $AdministrativeGroup.ObjectClass Error = '' } } else { [PSCustomObject] @{ Name = $Data.Name SID = $Data.Sid Type = $Data.Type Class = '' Error = $Data.Error } } } } else { [PSCustomObject] @{ Name = $Identity SID = $Identity Type = 'Unknown' Class = 'unknown' Error = 'SID not found' } } <# Name SID Error Type ---- --- ----- ---- 1-2-3 1-2-3 Exception calling ".ctor" with "1" argument(s): "Value was invalid.... Unknown #> } else { [PSCustomObject] @{ Name = $Identity SID = $Identity Type = 'Unknown' Class = 'unknown' Error = 'Identity unknown' } } } } End { } } 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 ConvertTo-JsonLiteral { <# .SYNOPSIS Converts an object to a JSON-formatted string. .DESCRIPTION The ConvertTo-Json cmdlet converts any object to a string in JavaScript Object Notation (JSON) format. The properties are converted to field names, the field values are converted to property values, and the methods are removed. .PARAMETER Object Specifies the objects to convert to JSON format. Enter a variable that contains the objects, or type a command or expression that gets the objects. You can also pipe an object to ConvertTo-JsonLiteral .PARAMETER Depth Specifies how many levels of contained objects are included in the JSON representation. The default value is 0. .PARAMETER AsArray Outputs the object in array brackets, even if the input is a single object. .PARAMETER DateTimeFormat Changes DateTime string format. Default "yyyy-MM-dd HH:mm:ss" .PARAMETER NumberAsString Provides an alternative serialization option that converts all numbers to their string representation. .PARAMETER BoolAsString Provides an alternative serialization option that converts all bool to their string representation. .PARAMETER PropertyName Uses PropertyNames provided by user (only works with Force) .PARAMETER NewLineFormat Provides a way to configure how new lines are converted for property names .PARAMETER NewLineFormatProperty Provides a way to configure how new lines are converted for values .PARAMETER PropertyName Allows passing property names to be used for custom objects (hashtables and alike are unaffected) .PARAMETER ArrayJoin Forces any array to be a string regardless of depth level .PARAMETER ArrayJoinString Uses defined string or char for array join. By default it uses comma with a space when used. .PARAMETER Force Forces using property names from first object or given thru PropertyName parameter .EXAMPLE Get-Process | Select-Object -First 2 | ConvertTo-JsonLiteral .EXAMPLE Get-Process | Select-Object -First 2 | ConvertTo-JsonLiteral -Depth 3 .EXAMPLE Get-Process | Select-Object -First 2 | ConvertTo-JsonLiteral -NewLineFormat $NewLineFormat = @{ NewLineCarriage = '\r\n' NewLine = "\n" Carriage = "\r" } -NumberAsString -BoolAsString .EXAMPLE Get-Process | Select-Object -First 2 | ConvertTo-JsonLiteral -NumberAsString -BoolAsString -DateTimeFormat "yyyy-MM-dd HH:mm:ss" .EXAMPLE # Keep in mind this advanced replace will break ConvertFrom-Json, but it's sometimes useful for projects like PSWriteHTML Get-Process | Select-Object -First 2 | ConvertTo-JsonLiteral -NewLineFormat $NewLineFormat = @{ NewLineCarriage = '\r\n' NewLine = "\n" Carriage = "\r" } -NumberAsString -BoolAsString -AdvancedReplace @{ '.' = '\.'; '$' = '\$' } .NOTES General notes #> [cmdletBinding()] param( [alias('InputObject')][Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Position = 0, Mandatory)][Array] $Object, [int] $Depth, [switch] $AsArray, [string] $DateTimeFormat = "yyyy-MM-dd HH:mm:ss", [switch] $NumberAsString, [switch] $BoolAsString, [System.Collections.IDictionary] $NewLineFormat = @{ NewLineCarriage = '\r\n' NewLine = "\n" Carriage = "\r" }, [System.Collections.IDictionary] $NewLineFormatProperty = @{ NewLineCarriage = '\r\n' NewLine = "\n" Carriage = "\r" }, [System.Collections.IDictionary] $AdvancedReplace, [string] $ArrayJoinString, [switch] $ArrayJoin, [string[]]$PropertyName, [switch] $Force ) Begin { $TextBuilder = [System.Text.StringBuilder]::new() $CountObjects = 0 filter IsNumeric() { return $_ -is [byte] -or $_ -is [int16] -or $_ -is [int32] -or $_ -is [int64] ` -or $_ -is [sbyte] -or $_ -is [uint16] -or $_ -is [uint32] -or $_ -is [uint64] ` -or $_ -is [float] -or $_ -is [double] -or $_ -is [decimal] } filter IsOfType() { return $_ -is [bool] -or $_ -is [char] -or $_ -is [datetime] -or $_ -is [string] ` -or $_ -is [timespan] -or $_ -is [URI] ` -or $_ -is [byte] -or $_ -is [int16] -or $_ -is [int32] -or $_ -is [int64] ` -or $_ -is [sbyte] -or $_ -is [uint16] -or $_ -is [uint32] -or $_ -is [uint64] ` -or $_ -is [float] -or $_ -is [double] -or $_ -is [decimal] } [int] $MaxDepth = $Depth [int] $InitialDepth = 0 } Process { for ($a = 0; $a -lt $Object.Count; $a++) { $CountObjects++ if ($CountObjects -gt 1) { $null = $TextBuilder.Append(',') } if ($Object[$a] -is [System.Collections.IDictionary]) { # Push to TEXT the same as [PSCustomObject] $null = $TextBuilder.AppendLine("{") for ($i = 0; $i -lt ($Object[$a].Keys).Count; $i++) { $Property = ([string[]]$Object[$a].Keys)[$i] #.Replace('\', "\\").Replace('"', '\"') $DisplayProperty = $Property.Replace('\', "\\").Replace('"', '\"').Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage) $null = $TextBuilder.Append("`"$DisplayProperty`":") $Value = ConvertTo-StringByType -Value $Object[$a][$Property] -DateTimeFormat $DateTimeFormat -NumberAsString:$NumberAsString -BoolAsString:$BoolAsString -Depth $InitialDepth -MaxDepth $MaxDepth -TextBuilder $TextBuilder -NewLineFormat $NewLineFormat -NewLineFormatProperty $NewLineFormatProperty -Force:$Force -ArrayJoin:$ArrayJoin -ArrayJoinString $ArrayJoinString -AdvancedReplace $AdvancedReplace $null = $TextBuilder.Append("$Value") if ($i -ne ($Object[$a].Keys).Count - 1) { $null = $TextBuilder.AppendLine(',') } } $null = $TextBuilder.Append("}") } elseif ($Object[$a] | IsOfType) { $Value = ConvertTo-StringByType -Value $Object[$a] -DateTimeFormat $DateTimeFormat -NumberAsString:$NumberAsString -BoolAsString:$BoolAsString -Depth $InitialDepth -MaxDepth $MaxDepth -TextBuilder $TextBuilder -NewLineFormat $NewLineFormat -NewLineFormatProperty $NewLineFormatProperty -Force:$Force -ArrayJoin:$ArrayJoin -ArrayJoinString $ArrayJoinString -AdvancedReplace $AdvancedReplace $null = $TextBuilder.Append($Value) } else { $null = $TextBuilder.AppendLine("{") if ($Force -and -not $PropertyName) { $PropertyName = $Object[0].PSObject.Properties.Name } elseif ($Force -and $PropertyName) { } else { $PropertyName = $Object[$a].PSObject.Properties.Name } $PropertyCount = 0 foreach ($Property in $PropertyName) { $PropertyCount++ $DisplayProperty = $Property.Replace('\', "\\").Replace('"', '\"').Replace([System.Environment]::NewLine, $NewLineFormatProperty.NewLineCarriage).Replace("`n", $NewLineFormatProperty.NewLine).Replace("`r", $NewLineFormatProperty.Carriage) $null = $TextBuilder.Append("`"$DisplayProperty`":") $Value = ConvertTo-StringByType -Value $Object[$a].$Property -DateTimeFormat $DateTimeFormat -NumberAsString:$NumberAsString -BoolAsString:$BoolAsString -Depth $InitialDepth -MaxDepth $MaxDepth -TextBuilder $TextBuilder -NewLineFormat $NewLineFormat -NewLineFormatProperty $NewLineFormatProperty -Force:$Force -ArrayJoin:$ArrayJoin -ArrayJoinString $ArrayJoinString -AdvancedReplace $AdvancedReplace # Push to Text $null = $TextBuilder.Append("$Value") if ($PropertyCount -ne $PropertyName.Count) { $null = $TextBuilder.AppendLine(',') } } $null = $TextBuilder.Append("}") } $InitialDepth = 0 } } End { if ($CountObjects -gt 1 -or $AsArray) { "[$($TextBuilder.ToString())]" } else { $TextBuilder.ToString() } } } function ConvertTo-OperatingSystem { <# .SYNOPSIS Allows easy conversion of OperatingSystem, Operating System Version to proper Windows 10 naming based on WMI or AD .DESCRIPTION Allows easy conversion of OperatingSystem, Operating System Version to proper Windows 10 naming based on WMI or AD .PARAMETER OperatingSystem Operating System as returned by Active Directory .PARAMETER OperatingSystemVersion Operating System Version as returned by Active Directory .EXAMPLE $Computers = Get-ADComputer -Filter * -Properties OperatingSystem, OperatingSystemVersion | ForEach-Object { $OPS = ConvertTo-OperatingSystem -OperatingSystem $_.OperatingSystem -OperatingSystemVersion $_.OperatingSystemVersion Add-Member -MemberType NoteProperty -Name 'OperatingSystemTranslated' -Value $OPS -InputObject $_ -Force $_ } $Computers | Select-Object DNS*, Name, SamAccountName, Enabled, OperatingSystem*, DistinguishedName | Format-Table .EXAMPLE $Registry = Get-PSRegistry -ComputerName 'AD1' -RegistryPath 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion' ConvertTo-OperatingSystem -OperatingSystem $Registry.ProductName -OperatingSystemVersion $Registry.CurrentBuildNumber .NOTES General notes #> [CmdletBinding()] param( [string] $OperatingSystem, [string] $OperatingSystemVersion ) if ($OperatingSystem -like 'Windows 10*' -or $OperatingSystem -like 'Windows 11*') { $Systems = @{ # This is how it's written in AD '10.0 (22621)' = 'Windows 11 22H2' '10.0 (22000)' = 'Windows 11 21H2' '10.0 (19045)' = 'Windows 10 22H2' '10.0 (19044)' = 'Windows 10 21H2' '10.0 (19043)' = 'Windows 10 21H1' '10.0 (19042)' = 'Windows 10 20H2' '10.0 (19041)' = 'Windows 10 2004' '10.0 (18898)' = 'Windows 10 Insider Preview' '10.0 (18363)' = "Windows 10 1909" '10.0 (18362)' = "Windows 10 1903" '10.0 (17763)' = "Windows 10 1809" '10.0 (17134)' = "Windows 10 1803" '10.0 (16299)' = "Windows 10 1709" '10.0 (15063)' = "Windows 10 1703" '10.0 (14393)' = "Windows 10 1607" '10.0 (10586)' = "Windows 10 1511" '10.0 (10240)' = "Windows 10 1507" # This is how WMI/CIM stores it '10.0.22621' = 'Windows 11 22H2' '10.0.22000' = 'Windows 11 21H2' '10.0.19045' = 'Windows 10 22H2' '10.0.19044' = 'Windows 10 21H2' '10.0.19043' = 'Windows 10 21H1' '10.0.19042' = 'Windows 10 20H2' '10.0.19041' = 'Windows 10 2004' '10.0.18898' = 'Windows 10 Insider Preview' '10.0.18363' = "Windows 10 1909" '10.0.18362' = "Windows 10 1903" '10.0.17763' = "Windows 10 1809" '10.0.17134' = "Windows 10 1803" '10.0.16299' = "Windows 10 1709" '10.0.15063' = "Windows 10 1703" '10.0.14393' = "Windows 10 1607" '10.0.10586' = "Windows 10 1511" '10.0.10240' = "Windows 10 1507" # This is how it's written in registry '22621' = 'Windows 11 22H2' '22000' = 'Windows 11 21H2' '19045' = 'Windows 10 22H2' '19044' = 'Windows 10 21H2' '19043' = 'Windows 10 21H1' '19042' = 'Windows 10 20H2' '19041' = 'Windows 10 2004' '18898' = 'Windows 10 Insider Preview' '18363' = "Windows 10 1909" '18362' = "Windows 10 1903" '17763' = "Windows 10 1809" '17134' = "Windows 10 1803" '16299' = "Windows 10 1709" '15063' = "Windows 10 1703" '14393' = "Windows 10 1607" '10586' = "Windows 10 1511" '10240' = "Windows 10 1507" } $System = $Systems[$OperatingSystemVersion] if (-not $System) { $System = $OperatingSystemVersion } } elseif ($OperatingSystem -like 'Windows Server*') { # May need updates https://docs.microsoft.com/en-us/windows-server/get-started/windows-server-release-info # to detect Core $Systems = @{ # This is how it's written in AD '10.0 (20348)' = 'Windows Server 2022' '10.0 (19042)' = 'Windows Server 2019 20H2' '10.0 (19041)' = 'Windows Server 2019 2004' '10.0 (18363)' = 'Windows Server 2019 1909' '10.0 (18362)' = "Windows Server 2019 1903" # (Datacenter Core, Standard Core) '10.0 (17763)' = "Windows Server 2019 1809" # (Datacenter, Essentials, Standard) '10.0 (17134)' = "Windows Server 2016 1803" # (Datacenter, Standard) '10.0 (14393)' = "Windows Server 2016 1607" '6.3 (9600)' = 'Windows Server 2012 R2' '6.1 (7601)' = 'Windows Server 2008 R2' '5.2 (3790)' = 'Windows Server 2003' # This is how WMI/CIM stores it '10.0.20348' = 'Windows Server 2022' '10.0.19042' = 'Windows Server 2019 20H2' '10.0.19041' = 'Windows Server 2019 2004' '10.0.18363' = 'Windows Server 2019 1909' '10.0.18362' = "Windows Server 2019 1903" # (Datacenter Core, Standard Core) '10.0.17763' = "Windows Server 2019 1809" # (Datacenter, Essentials, Standard) '10.0.17134' = "Windows Server 2016 1803" ## (Datacenter, Standard) '10.0.14393' = "Windows Server 2016 1607" '6.3.9600' = 'Windows Server 2012 R2' '6.1.7601' = 'Windows Server 2008 R2' # i think '5.2.3790' = 'Windows Server 2003' # i think # This is how it's written in registry '20348' = 'Windows Server 2022' '19042' = 'Windows Server 2019 20H2' '19041' = 'Windows Server 2019 2004' '18363' = 'Windows Server 2019 1909' '18362' = "Windows Server 2019 1903" # (Datacenter Core, Standard Core) '17763' = "Windows Server 2019 1809" # (Datacenter, Essentials, Standard) '17134' = "Windows Server 2016 1803" # (Datacenter, Standard) '14393' = "Windows Server 2016 1607" '9600' = 'Windows Server 2012 R2' '7601' = 'Windows Server 2008 R2' '3790' = 'Windows Server 2003' } $System = $Systems[$OperatingSystemVersion] if (-not $System) { $System = $OperatingSystemVersion } } else { $System = $OperatingSystem } if ($System) { $System } else { 'Unknown' } } <# $Computers = Get-ADComputer -Filter * -Properties OperatingSystem, OperatingSystemVersion | ForEach-Object { $OPS = ConvertTo-OperatingSystem -OperatingSystem $_.OperatingSystem -OperatingSystemVersion $_.OperatingSystemVersion Add-Member -MemberType NoteProperty -Name 'OperatingSystemTranslated' -Value $OPS -InputObject $_ -Force $_ } $Computers | Select-Object DNS*, Name, SamAccountName, Enabled, OperatingSystem*, DistinguishedName | Format-Table #> function ConvertTo-OrderedDictionary { [CmdletBinding()] Param ( [parameter(Mandatory = $true, ValueFromPipeline = $true)] $HashTable ) # $TimeToGenerate = Start-TimeLog $OrderedDictionary = [ordered]@{ } if ($HashTable -is [System.Collections.IDictionary]) { $Keys = $HashTable.Keys | Sort-Object foreach ($_ in $Keys) { $OrderedDictionary.Add($_, $HashTable[$_]) } } elseif ($HashTable -is [System.Collections.ICollection]) { for ($i = 0; $i -lt $HashTable.count; $i++) { $OrderedDictionary.Add($i, $HashTable[$i]) } } else { Write-Error "ConvertTo-OrderedDictionary - Wrong input type." } # $EndTime = Stop-TimeLog -Time $TimeToGenerate #Write-Verbose "ConvertTo-OrderedDictionary - Time to convert: $EndTime" return $OrderedDictionary } function ConvertTo-SID { [cmdletBinding()] param( [string[]] $Identity ) Begin { if (-not $Script:GlobalCacheSidConvert) { $Script:GlobalCacheSidConvert = @{ } } } Process { foreach ($Ident in $Identity) { if ($Script:GlobalCacheSidConvert[$Ident]) { $Script:GlobalCacheSidConvert[$Ident] } else { try { $Script:GlobalCacheSidConvert[$Ident] = [PSCustomObject] @{ Name = $Ident Sid = ([System.Security.Principal.NTAccount] $Ident).Translate([System.Security.Principal.SecurityIdentifier]).Value Error = '' } } catch { [PSCustomObject] @{ Name = $Ident Sid = '' Error = $_.Exception.Message } } $Script:GlobalCacheSidConvert[$Ident] } } } End { } } 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 Set-DnsServerIpAddress { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER ComputerName ComputerName/ServerName where to Set DNS Server .PARAMETER NicName Service is the name of the Network Card (takes wildcard) .PARAMETER IpAddresses IpAddresses can be one or more values .EXAMPLE Set-DnsServerIpAddress -ComputerName $ServerName -NicName "Service*" -IpAddresses '8.8.8.8','8.8.4.4','8.8.8.1' .NOTES Probably needs a rewrite #> [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-Warning "Set-DnsServerIpAddress - Can't access $ComputerName. Computer is not online." } } function Get-HTML { [CmdletBinding()] param ( $text ) $text = $text.Split("`r") foreach ($t in $text) { Write-Host $t } } function Send-Email { [CmdletBinding(SupportsShouldProcess = $true)] param ( [alias('EmailParameters')][System.Collections.IDictionary] $Email, [string] $Body, [string[]] $Attachment, [System.Collections.IDictionary] $InlineAttachments, [string] $Subject, [string[]] $To, [PSCustomObject] $Logger ) try { # Following code makes sure both formats are accepted. if ($Email.EmailTo) { $EmailParameters = $Email.Clone() $EmailParameters.EmailEncoding = $EmailParameters.EmailEncoding -replace "-", '' $EmailParameters.EmailEncodingSubject = $EmailParameters.EmailEncodingSubject -replace "-", '' $EmailParameters.EmailEncodingBody = $EmailParameters.EmailEncodingSubject -replace "-", '' $EmailParameters.EmailEncodingAlternateView = $EmailParameters.EmailEncodingAlternateView -replace "-", '' } else { $EmailParameters = @{ EmailFrom = $Email.From EmailTo = $Email.To EmailCC = $Email.CC EmailBCC = $Email.BCC EmailReplyTo = $Email.ReplyTo EmailServer = $Email.Server EmailServerPassword = $Email.Password EmailServerPasswordAsSecure = $Email.PasswordAsSecure EmailServerPasswordFromFile = $Email.PasswordFromFile EmailServerPort = $Email.Port EmailServerLogin = $Email.Login EmailServerEnableSSL = $Email.EnableSsl EmailEncoding = $Email.Encoding -replace "-", '' EmailEncodingSubject = $Email.EncodingSubject -replace "-", '' EmailEncodingBody = $Email.EncodingBody -replace "-", '' EmailEncodingAlternateView = $Email.EncodingAlternateView -replace "-", '' EmailSubject = $Email.Subject EmailPriority = $Email.Priority EmailDeliveryNotifications = $Email.DeliveryNotifications EmailUseDefaultCredentials = $Email.UseDefaultCredentials # EmailAlternativeClient = $Email.AlternativeClient } } } catch { return @{ Status = $False Error = $($_.Exception.Message) SentTo = '' } } $SmtpClient = [System.Net.Mail.SmtpClient]::new() 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) { $Credentials = Request-Credentials -UserName $EmailParameters.EmailServerLogin ` -Password $EmailParameters.EmailServerPassword ` -AsSecure:$EmailParameters.EmailServerPasswordAsSecure ` -FromFile:$EmailParameters.EmailServerPasswordFromFile ` -NetworkCredentials #-Verbose $SmtpClient.Credentials = $Credentials } if ($EmailParameters.EmailServerEnableSSL) { $SmtpClient.EnableSsl = $EmailParameters.EmailServerEnableSSL } $MailMessage = [System.Net.Mail.MailMessage]::new() $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) { foreach ($CC in $EmailParameters.EmailCC) { $MailMessage.CC.add($($CC)) } } if ($EmailParameters.EmailBCC) { foreach ($BCC in $EmailParameters.EmailBCC) { $MailMessage.BCC.add($($BCC)) } } if ($EmailParameters.EmailReplyTo) { $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 if ($EmailParameters.EmailEncodingSubject) { $MailMessage.SubjectEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncodingSubject) } elseif ($EmailParameters.EmailEncoding) { $MailMessage.SubjectEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncoding) } if ($EmailParameters.EmailEncodingBody) { $MailMessage.BodyEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncodingBody) } elseif ($EmailParameters.EmailEncoding) { $MailMessage.BodyEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncoding) } #$MailMessage.BodyTransferEncoding = [System.Net.Mime.TransferEncoding]::QuotedPrintable if ($EmailParameters.EmailUseDefaultCredentials) { $SmtpClient.UseDefaultCredentials = $EmailParameters.EmailUseDefaultCredentials } if ($EmailParameters.EmailDeliveryNotifications) { $MailMessage.DeliveryNotificationOptions = $EmailParameters.EmailDeliveryNotifications } # Inlining attachment (s) if ($PSBoundParameters.ContainsKey('InlineAttachments')) { # having any other encoding here caused Thunderbird to play weird things if ($EmailParameters.EmailEncodingAlternateView) { $BodyPart = [Net.Mail.AlternateView]::CreateAlternateViewFromString($Body, [System.Text.Encoding]::$($EmailParameters.EmailEncodingAlternateView) , 'text/html' ) } else { $BodyPart = [Net.Mail.AlternateView]::CreateAlternateViewFromString($Body, [System.Text.Encoding]::UTF8, 'text/html' ) } <# if ($EmailParameters.EmailEncodingBody) { $BodyPart = [Net.Mail.AlternateView]::CreateAlternateViewFromString($Body, [System.Text.Encoding]::$($EmailParameters.EmailEncodingBody), 'text/html' ) } elseif ($EmailParameters.EmailEncoding) { $BodyPart = [Net.Mail.AlternateView]::CreateAlternateViewFromString($Body, [System.Text.Encoding]::$($EmailParameters.EmailEncoding), 'text/html' ) } else { $BodyPart = [Net.Mail.AlternateView]::CreateAlternateViewFromString($Body, 'text/html' ) } #> #$BodyPart.TransferEncoding = [System.Net.Mime.TransferEncoding]::QuotedPrintable $MailMessage.AlternateViews.Add($BodyPart) foreach ($Entry in $InlineAttachments.GetEnumerator()) { try { $FilePath = $Entry.Value Write-Verbose $FilePath if ($Entry.Value.StartsWith('http', [System.StringComparison]::CurrentCultureIgnoreCase)) { $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 = [Net.Mail.LinkedResource]::new($FilePath, $ContentType ) $InAttachment.ContentId = $Entry.Key $BodyPart.LinkedResources.Add( $InAttachment ) } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " Write-Error "Error inlining attachments: $ErrorMessage" } } } else { $MailMessage.Body = $Body } # Attaching file (s) if ($PSBoundParameters.ContainsKey('Attachment')) { foreach ($Attach in $Attachment) { if (Test-Path -LiteralPath $Attach) { try { $File = [Net.Mail.Attachment]::new($Attach) #Write-Verbose "Send-Email - Attaching file $Attach" $MailMessage.Attachments.Add($File) } catch { # non critical error $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " if ($Logger) { $Logger.AddErrorRecord("Error attaching file $Attach`: $ErrorMessage") } else { Write-Error "Error attaching file $Attach`: $ErrorMessage" } } } } } # 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 [PSCustomObject] @{ Status = $True Error = "" SentTo = $MailSentTo } } else { return [PSCustomObject] @{ Status = $False Error = 'Email not sent (WhatIf)' SentTo = $MailSentTo } } } catch { $MailMessage.Dispose(); return [PSCustomObject] @{ Status = $False Error = $($_.Exception.Message) SentTo = "" } } } function Set-EmailBody { [CmdletBinding()] param( [Object] $TableData, [alias('TableWelcomeMessage')][string] $TableMessageWelcome, [string] $TableMessageNoData = 'No changes happened during that period.' ) $Body = "<p><i><u>$TableMessageWelcome</u></i></p>" if ($($TableData | Measure-Object).Count -gt 0) { $Body += $TableData | ConvertTo-Html -Fragment | Out-String # $Body += "</p>" } else { $Body += "<p><i>$TableMessageNoData</i></p>" } return $body } function Set-EmailBodyPreparedTable ($TableData, $TableWelcomeMessage) { $body = "<p><i><u>$TableWelcomeMessage</u></i></p>" $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 { [CmdletBinding()] [alias('Set-EmailBodyTableReplacement')] param ( [string] $Body, [string] $TableName, [Array] $TableData ) $TableData = $TableData | ConvertTo-Html -Fragment | Out-String $Body = $Body -replace "<<$TableName>>", $TableData return $Body } function Set-EmailFormatting { [CmdletBinding()] param ( $Template, [System.Collections.IDictionary] $FormattingParameters, [System.Collections.IDictionary] $ConfigurationParameters, [PSCustomObject] $Logger, [switch] $SkipNewLines, [string[]] $AddAfterOpening, [string[]] $AddBeforeClosing, [string] $Image ) if ($ConfigurationParameters) { $WriteParameters = $ConfigurationParameters.DisplayConsole } else { $WriteParameters = @{ ShowTime = $true; LogFile = ""; TimeFormat = "yyyy-MM-dd HH:mm:ss" } } if ($Image) { $Template = $Template -replace '<<Image>>', $Image } $Body = "<body>" if ($AddAfterOpening) { $Body += $AddAfterOpening } if (-not $SkipNewLines) { $Template = $Template.Split("`n") # https://blogs.msdn.microsoft.com/timid/2014/07/09/one-liner-fun-with-multi-line-blocktext-and-split-split/ 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 } foreach ($t in $Template) { $Body += "$t<br>" } } else { $Body += $Template } 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) { 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 ($AddAfterOpening) { $Body += $AddBeforeClosing } $Body += '</body>' if ($ConfigurationParameters) { if ($ConfigurationParameters.DisplayTemplateHTML -eq $true) { Get-HTML($Body) } } return $Body } function Set-EmailHead { [cmdletBinding()] param( [System.Collections.IDictionary] $FormattingOptions ) $head = @" <!DOCTYPE html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta content="width=device-width, initial-scale=1" name="viewport"> <meta name="description" content="Password Expiration Email"> <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> </head> "@ return $Head } function Set-EmailReportBranding { [cmdletBinding()] 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 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 } function Get-FileMetaData { <# .SYNOPSIS Small function that gets metadata information from file providing similar output to what Explorer shows when viewing file .DESCRIPTION Small function that gets metadata information from file providing similar output to what Explorer shows when viewing file .PARAMETER File FileName or FileObject .EXAMPLE Get-ChildItem -Path $Env:USERPROFILE\Desktop -Force | Get-FileMetaData | Out-HtmlView -ScrollX -Filtering -AllProperties .EXAMPLE Get-ChildItem -Path $Env:USERPROFILE\Desktop -Force | Where-Object { $_.Attributes -like '*Hidden*' } | Get-FileMetaData | Out-HtmlView -ScrollX -Filtering -AllProperties .NOTES #> [CmdletBinding()] param ( [Parameter(Position = 0, ValueFromPipeline)][Object] $File, [ValidateSet('None', 'MACTripleDES', 'MD5', 'RIPEMD160', 'SHA1', 'SHA256', 'SHA384', 'SHA512')][string] $HashAlgorithm = 'None', [switch] $Signature, [switch] $AsHashTable ) Process { foreach ($F in $File) { $MetaDataObject = [ordered] @{} if ($F -is [string]) { if ($F -and (Test-Path -LiteralPath $F)) { $FileInformation = Get-ItemProperty -Path $F if ($FileInformation -is [System.IO.DirectoryInfo]) { continue } } else { Write-Warning "Get-FileMetaData - Doesn't exists. Skipping $F." continue } } elseif ($F -is [System.IO.DirectoryInfo]) { #Write-Warning "Get-FileMetaData - Directories are not supported. Skipping $F." continue } elseif ($F -is [System.IO.FileInfo]) { $FileInformation = $F } else { Write-Warning "Get-FileMetaData - Only files are supported. Skipping $F." continue } $ShellApplication = New-Object -ComObject Shell.Application $ShellFolder = $ShellApplication.Namespace($FileInformation.Directory.FullName) $ShellFile = $ShellFolder.ParseName($FileInformation.Name) $MetaDataProperties = [ordered] @{} 0..400 | ForEach-Object -Process { $DataValue = $ShellFolder.GetDetailsOf($null, $_) $PropertyValue = (Get-Culture).TextInfo.ToTitleCase($DataValue.Trim()).Replace(' ', '') if ($PropertyValue -ne '') { $MetaDataProperties["$_"] = $PropertyValue } } foreach ($Key in $MetaDataProperties.Keys) { $Property = $MetaDataProperties[$Key] $Value = $ShellFolder.GetDetailsOf($ShellFile, [int] $Key) if ($Property -in 'Attributes', 'Folder', 'Type', 'SpaceFree', 'TotalSize', 'SpaceUsed') { continue } If (($null -ne $Value) -and ($Value -ne '')) { $MetaDataObject["$Property"] = $Value } } if ($FileInformation.VersionInfo) { $SplitInfo = ([string] $FileInformation.VersionInfo).Split([char]13) foreach ($Item in $SplitInfo) { $Property = $Item.Split(":").Trim() if ($Property[0] -and $Property[1] -ne '') { if ($Property[1] -in 'False', 'True') { $MetaDataObject["$($Property[0])"] = [bool] $Property[1] } else { $MetaDataObject["$($Property[0])"] = $Property[1] } } } } $MetaDataObject["Attributes"] = $FileInformation.Attributes $MetaDataObject['IsReadOnly'] = $FileInformation.IsReadOnly $MetaDataObject['IsHidden'] = $FileInformation.Attributes -like '*Hidden*' $MetaDataObject['IsSystem'] = $FileInformation.Attributes -like '*System*' if ($Signature) { $DigitalSignature = Get-AuthenticodeSignature -FilePath $FileInformation.Fullname $MetaDataObject['SignatureCertificateSubject'] = $DigitalSignature.SignerCertificate.Subject $MetaDataObject['SignatureCertificateIssuer'] = $DigitalSignature.SignerCertificate.Issuer $MetaDataObject['SignatureCertificateSerialNumber'] = $DigitalSignature.SignerCertificate.SerialNumber $MetaDataObject['SignatureCertificateNotBefore'] = $DigitalSignature.SignerCertificate.NotBefore $MetaDataObject['SignatureCertificateNotAfter'] = $DigitalSignature.SignerCertificate.NotAfter $MetaDataObject['SignatureCertificateThumbprint'] = $DigitalSignature.SignerCertificate.Thumbprint $MetaDataObject['SignatureStatus'] = $DigitalSignature.Status $MetaDataObject['IsOSBinary'] = $DigitalSignature.IsOSBinary } if ($HashAlgorithm -ne 'None') { $MetaDataObject[$HashAlgorithm] = (Get-FileHash -LiteralPath $FileInformation.FullName -Algorithm $HashAlgorithm).Hash } if ($AsHashTable) { $MetaDataObject } else { [PSCustomObject] $MetaDataObject } } } } #Get-FileMetaData -File 'c:\Support\GitHub\PSSharedGoods\Public\FilesFolders' 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) { # C:\Users\przemyslaw.klys\AppData\Local\Temp\p0v4bbif.xlsx return [io.path]::Combine([System.IO.Path]::GetTempPath(), "$($([System.IO.Path]::GetRandomFileName()).Split('.')[0]).$Extension") } if ($TemporaryFileOnly) { # Generates 3ymsxvav.tmp return "$($([System.IO.Path]::GetRandomFileName()).Split('.')[0]).$Extension" } } function Get-FileOwner { [cmdletBinding()] param( [Array] $Path, [switch] $Recursive, [switch] $JustPath, [switch] $Resolve, [switch] $AsHashTable ) Begin { } Process { foreach ($P in $Path) { if ($P -is [System.IO.FileSystemInfo]) { $FullPath = $P.FullName } elseif ($P -is [string]) { $FullPath = $P } if ($FullPath -and (Test-Path -Path $FullPath)) { if ($JustPath) { $FullPath | ForEach-Object -Process { # $File = $_ $ACL = Get-Acl -Path $_ $Object = [ordered]@{ FullName = $_ Owner = $ACL.Owner } if ($Resolve) { $Identity = Convert-Identity -Identity $ACL.Owner if ($Identity) { $Object['OwnerName'] = $Identity.Name $Object['OwnerSid'] = $Identity.SID $Object['OwnerType'] = $Identity.Type } else { $Object['OwnerName'] = '' $Object['OwnerSid'] = '' $Object['OwnerType'] = '' } } if ($AsHashTable) { $Object } else { [PSCustomObject] $Object } } } else { Get-ChildItem -LiteralPath $FullPath -Recurse:$Recursive -Force | ForEach-Object -Process { $File = $_ $ACL = Get-Acl -Path $File.FullName $Object = [ordered] @{ FullName = $_.FullName Extension = $_.Extension CreationTime = $_.CreationTime LastAccessTime = $_.LastAccessTime LastWriteTime = $_.LastWriteTime Attributes = $_.Attributes Owner = $ACL.Owner } if ($Resolve) { $Identity = Convert-Identity -Identity $ACL.Owner if ($Identity) { $Object['OwnerName'] = $Identity.Name $Object['OwnerSid'] = $Identity.SID $Object['OwnerType'] = $Identity.Type } else { $Object['OwnerName'] = '' $Object['OwnerSid'] = '' $Object['OwnerType'] = '' } } if ($AsHashTable) { $Object } else { [PSCustomObject] $Object } } } } } } End { } } function Get-FilePermission { [alias('Get-PSPermissions', 'Get-FilePermissions')] [cmdletBinding()] param( [Array] $Path, [switch] $Inherited, [switch] $NotInherited, [switch] $ResolveTypes, [switch] $Extended, [switch] $IncludeACLObject, [switch] $AsHashTable, [System.Security.AccessControl.FileSystemSecurity] $ACLS ) foreach ($P in $Path) { if ($P -is [System.IO.FileSystemInfo]) { $FullPath = $P.FullName } elseif ($P -is [string]) { $FullPath = $P } $TestPath = Test-Path -Path $FullPath if ($TestPath) { if (-not $ACLS) { try { $ACLS = (Get-Acl -Path $FullPath -ErrorAction Stop) } catch { Write-Warning -Message "Get-FilePermission - Can't access $FullPath. Error $($_.Exception.Message)" continue } } $Output = foreach ($ACL in $ACLS.Access) { if ($Inherited) { if ($ACL.IsInherited -eq $false) { # if it's not inherited and we require inherited lets continue continue } } if ($NotInherited) { if ($ACL.IsInherited -eq $true) { continue } } $TranslateRights = Convert-GenericRightsToFileSystemRights -OriginalRights $ACL.FileSystemRights $ReturnObject = [ordered] @{ } $ReturnObject['Path' ] = $FullPath $ReturnObject['AccessControlType'] = $ACL.AccessControlType if ($ResolveTypes) { $Identity = Convert-Identity -Identity $ACL.IdentityReference if ($Identity) { $ReturnObject['Principal'] = $ACL.IdentityReference $ReturnObject['PrincipalName'] = $Identity.Name $ReturnObject['PrincipalSid'] = $Identity.Sid $ReturnObject['PrincipalType'] = $Identity.Type } else { # this shouldn't happen because Identity should always return value $ReturnObject['Principal'] = $Identity $ReturnObject['PrincipalName'] = '' $ReturnObject['PrincipalSid'] = '' $ReturnObject['PrincipalType'] = '' } } else { $ReturnObject['Principal'] = $ACL.IdentityReference.Value } $ReturnObject['FileSystemRights'] = $TranslateRights $ReturnObject['IsInherited'] = $ACL.IsInherited if ($Extended) { $ReturnObject['InheritanceFlags'] = $ACL.InheritanceFlags $ReturnObject['PropagationFlags'] = $ACL.PropagationFlags } if ($IncludeACLObject) { $ReturnObject['ACL'] = $ACL $ReturnObject['AllACL'] = $ACLS } if ($AsHashTable) { $ReturnObject } else { [PSCustomObject] $ReturnObject } } $Output } else { Write-Warning "Get-PSPermissions - Path $Path doesn't exists. Skipping." } } } <# $Directories = @( '\\ad1.ad.evotec.xyz\SYSVOL' Get-ChildItem -Path '\\ad1.ad.evotec.xyz\SYSVOL' -Recurse -Directory ) $Output = foreach ($Directory in $Directories) { Get-PSPermissions -Path $Directory -NotInherited } $Output | Format-Table -a #> function Get-FilesInFolder { [CmdletBinding()] param( [string] $Folder, [string] $Extension = '*.evtx' ) $Files = Get-ChildItem -Path $Folder -Filter $Extension -Recurse $ReturnFiles = foreach ($File in $Files) { $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-PathSeparator { [CmdletBinding()] param() return [IO.Path]::PathSeparator } function Get-PathTemporary { [CmdletBinding()] param() return [IO.path]::GetTempPath() } function Get-TemporaryDirectory { param( ) $TemporaryFolder = Get-RandomStringName -Size 13 -LettersOnly -ToLower $TemporaryPath = [system.io.path]::GetTempPath() $Output = New-Item -ItemType Directory -Path $TemporaryPath -Name $TemporaryFolder -Force if (Test-Path -LiteralPath $Output.FullName) { $Output } } function Remove-FilePermission { [cmdletBinding()] param( [string] $Path, [string] $UserOrGroup = "", [switch] $All ) $ACL = Get-Acl -Path $Path if ($UserOrGroup -ne "") { foreach ($access in $ACL.Access) { if ($access.IdentityReference.Value -eq $UserOrGroup) { $ACL.RemoveAccessRule($access) | Out-Null } } } if ($All -eq $true) { foreach ($access in $ACL.Access) { $ACL.RemoveAccessRule($access) | Out-Null } } Set-Acl -Path $Path -AclObject $ACL } function Set-FileInheritance { [cmdletBinding()] param( [string] $StartingDir, [switch] $DisableInheritance, [switch] $KeepInheritedAcl ) $acl = Get-Acl -Path $StartingDir $acl.SetAccessRuleProtection($DisableInheritance, $KeepInheritedAcl) $acl | Set-Acl -Path $StartingDir } function Set-FileOwner { [cmdletBinding(SupportsShouldProcess)] param( [Array] $Path, [switch] $Recursive, [string] $Owner, [string[]] $Exlude, [switch] $JustPath ) <# System.Security.Principal.NTAccount new(string domainName, string accountName) System.Security.Principal.NTAccount new(string name) #> Begin { } Process { foreach ($P in $Path) { if ($P -is [System.IO.FileSystemInfo]) { $FullPath = $P.FullName } elseif ($P -is [string]) { $FullPath = $P } $OwnerTranslated = [System.Security.Principal.NTAccount]::new($Owner) if ($FullPath -and (Test-Path -Path $FullPath)) { if ($JustPath) { $FullPath | ForEach-Object -Process { $File = $_ try { $ACL = Get-Acl -Path $File -ErrorAction Stop } catch { Write-Warning "Set-FileOwner - Getting ACL failed with error: $($_.Exception.Message)" } if ($ACL.Owner -notin $Exlude -and $ACL.Owner -ne $OwnerTranslated) { if ($PSCmdlet.ShouldProcess($File, "Replacing owner $($ACL.Owner) to $OwnerTranslated")) { try { $ACL.SetOwner($OwnerTranslated) Set-Acl -Path $File -AclObject $ACL -ErrorAction Stop } catch { Write-Warning "Set-FileOwner - Replacing owner $($ACL.Owner) to $OwnerTranslated failed with error: $($_.Exception.Message)" } } } } } else { Get-ChildItem -LiteralPath $FullPath -Recurse:$Recursive -ErrorAction SilentlyContinue -ErrorVariable err | ForEach-Object -Process { $File = $_ try { $ACL = Get-Acl -Path $File.FullName -ErrorAction Stop } catch { Write-Warning "Set-FileOwner - Getting ACL failed with error: $($_.Exception.Message)" } if ($ACL.Owner -notin $Exlude -and $ACL.Owner -ne $OwnerTranslated) { if ($PSCmdlet.ShouldProcess($File.FullName, "Replacing owner $($ACL.Owner) to $OwnerTranslated")) { try { $ACL.SetOwner($OwnerTranslated) Set-Acl -Path $File.FullName -AclObject $ACL -ErrorAction Stop } catch { Write-Warning "Set-FileOwner - Replacing owner $($ACL.Owner) to $OwnerTranslated failed with error: $($_.Exception.Message)" } } } } foreach ($e in $err) { Write-Warning "Set-FileOwner - Errors processing $($e.Exception.Message) ($($e.CategoryInfo.Reason))" } } } } } End { } } function Set-FilePermission { [cmdletBinding()] param ( [string] $Path, [alias('UserOrGroup')][string] $Principal, [System.Security.AccessControl.InheritanceFlags] $InheritedFolderPermissions = @( [System.Security.AccessControl.InheritanceFlags]::ContainerInherit, [System.Security.AccessControl.InheritanceFlags]::ObjectInherit ), [System.Security.AccessControl.AccessControlType] $AccessControlType = [System.Security.AccessControl.AccessControlType]::Allow, [System.Security.AccessControl.PropagationFlags] $PropagationFlags = [System.Security.AccessControl.PropagationFlags]::None, [System.Security.AccessControl.FileSystemRights] $AclRightsToAssign ) ### The possible values for Rights are: # ListDirectory, ReadData, WriteData, CreateFiles, CreateDirectories, AppendData, Synchronize, FullControl # ReadExtendedAttributes, WriteExtendedAttributes, Traverse, ExecuteFile, DeleteSubdirectoriesAndFiles, ReadAttributes # WriteAttributes, Write, Delete, ReadPermissions, Read, ReadAndExecute, Modify, ChangePermissions, TakeOwnership ### Principal expected # domain\username ### Inherited folder permissions: # Object inherit - This folder and files. (no inheritance to subfolders) # Container inherit - This folder and subfolders. # Inherit only - The ACE does not apply to the current file/directory if ($Principal) { $User = [System.Security.Principal.NTAccount]::new($Principal) #define a new access rule. $ACL = Get-Acl -Path $Path <# System.Security.AccessControl.FileSystemAccessRule new(System.Security.Principal.IdentityReference identity, System.Security.AccessControl.FileSystemRights fileSystemRights, System.Security.AccessControl.AccessControlType type) System.Security.AccessControl.FileSystemAccessRule new(string identity, System.Security.AccessControl.FileSystemRights fileSystemRights, System.Security.AccessControl.AccessControlType type) System.Security.AccessControl.FileSystemAccessRule new(System.Security.Principal.IdentityReference identity, System.Security.AccessControl.FileSystemRights fileSystemRights, System.Security.AccessControl.InheritanceFlags inheritanceFlags, System.Security.AccessControl.PropagationFlags propagationFlags, System.Security.AccessControl.AccessControlType type) System.Security.AccessControl.FileSystemAccessRule new(string identity, System.Security.AccessControl.FileSystemRights fileSystemRights, System.Security.AccessControl.InheritanceFlags inheritanceFlags, System.Security.AccessControl.PropagationFlags propagationFlags, System.Security.AccessControl.AccessControlType type) #> $Rule = [System.Security.AccessControl.FileSystemAccessRule]::new($User, $AclRightsToAssign, $InheritedFolderPermissions, $PropagationFlags, $AccessControlType) $ACL.SetAccessRule($Rule) Try { Set-Acl -Path $Path -AclObject $ACL } catch { Write-Warning "Set-FilePermission - Setting permission $AclRightsToAssign failed with error: $($_.Exception.Message)" } } } function Get-GitHubLatestRelease { <# .SYNOPSIS Gets one or more releases from GitHub repository .DESCRIPTION Gets one or more releases from GitHub repository .PARAMETER Url Url to github repository .EXAMPLE Get-GitHubLatestRelease -Url "https://api.github.com1/repos/evotecit/Testimo/releases" | Format-Table .NOTES General notes #> [CmdLetBinding()] param( [parameter(Mandatory)][alias('ReleasesUrl')][uri] $Url ) $ProgressPreference = 'SilentlyContinue' $Responds = Test-Connection -ComputerName $URl.Host -Quiet -Count 1 if ($Responds) { Try { [Array] $JsonOutput = (Invoke-WebRequest -Uri $Url -ErrorAction Stop | ConvertFrom-Json) foreach ($JsonContent in $JsonOutput) { [PSCustomObject] @{ PublishDate = [DateTime] $JsonContent.published_at CreatedDate = [DateTime] $JsonContent.created_at PreRelease = [bool] $JsonContent.prerelease Version = [version] ($JsonContent.name -replace 'v', '') Tag = $JsonContent.tag_name Branch = $JsonContent.target_commitish Errors = '' } } } catch { [PSCustomObject] @{ PublishDate = $null CreatedDate = $null PreRelease = $null Version = $null Tag = $null Branch = $null Errors = $_.Exception.Message } } } else { [PSCustomObject] @{ PublishDate = $null CreatedDate = $null PreRelease = $null Version = $null Tag = $null Branch = $null Errors = "No connection (ping) to $($Url.Host)" } } $ProgressPreference = 'Continue' } function Get-GitHubVersion { <# .SYNOPSIS Get the latest version of a GitHub repository and compare with local version .DESCRIPTION Get the latest version of a GitHub repository and compare with local version .PARAMETER Cmdlet Cmdlet to find module for .PARAMETER RepositoryOwner Repository owner .PARAMETER RepositoryName Repository name .EXAMPLE Get-GitHubVersion -Cmdlet 'Start-DelegationModel' -RepositoryOwner 'evotecit' -RepositoryName 'DelegationModel' .NOTES General notes #> [cmdletBinding()] param( [Parameter(Mandatory)][string] $Cmdlet, [Parameter(Mandatory)][string] $RepositoryOwner, [Parameter(Mandatory)][string] $RepositoryName ) $App = Get-Command -Name $Cmdlet -ErrorAction SilentlyContinue if ($App) { [Array] $GitHubReleases = (Get-GitHubLatestRelease -Url "https://api.github.com/repos/$RepositoryOwner/$RepositoryName/releases" -Verbose:$false) $LatestVersion = $GitHubReleases[0] if (-not $LatestVersion.Errors) { if ($App.Version -eq $LatestVersion.Version) { "Current/Latest: $($LatestVersion.Version) at $($LatestVersion.PublishDate)" } elseif ($App.Version -lt $LatestVersion.Version) { "Current: $($App.Version), Published: $($LatestVersion.Version) at $($LatestVersion.PublishDate). Update?" } elseif ($App.Version -gt $LatestVersion.Version) { "Current: $($App.Version), Published: $($LatestVersion.Version) at $($LatestVersion.PublishDate). Lucky you!" } } else { "Current: $($App.Version)" } } } function Get-Logger { <# .SYNOPSIS Returns an instance of the logger object. .DESCRIPTION Long description .PARAMETER LogPath Parameter description .PARAMETER LogsDir Parameter description .PARAMETER Filename Parameter description .PARAMETER ShowTime Parameter description .PARAMETER TimeFormat Parameter description .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") .NOTES General notes #> [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 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 $null -eq $Element) { #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) } } function Clear-DataInformation { [CmdletBinding()] param( [System.Collections.IDictionary] $Data, [Array] $TypesRequired, [switch] $DontRemoveSupportData, [switch] $DontRemoveEmpty ) # Check each domain for empty values and remove if empty foreach ($Domain in $Data.FoundDomains.Keys) { $RemoveDomainKeys = foreach ($Key in $Data.FoundDomains.$Domain.Keys) { if ($null -eq $Data.FoundDomains.$Domain.$Key) { if (-not $DontRemoveEmpty) { $Key } continue } if ($Key -notin $TypesRequired -and $DontRemoveSupportData -eq $false) { $Key } } foreach ($Key in $RemoveDomainKeys) { $Data.FoundDomains.$Domain.Remove($Key) } } # Remove domains if empty $RemoveDomains = foreach ($Domain in $Data.FoundDomains.Keys) { if ($Data.FoundDomains.$Domain.Count -eq 0) { $Domain } } foreach ($Domain in $RemoveDomains) { $Data.FoundDomains.Remove($Domain) } # Remove FoundDomains if empty if ($Data.FoundDomains.Count -eq 0) { $Data.Remove('FoundDomains') } # Remove empty keys in Forest $RemoveKeys = foreach ($Key in $Data.Keys) { if ($Key -eq 'FoundDomains') { # Skip this key continue } if ($null -eq $Data.$Key) { if (-not $DontRemoveEmpty) { $Key } continue } if ($Key -notin $TypesRequired -and $DontRemoveSupportData -eq $false) { $Key } } foreach ($Key in $RemoveKeys) { $Data.Remove($Key) } } function Compare-MultipleObjects { [CmdLetBinding()] param( [System.Collections.IList] $Objects, [Array] $ObjectsName = @(), [switch] $CompareSorted, [switch] $FormatOutput, [switch] $FormatDifferences, [switch] $Summary, [string] $Splitter = ', ', [string[]] $Property, [string[]] $ExcludeProperty, [switch] $AllProperties, [switch] $SkipProperties, [int] $First, [int] $Last, [Array] $Replace, [switch] $FlattenObject ) if ($null -eq $Objects -or $Objects.Count -eq 1) { Write-Warning "Compare-MultipleObjects - Unable to compare objects. Not enough objects to compare ($($Objects.Count))." return } if (-not $ObjectsName) { $ObjectsName = @() } if ($ObjectsName.Count -gt 0 -and $Objects.Count -gt $ObjectsName.Count) { #$ObjectsName = @() Write-Warning -Message "Compare-MultipleObjects - Unable to rename objects. ObjectsName small then amount of Objects ($($Objects.Count))." } # Default Select-Object -Unique is case sensitive, Sort-Object -Unique isn't but it sorts object. # Below is function that solves this, ugly but it works function Compare-TwoArrays { [CmdLetBinding()] param( [string] $FieldName, [Array] $Object1, [Array] $Object2, [Array] $Replace ) $Result = [ordered] @{ Status = $false Same = [System.Collections.Generic.List[string]]::new() Add = [System.Collections.Generic.List[string]]::new() Remove = [System.Collections.Generic.List[string]]::new() } if ($Replace) { foreach ($R in $Replace) { # if no keys are given replace is for all objects '' # if keys are given replace is only done for a given FieldName if (($($R.Keys[0]) -eq '') -or ($($R.Keys[0]) -eq $FieldName)) { if ($null -ne $Object1) { $Object1 = $Object1 -replace $($R.Values)[0], $($R.Values)[1] #$Object1 = $Object1 -replace $R[0], $R[1] } if ($null -ne $Object2) { $Object2 = $Object2 -replace $($R.Values)[0], $($R.Values)[1] #$Object2 = $Object2 -replace $R[0], $R[1] } } } } if ($null -eq $Object1 -and $null -eq $Object2) { $Result['Status'] = $true } elseif (($null -eq $Object1) -or ($null -eq $Object2)) { $Result['Status'] = $false foreach ($O in $Object1) { $Result['Add'].Add($O) } foreach ($O in $Object2) { $Result['Remove'].Add($O) } } else { $ComparedObject = Compare-Object -ReferenceObject $Object1 -DifferenceObject $Object2 -IncludeEqual foreach ($_ in $ComparedObject) { if ($_.SideIndicator -eq '==') { $Result['Same'].Add($_.InputObject) } elseif (($_.SideIndicator -eq '<=')) { $Result['Add'].Add($_.InputObject) } elseif (($_.SideIndicator -eq '=>')) { $Result['Remove'].Add($_.InputObject) } } IF ($Result['Add'].Count -eq 0 -and $Result['Remove'].Count -eq 0) { $Result['Status'] = $true } else { $Result['Status'] = $false } } $Result } if ($ObjectsName[0]) { $ValueSourceName = $ObjectsName[0] } else { $ValueSourceName = "Source" } [Array] $Objects = foreach ($Object in $Objects) { if ($null -eq $Object) { [PSCustomObject] @{} } else { $Object } } if ($FlattenObject) { try { [Array] $Objects = ConvertTo-FlatObject -Objects $Objects -ExcludeProperty $ExcludeProperty } catch { Write-Warning "Compare-MultipleObjects - Unable to flatten objects. ($($_.Exception.Message))" } } if ($First -or $Last) { [int] $TotalCount = $First + $Last if ($TotalCount -gt 1) { $Objects = $Objects | Select-Object -First $First -Last $Last } else { Write-Warning "Compare-MultipleObjects - Unable to compare objects. Not enough objects to compare ($TotalCount)." return } } $ReturnValues = @( $FirstElement = [ordered] @{ } $FirstElement['Name'] = 'Properties' if ($Summary) { $FirstElement['Same'] = $null $FirstElement['Different'] = $null } $FirstElement['Status'] = $false # Compare properties $FirstObjectProperties = Select-Properties -Objects $Objects -Property $Property -ExcludeProperty $ExcludeProperty -AllProperties:$AllProperties if (-not $SkipProperties) { if ($FormatOutput) { $FirstElement[$ValueSourceName] = $FirstObjectProperties -join $Splitter } else { $FirstElement[$ValueSourceName] = $FirstObjectProperties } [Array] $IsSame = for ($i = 1; $i -lt $Objects.Count; $i++) { if ($ObjectsName[$i]) { $ValueToUse = $ObjectsName[$i] } else { $ValueToUse = $i } if ($Objects[0] -is [System.Collections.IDictionary]) { [string[]] $CompareObjectProperties = $Objects[$i].Keys } else { [string[]] $CompareObjectProperties = $Objects[$i].PSObject.Properties.Name [string[]] $CompareObjectProperties = Select-Properties -Objects $Objects[$i] -Property $Property -ExcludeProperty $ExcludeProperty -AllProperties:$AllProperties } if ($FormatOutput) { $FirstElement["$ValueToUse"] = $CompareObjectProperties -join $Splitter } else { $FirstElement["$ValueToUse"] = $CompareObjectProperties } if ($CompareSorted) { $Value1 = $FirstObjectProperties | Sort-Object $Value2 = $CompareObjectProperties | Sort-Object } else { $Value1 = $FirstObjectProperties $Value2 = $CompareObjectProperties } $Status = Compare-TwoArrays -FieldName 'Properties' -Object1 $Value1 -Object2 $Value2 -Replace $Replace if ($FormatDifferences) { $FirstElement["$ValueToUse-Add"] = $Status['Add'] -join $Splitter $FirstElement["$ValueToUse-Remove"] = $Status['Remove'] -join $Splitter $FirstElement["$ValueToUse-Same"] = $Status['Same'] -join $Splitter } else { $FirstElement["$ValueToUse-Add"] = $Status['Add'] $FirstElement["$ValueToUse-Remove"] = $Status['Remove'] $FirstElement["$ValueToUse-Same"] = $Status['Same'] } $Status } if ($IsSame.Status -notcontains $false) { $FirstElement['Status'] = $true } else { $FirstElement['Status'] = $false } if ($Summary) { [Array] $Collection = (0..($IsSame.Count - 1)).Where( { $IsSame[$_].Status -eq $true }, 'Split') if ($FormatDifferences) { $FirstElement['Same'] = ($Collection[0] | ForEach-Object { $Count = $_ + 1 if ($ObjectsName[$Count]) { $ObjectsName[$Count] } else { $Count } } ) -join $Splitter $FirstElement['Different'] = ($Collection[1] | ForEach-Object { $Count = $_ + 1 if ($ObjectsName[$Count]) { $ObjectsName[$Count] } else { $Count } } ) -join $Splitter } else { $FirstElement['Same'] = $Collection[0] | ForEach-Object { $Count = $_ + 1 if ($ObjectsName[$Count]) { $ObjectsName[$Count] } else { $Count } } $FirstElement['Different'] = $Collection[1] | ForEach-Object { $Count = $_ + 1 if ($ObjectsName[$Count]) { $ObjectsName[$Count] } else { $Count } } } } [PSCustomObject] $FirstElement } # Compare Objects foreach ($NameProperty in $FirstObjectProperties) { $EveryOtherElement = [ordered] @{ } $EveryOtherElement['Name'] = $NameProperty if ($Summary) { $EveryOtherElement['Same'] = $null $EveryOtherElement['Different'] = $null } $EveryOtherElement.Status = $false if ($FormatOutput) { $EveryOtherElement[$ValueSourceName] = $Objects[0].$NameProperty -join $Splitter } else { $EveryOtherElement[$ValueSourceName] = $Objects[0].$NameProperty } [Array] $IsSame = for ($i = 1; $i -lt $Objects.Count; $i++) { $Skip = $false if ($ObjectsName[$i]) { $ValueToUse = $ObjectsName[$i] } else { $ValueToUse = $i } if ($Objects[$i] -is [System.Collections.IDictionary]) { if ($Objects[$i].Keys -notcontains $NameProperty) { $Status = [ordered] @{ Status = $false Same = @() Add = @() Remove = @() } $Skip = $true } } elseif ($Objects[$i].PSObject.Properties.Name -notcontains $NameProperty) { $Status = [ordered] @{ Status = $false; Same = @() Add = @() Remove = @() } $Skip = $true } if ($FormatOutput) { $EveryOtherElement["$ValueToUse"] = $Objects[$i].$NameProperty -join $Splitter } else { $EveryOtherElement["$ValueToUse"] = $Objects[$i].$NameProperty } if ($CompareSorted) { $Value1 = $Objects[0].$NameProperty | Sort-Object $Value2 = $Objects[$i].$NameProperty | Sort-Object } else { $Value1 = $Objects[0].$NameProperty $Value2 = $Objects[$i].$NameProperty } # This will be used only if we don't use flattening of objects. if ($Value1 -is [PSCustomObject]) { [ordered] @{ Status = $null; Same = @(); Add = @(); Remove = @() } continue } elseif ($Value1 -is [System.Collections.IDictionary]) { [ordered] @{ Status = $null; Same = @(); Add = @(); Remove = @() } continue } elseif ($Value1 -is [Array] -and $Value1.Count -ne 0 -and $Value1[0] -isnot [string]) { [ordered] @{ Status = $null; Same = @(); Add = @(); Remove = @() } continue } if (-not $Skip) { $Status = Compare-TwoArrays -FieldName $NameProperty -Object1 $Value1 -Object2 $Value2 -Replace $Replace } else { $Status['Add'] = $Value1 } if ($FormatDifferences) { $EveryOtherElement["$ValueToUse-Add"] = $Status['Add'] -join $Splitter $EveryOtherElement["$ValueToUse-Remove"] = $Status['Remove'] -join $Splitter $EveryOtherElement["$ValueToUse-Same"] = $Status['Same'] -join $Splitter } else { $EveryOtherElement["$ValueToUse-Add"] = $Status['Add'] $EveryOtherElement["$ValueToUse-Remove"] = $Status['Remove'] $EveryOtherElement["$ValueToUse-Same"] = $Status['Same'] } $Status } if ($null -eq $IsSame.Status) { $EveryOtherElement['Status'] = $null } elseif ($IsSame.Status -notcontains $false) { $EveryOtherElement['Status'] = $true } else { $EveryOtherElement['Status'] = $false } if ($Summary) { [Array] $Collection = (0..($IsSame.Count - 1)).Where( { $IsSame[$_].Status -eq $true }, 'Split') if ($FormatDifferences) { $EveryOtherElement['Same'] = ($Collection[0] | ForEach-Object { $Count = $_ + 1 if ($ObjectsName[$Count]) { $ObjectsName[$Count] } else { $Count } } ) -join $Splitter $EveryOtherElement['Different'] = ($Collection[1] | ForEach-Object { $Count = $_ + 1 if ($ObjectsName[$Count]) { $ObjectsName[$Count] } else { $Count } } ) -join $Splitter } else { $EveryOtherElement['Same'] = $Collection[0] | ForEach-Object { $Count = $_ + 1 if ($ObjectsName[$Count]) { $ObjectsName[$Count] } else { $Count } } $EveryOtherElement['Different'] = $Collection[1] | ForEach-Object { $Count = $_ + 1 if ($ObjectsName[$Count]) { $ObjectsName[$Count] } else { $Count } } } } [PSCuStomObject] $EveryOtherElement } ) if ($ReturnValues.Count -eq 1) { return , $ReturnValues } else { return $ReturnValues } } 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, [switch]$CaseSensitive = $false ) $objprops = @( $($ReferenceObject | Get-Member -MemberType Property, NoteProperty | ForEach-Object Name), $($DifferenceObject | Get-Member -MemberType Property, NoteProperty | ForEach-Object Name) ) $objprops = $objprops | Sort-Object -Unique $diffs = foreach ($objprop in $objprops) { $diff = Compare-Object $ReferenceObject $DifferenceObject -Property $objprop -CaseSensitive:$CaseSensitive if ($diff) { $diffprops = [PsCustomObject] @{ PropertyName = $objprop RefValue = ($diff | Where-Object { $_.SideIndicator -eq '<=' } | ForEach-Object $($objprop)) DiffValue = ($diff | Where-Object { $_.SideIndicator -eq '=>' } | ForEach-Object $($objprop)) } $diffprops #New-Object PSObject -Property $diffprops } } if ($diffs) { return ($diffs | Select-Object 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 Copy-Dictionary { [alias('Copy-Hashtable', 'Copy-OrderedHashtable')] [cmdletbinding()] param( [System.Collections.IDictionary] $Dictionary ) # create a deep-clone of an object $ms = [System.IO.MemoryStream]::new() $bf = [System.Runtime.Serialization.Formatters.Binary.BinaryFormatter]::new() $bf.Serialize($ms, $Dictionary) $ms.Position = 0 $clone = $bf.Deserialize($ms) $ms.Close() $clone } function Copy-DictionaryManual { [CmdletBinding()] param( [System.Collections.IDictionary] $Dictionary ) $clone = @{} foreach ($Key in $Dictionary.Keys) { $value = $Dictionary.$Key $clonedValue = switch ($Dictionary.$Key) { { $null -eq $_ } { $null continue } { $_ -is [System.Collections.IDictionary] } { Copy-DictionaryManual -Dictionary $_ continue } { $type = $_.GetType() $type.IsPrimitive -or $type.IsValueType -or $_ -is [string] } { $_ continue } default { $_ | Select-Object -Property * } } if ($value -is [System.Collections.IList]) { $clone[$Key] = @($clonedValue) } else { $clone[$Key] = $clonedValue } } $clone } 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 = $false, 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)][System.Collections.ICollection] $Object, [switch] $SkipTitle, [string[]] $Property, [string[]] $ExcludeProperty, [Object] $OverwriteHeaders, [switch] $PreScanHeaders, [string] $Splitter = ';' ) if ($Object[0] -is [System.Collections.IDictionary]) { $Array = @( ### Add Titles if (-not $SkipTitle) { , @('Name', 'Value') } ### Add Data foreach ($O in $Object) { foreach ($Name in $O.Keys) { $Value = $O[$Name] # If array contains another array as one of the values we need to do something with it if ($O[$Name].Count -gt 1) { $Value = $O[$Name] -join $Splitter } else { $Value = $O[$Name] } , @($Name, $Value) } } ) if ($Array.Count -eq 1) { , $Array } else { $Array } } elseif ($Object[0].GetType().Name -match 'bool|byte|char|datetime|decimal|double|ExcelHyperLink|float|int|long|sbyte|short|string|timespan|uint|ulong|URI|ushort') { return $Object } else { if ($Property) { $Object = $Object | Select-Object -Property $Property } $Array = @( # Get Titles first (to make sure order is correct for all rows) if ($PreScanHeaders) { $Titles = Get-ObjectProperties -Object $Object } elseif ($OverwriteHeaders) { $Titles = $OverwriteHeaders } else { # If no prescan takes property names from first object $Titles = $Object[0].PSObject.Properties.Name } if (-not $SkipTitle) { # Add Titles to Array (if not -SkipTitles) , $Titles } # Extract data (based on Title) foreach ($O in $Object) { $ArrayValues = foreach ($Name in $Titles) { $Value = $O."$Name" # If array contains another array as one of the values we need to do something with it if ($Value.Count -gt 1) { $Value -join $Splitter } elseif ($Value.Count -eq 1) { if ($Value.Value) { $Value.Value #$Value } else { $Value } } else { '' } } , $ArrayValues } ) if ($Array.Count -eq 1) { , $Array } else { $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)] [Array] $InputObject, [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 0, ParameterSetName = 'Property')] [string[]] $Property, [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 2, ParameterSetName = 'ExcludeProperty')] [string[]] $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 ($InputObject.Count -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 } } } function Format-ToTitleCase { <# .SYNOPSIS Formats string or number of strings to Title Case .DESCRIPTION Formats string or number of strings to Title Case allowing for prettty display .PARAMETER Text Sentence or multiple sentences to format .PARAMETER RemoveWhiteSpace Removes spaces after formatting string to Title Case. .PARAMETER RemoveChar Array of characters to remove .EXAMPLE Format-ToTitleCase 'me' Output: Me .EXAMPLE 'me i feel good' | Format-ToTitleCase Output: Me I Feel Good Not Feel .EXAMPLE 'me i feel', 'not feel' | Format-ToTitleCase Output: Me I Feel Good Not Feel .EXAMPLE Format-ToTitleCase -Text 'This is my thing' -RemoveWhiteSpace Output: ThisIsMyThing .EXAMPLE Format-ToTitleCase -Text "This is my thing: That - No I don't want all chars" -RemoveWhiteSpace -RemoveChar ',', '-', "'", '\(', '\)', ':' .NOTES General notes #> [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)][string[]] $Text, [switch] $RemoveWhiteSpace, [string[]] $RemoveChar ) Begin {} Process { $Conversion = foreach ($T in $Text) { $Output = (Get-Culture).TextInfo.ToTitleCase($T) foreach ($Char in $RemoveChar) { $Output = $Output -replace $Char } if ($RemoveWhiteSpace) { $Output = $Output -replace ' ', '' } $Output } $Conversion } End {} } function Format-TransposeTable { [CmdletBinding()] param ( [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)][System.Collections.ICollection] $Object, [ValidateSet("ASC", "DESC", "NONE")][String] $Sort = 'NONE' ) process { foreach ($myObject in $Object) { if ($myObject -is [System.Collections.IDictionary]) { if ($Sort -eq 'ASC') { [PSCustomObject] $myObject.GetEnumerator() | Sort-Object -Property Name -Descending:$false } elseif ($Sort -eq 'DESC') { [PSCustomObject] $myObject.GetEnumerator() | Sort-Object -Property Name -Descending:$true } else { [PSCustomObject] $myObject } } else { $Output = [ordered] @{ } if ($Sort -eq 'ASC') { $myObject.PSObject.Properties | Sort-Object -Property Name -Descending:$false | ForEach-Object { $Output["$($_.Name)"] = $_.Value } } elseif ($Sort -eq 'DESC') { $myObject.PSObject.Properties | Sort-Object -Property Name -Descending:$true | ForEach-Object { $Output["$($_.Name)"] = $_.Value } } else { $myObject.PSObject.Properties | ForEach-Object { $Output["$($_.Name)"] = $_.Value } } $Output } } } } <# $T = [PSCustomObject] @{ Test = 1 Test2 = 7 Ole = 'bole' Trolle = 'A' Alle = 'sd' } $T1 = [PSCustomObject] @{ Test = 1 Test2 = 7 Ole = 'bole' Trolle = 'A' Alle = 'sd' } $T2 = [ordered] @{ Test = 1 Test2 = 7 Ole = 'bole' Trolle = 'A' Alle = 'sd' } $T3 = @{ Test = 1 Test2 = 7 Ole = 'bole' Trolle = 'A' Alle = 'sd' } Clear-Host Format-TransposeTable -Object @($T) -Sort ASC | Format-Table Format-TransposeTable -Object @($T) -Sort DESC | Format-Table Format-TransposeTable -Object @($T) | Format-Table Format-TransposeTable -Object @($T2) -Sort ASC | Format-Table Format-TransposeTable -Object @($T2) -Sort DESC | Format-Table Format-TransposeTable -Object @($T2) | Format-Table #> function Format-View { [alias('FV', 'Format-Verbose', 'Format-Debug', 'Format-Warning')] [CmdletBinding(DefaultParameterSetName = 'All')] param( [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)] [object] $InputObject, [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 1, 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)] [switch] $Autosize ) 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 $MyList = [System.Collections.Generic.List[Object]]::new() } Process { $MyList.Add($InputObject) } End { $Parameters = @{} if ($Property) { $Parameters.Property = $Property } if ($ExcludeProperty) { $Parameters.ExcludeProperty = $ExcludeProperty } if ($HideTableHeaders) { $Parameters.HideTableHeaders = $HideTableHeaders } if ($List) { # Show and set back to defaults if ($Stream -eq 'Output') { $MyList | Format-List @Parameters | Out-String | Write-Output } elseif ($Stream -eq 'Host') { $MyList | Format-List @Parameters | Out-String | Write-Host } elseif ($Stream -eq 'Warning') { $MyList | Format-List @Parameters | Out-String | Write-Warning $WarningPreference = $WarningCurrent } elseif ($Stream -eq 'Verbose') { $MyList | Format-List @Parameters | Out-String | Write-Verbose $VerbosePreference = $VerboseCurrent } elseif ($Stream -eq 'Debug') { $MyList | Format-List @Parameters | Out-String | Write-Debug $DebugPreference = $DebugCurrent } elseif ($Stream -eq 'Information') { $MyList | Format-List @Parameters | Out-String | Write-Information $InformationPreference = $InformationCurrent } } else { # Show and set back to defaults if ($Stream -eq 'Output') { $MyList | Format-Table @Parameters | Out-String | Write-Output } elseif ($Stream -eq 'Host') { $MyList | Format-Table @Parameters | Out-String | Write-Host } elseif ($Stream -eq 'Warning') { $MyList | Format-Table @Parameters | Out-String | Write-Warning $WarningPreference = $WarningCurrent } elseif ($Stream -eq 'Verbose') { $MyList | Format-Table @Parameters | Out-String | Write-Verbose $VerbosePreference = $VerboseCurrent } elseif ($Stream -eq 'Debug') { $MyList | Format-Table @Parameters | Out-String | Write-Debug $DebugPreference = $DebugCurrent } elseif ($Stream -eq 'Information') { $MyList | Format-Table @Parameters | Out-String | Write-Information $InformationPreference = $InformationCurrent } } } } function Get-Colors { [CmdletBinding()] param( [string[]] $Color ) if ($Color) { foreach ($_ in $Color) { $Script:RGBColors.$_ } } else { return $Script:RGBColors } } $ScriptBlockColors = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $Script:RGBColors.Keys | Where-Object { $_ -like "*$wordToComplete*" } } Register-ArgumentCompleter -CommandName Get-Colors -ParameterName Color -ScriptBlock $ScriptBlockColors function Get-DataInformation { [CmdletBinding()] param( [ScriptBlock] $Content, [string] $Text, [Array] $TypesRequired, [Array] $TypesNeeded, [Array] $Commands, [switch] $SkipAvailability ) if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded $TypesNeeded) { Write-Verbose -Message $Text $Time = Start-TimeLog if ($Commands.Count -gt 0 -and -not $SkipAvailability) { $Available = Test-AvailabilityCommands -Commands $Commands if ($Available -contains $false) { $EndTime = Stop-TimeLog -Time $Time -Option OneLiner Write-Warning "Get-DataInformation - Commands $($Commands -join ", ") is/are not available. Data gathering skipped." Write-Verbose "$Text - Time: $EndTime" return } } if ($null -ne $Content) { & $Content } $EndTime = Stop-TimeLog -Time $Time -Option OneLiner Write-Verbose "$Text - Time: $EndTime" } } 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-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-ObjectCount { [CmdletBinding()] param( [parameter(ValueFromPipelineByPropertyName, ValueFromPipeline)][Object]$Object ) return $($Object | Measure-Object).Count } function Get-ObjectData { [CmdletBinding()] param( $Object, $Title, [switch] $DoNotAddTitles ) [Array] $Values = $Object.$Title [Array] $ArrayList = @( if ($Values.Count -eq 1 -and $DoNotAddTitles -eq $false) { "$Title - $($Values[0])" } else { if ($DoNotAddTitles -eq $false) { $Title } foreach ($Value in $Values) { "$Value" } } ) return $ArrayList } Function Get-ObjectEnumValues { param( [string]$enum ) $enumValues = @{} [enum]::getvalues([type]$enum) | ForEach-Object { $enumValues.add($_, $_.value__) } $enumValues } 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 { [CmdletBinding()] param ( [System.Collections.ICollection] $Object, [string[]] $AddProperties, # provides ability to add some custom properties [switch] $Sort, [bool] $RequireUnique = $true ) $Properties = @( foreach ($O in $Object) { $ObjectProperties = $O.PSObject.Properties.Name $ObjectProperties # foreach ($Property in $ObjectProperties) { # $Property # } } foreach ($Property in $AddProperties) { #Add-ToArrayAdvanced -List $Properties -Element $Property -SkipNull -RequireUnique $Property } ) if ($Sort) { return $Properties | Sort-Object -Unique:$RequireUnique } else { return $Properties | Select-Object -Unique:$RequireUnique } } 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-Types { [CmdletBinding()] param ( [Object] $Types ) $TypesRequired = foreach ($Type in $Types) { $Type.GetEnumValues() } return $TypesRequired } function Join-Uri { <# .SYNOPSIS Provides ability to join two Url paths together .DESCRIPTION Provides ability to join two Url paths together .PARAMETER BaseUri Primary Url to merge .PARAMETER RelativeOrAbsoluteUri Additional path to merge with primary url .EXAMPLE Join-Uri 'https://evotec.xyz/' '/wp-json/wp/v2/posts' .EXAMPLE Join-Uri 'https://evotec.xyz/' 'wp-json/wp/v2/posts' .EXAMPLE Join-Uri -BaseUri 'https://evotec.xyz/' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts' .EXAMPLE Join-Uri -BaseUri 'https://evotec.xyz/test/' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts' .NOTES General notes #> [alias('Join-Url')] [cmdletBinding()] param( [parameter(Mandatory)][uri] $BaseUri, [parameter(Mandatory)][uri] $RelativeOrAbsoluteUri ) return ($BaseUri.OriginalString.TrimEnd('/') + "/" + $RelativeOrAbsoluteUri.OriginalString.TrimStart('/')) #return [Uri]::new([Uri]::new($BaseUri), $RelativeOrAbsoluteUri).ToString() #return [Uri]::new($BaseUri.OriginalString, $RelativeOrAbsoluteUri).ToString() } function Join-UriQuery { <# .SYNOPSIS Provides ability to join two Url paths together including advanced querying .DESCRIPTION Provides ability to join two Url paths together including advanced querying which is useful for RestAPI/GraphApi calls .PARAMETER BaseUri Primary Url to merge .PARAMETER RelativeOrAbsoluteUri Additional path to merge with primary url (optional) .PARAMETER QueryParameter Parameters and their values in form of hashtable .EXAMPLE Join-UriQuery -BaseUri 'https://evotec.xyz/' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts' -QueryParameter @{ page = 1 per_page = 20 search = 'SearchString' } .EXAMPLE Join-UriQuery -BaseUri 'https://evotec.xyz/wp-json/wp/v2/posts' -QueryParameter @{ page = 1 per_page = 20 search = 'SearchString' } .EXAMPLE Join-UriQuery -BaseUri 'https://evotec.xyz' -RelativeOrAbsoluteUri '/wp-json/wp/v2/posts' .NOTES General notes #> [alias('Join-UrlQuery')] [CmdletBinding()] param ( [parameter(Mandatory)][uri] $BaseUri, [parameter(Mandatory = $false)][uri] $RelativeOrAbsoluteUri, [Parameter()][System.Collections.IDictionary] $QueryParameter ) # Join primary url with additional path if needed if ($BaseUri -and $RelativeOrAbsoluteUri) { $Url = Join-Uri -BaseUri $BaseUri -RelativeOrAbsoluteUri $RelativeOrAbsoluteUri } else { $Url = $BaseUri } # Create a http name value collection from an empty string if ($QueryParameter) { $Collection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) foreach ($key in $QueryParameter.Keys) { $Collection.Add($key, $QueryParameter.$key) } } # Build the uri $uriRequest = [System.UriBuilder] $Url if ($Collection) { $uriRequest.Query = $Collection.ToString() } #return $uriRequest.Uri.OriginalUri return $uriRequest.Uri.AbsoluteUri } function Merge-Array { <# .SYNOPSIS Merge-Array allows to merge two or more arrays together. .DESCRIPTION Merge-Array allows to merge two or more arrays together. It copies headers from each Array and merges them together allowing for fulll output. When used with Prescan parameter it actually is able to show headers from all arrays .PARAMETER Array List of Arrays to process .PARAMETER Prescan Scans each element of each array for headers. .EXAMPLE $Array1 = @( [PSCustomObject] @{ Name = 'Company1'; Count = 259 } [PSCustomObject] @{ Name = 'Company2'; Count = 300 } ) $Array2 = @( [PSCustomObject] @{ Name = 'Company1'; Count = 25 } [PSCustomObject] @{ Name = 'Company2'; Count = 100 } ) $Array3 = @( [PSCustomObject] @{ Name1 = 'Company1'; Count3 = 25 } [PSCustomObject] @{ Name1 = 'Company2'; Count3 = 100 } [PSCustomObject] @{ Name2 = 'Company2'; Count3 = 100 } ) $Array1 | Format-Table -AutoSize $Array2 | Format-Table -AutoSize $Array3 | Format-Table -AutoSize Merge-Array -Array $Array1, $Array2, $Array3 | Format-Table -AutoSize Merge-Array -Array $Array1, $Array2, $Array3 -Prescan | Format-Table -AutoSize .NOTES General notes #> param( [Array[]] $Array, [switch] $Prescan ) $PropertyNames = foreach ($A in $Array) { if ($Prescan) { foreach ($O in $A) { $O.PSObject.Properties.Name } } else { $A[0].PSObject.Properties.Name } } $PropertyNames = $PropertyNames | Sort-Object -Unique foreach ($A in $Array) { $A | Select-Object -Property $PropertyNames } } <# $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 = [System.Collections.ArrayList]::new() <# 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 Remove-DuplicateObjects { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER Object Parameter description .PARAMETER Property Parameter description .EXAMPLE $Array = @() $Array += [PSCustomObject] @{ 'Name' = 'Test'; 'Val1' = 'Testor2'; 'Val2' = 'Testor2'} $Array += [PSCustomObject] @{ 'Name' = 'Test'; 'Val1' = 'Testor2'; 'Val2' = 'Testor2'} $Array += [PSCustomObject] @{ 'Name' = 'Test1'; 'Val1' = 'Testor2'; 'Val2' = 'Testor2'} $Array += [PSCustomObject] @{ 'Name' = 'Test1'; 'Val1' = 'Testor2'; 'Val2' = 'Testor2'} Write-Color 'Before' -Color Red $Array | Format-Table -A Write-Color 'After' -Color Green $Array = $Array | Sort-Object -Unique -Property 'Name', 'Val1','Val2' $Array | Format-Table -AutoSize .NOTES General notes #> [CmdletBinding()] param( [System.Collections.IList] $Object, [string[]] $Property ) if ($Object.Count -eq 0) { return $Object } else { return $Object | Sort-Object -Property $Property -Unique } } function Remove-EmptyValue { [alias('Remove-EmptyValues')] [CmdletBinding()] param( [alias('Splat', 'IDictionary')][Parameter(Mandatory)][System.Collections.IDictionary] $Hashtable, [string[]] $ExcludeParameter, [switch] $Recursive, [int] $Rerun, [switch] $DoNotRemoveNull, [switch] $DoNotRemoveEmpty, [switch] $DoNotRemoveEmptyArray, [switch] $DoNotRemoveEmptyDictionary ) foreach ($Key in [string[]] $Hashtable.Keys) { if ($Key -notin $ExcludeParameter) { if ($Recursive) { if ($Hashtable[$Key] -is [System.Collections.IDictionary]) { if ($Hashtable[$Key].Count -eq 0) { if (-not $DoNotRemoveEmptyDictionary) { $Hashtable.Remove($Key) } } else { Remove-EmptyValue -Hashtable $Hashtable[$Key] -Recursive:$Recursive } } else { if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) { $Hashtable.Remove($Key) } } } else { if (-not $DoNotRemoveNull -and $null -eq $Hashtable[$Key]) { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmpty -and $Hashtable[$Key] -is [string] -and $Hashtable[$Key] -eq '') { $Hashtable.Remove($Key) } elseif (-not $DoNotRemoveEmptyArray -and $Hashtable[$Key] -is [System.Collections.IList] -and $Hashtable[$Key].Count -eq 0) { $Hashtable.Remove($Key) } } } } if ($Rerun) { for ($i = 0; $i -lt $Rerun; $i++) { Remove-EmptyValue -Hashtable $Hashtable -Recursive:$Recursive } } } <# $SplatDictionary = [ordered] @{ Test = $NotExistingParameter Test1 = 'Existing Entry' Test2 = $null Test3 = '' Test5 = 0 Test6 = 6 Test7 = @{} } Remove-EmptySplatProperty -Splat $SplatDictionary -Recursive -ExcludeParameter 'Test7' $SplatDictionary #> 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 Rename-LatinCharacters { <# .SYNOPSIS Renames a name to a name without special chars. .DESCRIPTION Renames a name to a name without special chars. .PARAMETER String Provide a string to rename .EXAMPLE Rename-LatinCharacters -String 'Przemysław Kłys' .EXAMPLE Rename-LatinCharacters -String 'Przemysław' .NOTES General notes #> [alias('Remove-StringLatinCharacters')] [cmdletBinding()] param( [string] $String ) [Text.Encoding]::ASCII.GetString([Text.Encoding]::GetEncoding("Cyrillic").GetBytes($String)) } 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 Select-Properties { <# .SYNOPSIS Allows for easy selecting property names from one or multiple objects .DESCRIPTION Allows for easy selecting property names from one or multiple objects. This is especially useful with using AllProperties parameter where we want to make sure to get all properties from all objects. .PARAMETER Objects One or more objects .PARAMETER Property Properties to include .PARAMETER ExcludeProperty Properties to exclude .PARAMETER AllProperties All unique properties from all objects .PARAMETER PropertyNameReplacement Default property name when object has no properties .EXAMPLE $Object1 = [PSCustomobject] @{ Name1 = '1' Name2 = '3' Name3 = '5' } $Object2 = [PSCustomobject] @{ Name4 = '2' Name5 = '6' Name6 = '7' } Select-Properties -Objects $Object1, $Object2 -AllProperties #OR: $Object1, $Object2 | Select-Properties -AllProperties -ExcludeProperty Name6 -Property Name3 .EXAMPLE $Object3 = [Ordered] @{ Name1 = '1' Name2 = '3' Name3 = '5' } $Object4 = [Ordered] @{ Name4 = '2' Name5 = '6' Name6 = '7' } Select-Properties -Objects $Object3, $Object4 -AllProperties $Object3, $Object4 | Select-Properties -AllProperties .NOTES General notes #> [CmdLetBinding()] param( [Array][Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] $Objects, [string[]] $Property, [string[]] $ExcludeProperty, [switch] $AllProperties, [string] $PropertyNameReplacement = '*' ) Begin { function Select-Unique { [CmdLetBinding()] param( [System.Collections.IList] $Object ) [Array] $CleanedList = foreach ($O in $Object) { if ($null -ne $O) { $O } } $New = $CleanedList.ToLower() | Select-Object -Unique $Selected = foreach ($_ in $New) { $Index = $Object.ToLower().IndexOf($_) if ($Index -ne -1) { $Object[$Index] } } $Selected } $ObjectsList = [System.Collections.Generic.List[Object]]::new() } Process { foreach ($Object in $Objects) { $ObjectsList.Add($Object) } } End { if ($ObjectsList.Count -eq 0) { Write-Warning 'Select-Properties - Unable to process. Objects count equals 0.' return } if ($ObjectsList[0] -is [System.Collections.IDictionary]) { if ($AllProperties) { [Array] $All = foreach ($_ in $ObjectsList) { $_.Keys } # $FirstObjectProperties = $All | Select-Object -Unique $FirstObjectProperties = Select-Unique -Object $All } else { $FirstObjectProperties = $ObjectsList[0].Keys } if ($Property.Count -gt 0 -and $ExcludeProperty.Count -gt 0) { #$FirstObjectProperties = $FirstObjectProperties | Where-Object { $Property -contains $_ -and $ExcludeProperty -notcontains $_ } $FirstObjectProperties = foreach ($_ in $FirstObjectProperties) { if ($Property -contains $_ -and $ExcludeProperty -notcontains $_) { $_ continue } } } elseif ($Property.Count -gt 0) { # $FirstObjectProperties = $FirstObjectProperties | Where-Object { $Property -contains $_ } $FirstObjectProperties = foreach ($_ in $FirstObjectProperties) { if ($Property -contains $_) { $_ continue } } } elseif ($ExcludeProperty.Count -gt 0) { #$FirstObjectProperties = $FirstObjectProperties | Where-Object { $ExcludeProperty -notcontains $_ } $FirstObjectProperties = foreach ($_ in $FirstObjectProperties) { if ($ExcludeProperty -notcontains $_) { $_ continue } } } } elseif ($ObjectsList[0].GetType().Name -match 'bool|byte|char|datetime|decimal|double|ExcelHyperLink|float|int|long|sbyte|short|string|timespan|uint|ulong|URI|ushort') { $FirstObjectProperties = $PropertyNameReplacement #Write-Warning "Select-Properties - Object of type $($ObjectsList[0].GetType().Name). Skipping." } else { if ($Property.Count -gt 0 -and $ExcludeProperty.Count -gt 0) { $ObjectsList = $ObjectsList | Select-Object -Property $Property -ExcludeProperty $ExcludeProperty } elseif ($Property.Count -gt 0) { $ObjectsList = $ObjectsList | Select-Object -Property $Property #-ExcludeProperty $ExcludeProperty } elseif ($ExcludeProperty.Count -gt 0) { $ObjectsList = $ObjectsList | Select-Object -Property '*' -ExcludeProperty $ExcludeProperty } if ($AllProperties) { [Array] $All = foreach ($_ in $ObjectsList) { $ListProperties = $_.PSObject.Properties.Name if ($null -ne $ListProperties) { $ListProperties } } #$FirstObjectProperties = $All | Select-Object -Unique $FirstObjectProperties = Select-Unique -Object $All } else { $FirstObjectProperties = $ObjectsList[0].PSObject.Properties.Name } } $FirstObjectProperties } } function Split-Array { <# .SYNOPSIS Split an array into multiple arrays of a specified size or by a specified number of elements .DESCRIPTION Split an array into multiple arrays of a specified size or by a specified number of elements .PARAMETER Objects Lists of objects you would like to split into multiple arrays based on their size or number of parts to split into. .PARAMETER Parts Parameter description .PARAMETER Size Parameter description .EXAMPLE This splits array into multiple arrays of 3 Example below wil return 1,2,3 + 4,5,6 + 7,8,9 Split-array -Objects @(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 -Objects @(1,2,3,4,5,6,7,8,9,10) -Size 3 .NOTES #> [CmdletBinding()] param( [alias('InArray', 'List')][Array] $Objects, [int]$Parts, [int]$Size ) if ($Objects.Count -eq 1) { return $Objects } if ($Parts) { $PartSize = [Math]::Ceiling($inArray.count / $Parts) } if ($Size) { $PartSize = $Size $Parts = [Math]::Ceiling($Objects.count / $Size) } $outArray = [System.Collections.Generic.List[Object]]::new() for ($i = 1; $i -le $Parts; $i++) { $start = (($i - 1) * $PartSize) $end = (($i) * $PartSize) - 1 if ($end -ge $Objects.count) { $end = $Objects.count - 1 } $outArray.Add(@($Objects[$start..$end])) } , $outArray } function Test-IsDistinguishedName { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER Identity Parameter description .EXAMPLE An example .NOTES Original source: https://github.com/PsCustomObject/IT-ToolBox/blob/master/Public/Test-IsValidDn.ps1 #> [alias('Test-IsDN')] [cmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('DN', 'DistinguishedName')][string] $Identity ) Process { [regex]$distinguishedNameRegex = '^(?:(?<cn>CN=(?<name>(?:[^,]|\,)*)),)?(?:(?<path>(?:(?:CN|OU)=(?:[^,]|\,)+,?)+),)?(?<domain>(?:DC=(?:[^,]|\,)+,?)+)$' $Identity -match $distinguishedNameRegex } } function Find-MyProgramData { [CmdletBinding()] param ( $Data, $FindText ) foreach ($Sub in $Data) { if ($Sub -like $FindText) { $Split = $Sub.Split(' ') return $Split[1] } } return '' } function Initialize-ModulePortable { [CmdletBinding()] param( [alias('ModuleName')][string] $Name, [string] $Path = $PSScriptRoot, [switch] $Download, [switch] $Import ) function Get-RequiredModule { param( [string] $Path, [string] $Name ) $PrimaryModule = Get-ChildItem -LiteralPath "$Path\$Name" -Filter '*.psd1' -Recurse -ErrorAction SilentlyContinue -Depth 1 if ($PrimaryModule) { $Module = Get-Module -ListAvailable $PrimaryModule.FullName -ErrorAction SilentlyContinue -Verbose:$false if ($Module) { [Array] $RequiredModules = $Module.RequiredModules.Name if ($null -ne $RequiredModules) { $null } $RequiredModules foreach ($_ in $RequiredModules) { Get-RequiredModule -Path $Path -Name $_ } } } else { Write-Warning "Initialize-ModulePortable - Modules to load not found in $Path" } } if (-not $Name) { Write-Warning "Initialize-ModulePortable - Module name not given. Terminating." return } if (-not $Download -and -not $Import) { Write-Warning "Initialize-ModulePortable - Please choose Download/Import switch. Terminating." return } if ($Download) { try { if (-not $Path -or -not (Test-Path -LiteralPath $Path)) { $null = New-Item -ItemType Directory -Path $Path -Force } Save-Module -Name $Name -LiteralPath $Path -WarningVariable WarningData -WarningAction SilentlyContinue -ErrorAction Stop } catch { $ErrorMessage = $_.Exception.Message if ($WarningData) { Write-Warning "Initialize-ModulePortable - $WarningData" } Write-Warning "Initialize-ModulePortable - Error $ErrorMessage" return } } if ($Download -or $Import) { [Array] $Modules = Get-RequiredModule -Path $Path -Name $Name | Where-Object { $null -ne $_ } if ($null -ne $Modules) { [array]::Reverse($Modules) } $CleanedModules = [System.Collections.Generic.List[string]]::new() foreach ($_ in $Modules) { if ($CleanedModules -notcontains $_) { $CleanedModules.Add($_) } } $CleanedModules.Add($Name) $Items = foreach ($_ in $CleanedModules) { Get-ChildItem -LiteralPath "$Path\$_" -Filter '*.psd1' -Recurse -ErrorAction SilentlyContinue -Depth 1 } [Array] $PSD1Files = $Items.FullName } if ($Download) { $ListFiles = foreach ($PSD1 in $PSD1Files) { $PSD1.Replace("$Path", '$PSScriptRoot') } # Build File $Content = @( '$Modules = @(' foreach ($_ in $ListFiles) { " `"$_`"" } ')' "foreach (`$_ in `$Modules) {" " Import-Module `$_ -Verbose:`$false -Force" "}" ) $Content | Set-Content -Path $Path\$Name.ps1 -Force } if ($Import) { $ListFiles = foreach ($PSD1 in $PSD1Files) { $PSD1 } foreach ($_ in $ListFiles) { Import-Module $_ -Verbose:$false -Force } } } #Initialize-ModulePortable -Name 'Testimo' -Path $Env:USERPROFILE\Desktop\TestimoPortable -Verbose -Download -Import #Initialize-ModulePortable -Name 'SqlServer' -Path $Env:USERPROFILE\Desktop\SqlServer -Verbose -Download #-Import function Invoke-CommandCustom { [cmdletBinding()] param( [scriptblock] $ScriptBlock, [System.Collections.IDictionary] $Parameter, [array] $Argument, [switch] $ReturnVerbose, [switch] $ReturnError, [switch] $ReturnWarning, [switch] $AddParameter ) $Output = [ordered]@{} $ps = [PowerShell]::Create() if ($ReturnVerbose) { $null = $ps.AddScript('$VerbosePreference = "Continue"').AddStatement() } if ($ScriptBlock) { if ($Parameter -and $AddParameter) { $Count = 0 [string] $ScriptBlockParams = @( "param(" foreach ($Key in $Parameter.Keys) { $Count++ if ($Count -eq $Parameter.Keys.Count) { "`$$($Key)" } else { "`$$($Key)," } } ")" $ScriptBlock.ToString() ) $ScriptBlockScript = [scriptblock]::Create($ScriptBlockParams) $null = $ps.AddScript($ScriptBlockScript) } else { $null = $ps.AddScript($ScriptBlock) } } if ($Parameter) { foreach ($Key in $Parameter.Keys) { $null = $ps.AddParameter($Key, $Parameter[$Key]) } } if ($Argument) { foreach ($Arg in $Argument) { $null = $ps.AddArgument($Arg) } } $ErrorCaught = $null try { $InvokedCommand = $ps.Invoke() } catch { $ErrorCaught = $_ } if ($InvokedCommand) { $Output['Output'] = $InvokedCommand } if ($ReturnVerbose) { if ($Ps.Streams.Verbose) { $Output['Verbose'] = $ps.Streams.Verbose } } if ($ReturnWarning) { if ($ps.Streams.Warning) { $Output['Warning'] = $ps.Streams.Warning } } if ($ReturnError) { if ($ErrorCaught) { $Output['Error'] = $ErrorCaught } else { if ($Ps.Streams.Error) { $Output['Error'] = $ps.Streams.Error } } } if ($ReturnError -or $ReturnVerbose -or $ReturnWarning) { $Output } else { $Output.Output } } function Start-InternalFunction { [CmdletBinding()] param( [ScriptBlock] $ScriptBlock, [string] $Module ) $InternalModule = Import-Module -Name $Module -PassThru & $InternalModule $ScriptBlock } function Start-MyProgram { [CmdletBinding()] param ( [string] $Program, [string[]] $CmdArgList, [System.Collections.IDictionary] $LoggerParameters ) # $Output = & $Program $CmdArgList 2>&1 $Output = (cmd /c $Program $CmdArgList '2>&1') if (-not $LoggerParameters) { if ($Output) { return $Output } } else { $Logger = Get-Logger @LoggerParameters if ($null -ne $Output) { $Logger.AddInfoRecord("Running program $Program with output: $Output") } else { $Logger.AddInfoRecord("Running program $Program $CmdArgList") } } } function Get-RandomCharacters { [cmdletbinding()] 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-RandomFileName { [cmdletbinding()] param( $Length = 16, $Extension ) $File = Get-RandomStringName -Size $Length -LettersOnly -ToLower return "$File.$Extension" } function Get-RandomPassword { [cmdletbinding()] param( [int] $LettersLowerCase = 4, [int] $LettersHigherCase = 2, [int] $Numbers = 1, [int] $SpecialChars = 0, [int] $SpecialCharsLimited = 1 ) $Password = @( Get-RandomCharacters -length $LettersLowerCase -characters 'abcdefghiklmnoprstuvwxyz' Get-RandomCharacters -length $LettersHigherCase -characters 'ABCDEFGHKLMNOPRSTUVWXYZ' Get-RandomCharacters -length $Numbers -characters '1234567890' Get-RandomCharacters -length $SpecialChars -characters '!$%()=?{@#' Get-RandomCharacters -length $SpecialCharsLimited -characters '!$#' ) $StringPassword = $Password -join '' $StringPassword = ($StringPassword.ToCharArray() | Get-Random -Count $StringPassword.Length) -join '' return $StringPassword } function Get-RandomStringName { [cmdletbinding()] param( [int] $Size = 31, [switch] $ToLower, [switch] $ToUpper, [switch] $LettersOnly ) [string] $MyValue = @( if ($LettersOnly) { ( -join ((1..$Size) | ForEach-Object { (65..90) + (97..122) | Get-Random } | ForEach-Object { [char]$_ })) } else { ( -join ((48..57) + (97..122) | Get-Random -Count $Size | ForEach-Object { [char]$_ })) } ) if ($ToLower) { return $MyValue.ToLower() } if ($ToUpper) { return $MyValue.ToUpper() } return $MyValue } function Dismount-PSRegistryPath { [alias('Dismount-RegistryPath')] [cmdletbinding()] param( [Parameter(Mandatory)][string] $MountPoint, [switch] $Suppress ) # This is required to force removal of the mount point [gc]::Collect() $pinfo = [System.Diagnostics.ProcessStartInfo]::new() $pinfo.FileName = "reg.exe" $pinfo.RedirectStandardError = $true $pinfo.RedirectStandardOutput = $true $pinfo.UseShellExecute = $false $pinfo.Arguments = " unload $MountPoint" $pinfo.CreateNoWindow = $true $pinfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden $p = [System.Diagnostics.Process]::new() $p.StartInfo = $pinfo $p.Start() | Out-Null $p.WaitForExit() $Output = $p.StandardOutput.ReadToEnd() $Errors = $p.StandardError.ReadToEnd() if ($Errors) { if ($PSBoundParameters.ErrorAction -eq 'Stop') { throw $Errors } else { Write-Warning -Message "Dismount-PSRegistryPath - Couldn't unmount $MountPoint. $Errors" } } else { if ($Output -like "*operation completed*") { if (-not $Suppress) { return $true } } } if (-not $Suppress) { return $false } } <# function Dismount-PSRegistryPath { [alias('Dismount-RegistryPath')] [cmdletbinding()] param( [Parameter(Mandatory)][string] $MountPoint, [switch] $Suppress ) # This is required to force removal of the mount point [gc]::Collect() # $pinfo = [System.Diagnostics.ProcessStartInfo]::new() # $pinfo.FileName = "reg.exe" # $pinfo.RedirectStandardError = $true # $pinfo.RedirectStandardOutput = $true # $pinfo.UseShellExecute = $false # $pinfo.Arguments = " unload $MountPoint" # $pinfo.CreateNoWindow = $true # $pinfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden # $p = [System.Diagnostics.Process]::new() # $p.StartInfo = $pinfo # $p.Start() | Out-Null # $p.WaitForExit() # $Output = $p.StandardOutput.ReadToEnd() # $Errors = $p.StandardError.ReadToEnd() $p = reg unload $MountPoint 2>&1 #$Errors , $Output = $p.Where({ $_ -is [System.Management.Automation.ErrorRecord] }, 'Split') if ($p -is [Array] -and $p[0] -is [System.Management.Automation.ErrorRecord] -or $p -is [System.Management.Automation.ErrorRecord]) { $Errors = $p | ForEach-Object { $_.ToString().Replace('System.Management.Automation.RemoteException', '') } } else { $Output = $p } if ($Errors) { if ($PSBoundParameters.ErrorAction -eq 'Stop') { throw $Errors } else { Write-Warning -Message "Dismount-PSRegistryPath - Couldn't unmount $MountPoint. $Errors" } } else { if ($Output -like "*operation completed*") { if (-not $Suppress) { return $true } } } if (-not $Suppress) { return $false } } #> function Get-PSRegistry { <# .SYNOPSIS Get registry key values. .DESCRIPTION Get registry key values. .PARAMETER RegistryPath The registry path to get the values from. .PARAMETER ComputerName The computer to get the values from. If not specified, the local computer is used. .PARAMETER ExpandEnvironmentNames Expand environment names in the registry value. By default it doesn't do that. If you want to expand environment names, use this parameter. .EXAMPLE Get-PSRegistry -RegistryPath 'HKLM\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' -ComputerName AD1 .EXAMPLE Get-PSRegistry -RegistryPath 'HKLM\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' .EXAMPLE Get-PSRegistry -RegistryPath "HKLM\SYSTEM\CurrentControlSet\Services\DFSR\Parameters" -ComputerName AD1,AD2,AD3 | ft -AutoSize .EXAMPLE Get-PSRegistry -RegistryPath 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Directory Service' .EXAMPLE Get-PSRegistry -RegistryPath 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Windows PowerShell' | Format-Table -AutoSize .EXAMPLE Get-PSRegistry -RegistryPath 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Directory Service' -ComputerName AD1 -Advanced .EXAMPLE Get-PSRegistry -RegistryPath "HKLM:\Software\Microsoft\Powershell\1\Shellids\Microsoft.Powershell\" .EXAMPLE # Get default key and it's value Get-PSRegistry -RegistryPath "HKEY_CURRENT_USER\Tests" -Key "" .EXAMPLE # Get default key and it's value (alternative) Get-PSRegistry -RegistryPath "HKEY_CURRENT_USER\Tests" -DefaultKey .NOTES General notes #> [cmdletbinding()] param( [alias('Path')][string[]] $RegistryPath, [string[]] $ComputerName = $Env:COMPUTERNAME, [string] $Key, [switch] $Advanced, [switch] $DefaultKey, [switch] $ExpandEnvironmentNames, [Parameter(DontShow)][switch] $DoNotUnmount ) $Script:CurrentGetCount++ Get-PSRegistryDictionaries # Cleans up registry path and makes sure it's as required to be processed further $RegistryPath = Resolve-PrivateRegistry -RegistryPath $RegistryPath [Array] $Computers = Get-ComputerSplit -ComputerName $ComputerName [Array] $RegistryTranslated = Get-PSConvertSpecialRegistry -RegistryPath $RegistryPath -Computers $ComputerName -HiveDictionary $Script:HiveDictionary -ExpandEnvironmentNames:$ExpandEnvironmentNames.IsPresent if ($PSBoundParameters.ContainsKey("Key") -or $DefaultKey) { [Array] $RegistryValues = Get-PSSubRegistryTranslated -RegistryPath $RegistryTranslated -HiveDictionary $Script:HiveDictionary -Key $Key foreach ($Computer in $Computers[0]) { foreach ($R in $RegistryValues) { Get-PSSubRegistry -Registry $R -ComputerName $Computer -ExpandEnvironmentNames:$ExpandEnvironmentNames.IsPresent } } foreach ($Computer in $Computers[1]) { foreach ($R in $RegistryValues) { Get-PSSubRegistry -Registry $R -ComputerName $Computer -Remote -ExpandEnvironmentNames:$ExpandEnvironmentNames.IsPresent } } } else { [Array] $RegistryValues = Get-PSSubRegistryTranslated -RegistryPath $RegistryTranslated -HiveDictionary $Script:HiveDictionary foreach ($Computer in $Computers[0]) { foreach ($R in $RegistryValues) { #Write-Verbose -Message "Getting registry value from $Computer : $($R.Registry)" Get-PSSubRegistryComplete -Registry $R -ComputerName $Computer -Advanced:$Advanced -ExpandEnvironmentNames:$ExpandEnvironmentNames.IsPresent } } foreach ($Computer in $Computers[1]) { foreach ($R in $RegistryValues) { Get-PSSubRegistryComplete -Registry $R -ComputerName $Computer -Remote -Advanced:$Advanced -ExpandEnvironmentNames:$ExpandEnvironmentNames.IsPresent } } } $Script:CurrentGetCount-- if ($Script:CurrentGetCount -eq 0) { if (-not $DoNotUnmount) { Unregister-MountedRegistry } } } function Mount-PSRegistryPath { <# .SYNOPSIS Short description .DESCRIPTION Long description .PARAMETER MountPoint Parameter description .PARAMETER FilePath Parameter description .EXAMPLE Mount-PSRegistryPath -MountPoint 'HKEY_USERS\.DEFAULT_USER111' -FilePath 'C:\Users\Default\NTUSER.DAT' .NOTES General notes #> [alias('Mount-RegistryPath')] [cmdletbinding()] param( [Parameter(Mandatory)][string] $MountPoint, [Parameter(Mandatory)][string] $FilePath ) $pinfo = [System.Diagnostics.ProcessStartInfo]::new() $pinfo.FileName = "reg.exe" $pinfo.RedirectStandardError = $true $pinfo.RedirectStandardOutput = $true $pinfo.UseShellExecute = $false $pinfo.Arguments = " load $MountPoint $FilePath" $pinfo.CreateNoWindow = $true $pinfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden $p = [System.Diagnostics.Process]::new() $p.StartInfo = $pinfo $p.Start() | Out-Null $p.WaitForExit() $Output = $p.StandardOutput.ReadToEnd() $Errors = $p.StandardError.ReadToEnd() if ($Errors) { if ($PSBoundParameters.ErrorAction -eq 'Stop') { throw $Errors } else { Write-Warning -Message "Mount-PSRegistryPath - Couldn't mount $MountPoint. $Errors" } } else { if ($Output -like "*operation completed*") { if (-not $Suppress) { return $true } } } if (-not $Suppress) { return $false } } <# function Mount-PSRegistryPath { [alias('Mount-RegistryPath')] [cmdletbinding()] param( [Parameter(Mandatory)][string] $MountPoint, [Parameter(Mandatory)][string] $FilePath ) # $pinfo = [System.Diagnostics.ProcessStartInfo]::new() # $pinfo.FileName = "reg.exe" # $pinfo.RedirectStandardError = $true # $pinfo.RedirectStandardOutput = $true # $pinfo.UseShellExecute = $false # $pinfo.Arguments = " load $MountPoint $FilePath" # $pinfo.CreateNoWindow = $true # $pinfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden # $p = [System.Diagnostics.Process]::new() # $p.StartInfo = $pinfo # $p.Start() | Out-Null # $p.WaitForExit() # $Output = $p.StandardOutput.ReadToEnd() # $Errors = $p.StandardError.ReadToEnd() $p = reg load $MountPoint $FilePath 2>&1 #$Errors , $Output = $p.Where({ $_ -is [System.Management.Automation.ErrorRecord] }, 'Split') if ($p -is [Array] -and $p[0] -is [System.Management.Automation.ErrorRecord] -or $p -is [System.Management.Automation.ErrorRecord]) { $Errors = $p | ForEach-Object { $_.ToString().Replace('System.Management.Automation.RemoteException', '') } } else { $Output = $p } if ($Errors) { if ($PSBoundParameters.ErrorAction -eq 'Stop') { throw $Errors } else { Write-Warning -Message "Mount-PSRegistryPath - Couldn't mount $MountPoint. $Errors" } } else { if ($Output -like "*operation completed*") { if (-not $Suppress) { return $true } } } if (-not $Suppress) { return $false } } #> function New-PSRegistry { <# .SYNOPSIS Provides a way to create new registry paths .DESCRIPTION Provides a way to create new registry paths .PARAMETER ComputerName The computer to run the command on. Defaults to local computer. .PARAMETER RegistryPath Registry Path to Create .PARAMETER Suppress Suppresses the output of the command. By default the command outputs PSObject with the results of the operation. .EXAMPLE New-PSRegistry -RegistryPath "HKCU:\\Tests1\CurrentControlSet\Control\Lsa" -Verbose -WhatIf .EXAMPLE New-PSRegistry -RegistryPath "HKCU:\\Tests1\CurrentControlSet\Control\Lsa" .NOTES General notes #> [cmdletbinding(SupportsShouldProcess)] param( [string[]] $ComputerName = $Env:COMPUTERNAME, [Parameter(Mandatory)][string] $RegistryPath, [switch] $Suppress ) Get-PSRegistryDictionaries [Array] $ComputersSplit = Get-ComputerSplit -ComputerName $ComputerName # Cleans up registry path and makes sure it's as required to be processed further $RegistryPath = Resolve-PrivateRegistry -RegistryPath $RegistryPath [Array] $RegistryTranslated = Get-PSConvertSpecialRegistry -RegistryPath $RegistryPath -Computers $ComputerName -HiveDictionary $Script:HiveDictionary foreach ($Registry in $RegistryTranslated) { $RegistryValue = Get-PrivateRegistryTranslated -RegistryPath $Registry -HiveDictionary $Script:HiveDictionary -Key $Key -ReverseTypesDictionary $Script:ReverseTypesDictionary if ($RegistryValue.HiveKey) { foreach ($Computer in $ComputersSplit[0]) { # Local computer New-PrivateRegistry -RegistryValue $RegistryValue -Computer $Computer -ErrorAction $ErrorActionPreference -WhatIf:$WhatIfPreference } foreach ($Computer in $ComputersSplit[1]) { # Remote computer New-PrivateRegistry -RegistryValue $RegistryValue -Computer $Computer -Remote -ErrorAction $ErrorActionPreference -WhatIf:$WhatIfPreference } } else { if ($PSBoundParameters.ErrorAction -eq 'Stop') { Unregister-MountedRegistry throw } else { # This shouldn't really happen Write-Warning "New-PSRegistry - Setting registry to $RegistryPath have failed. Couldn't translate HIVE." } } } Unregister-MountedRegistry } function Remove-PSRegistry { <# .SYNOPSIS Remove registry keys and folders .DESCRIPTION Remove registry keys and folders using .NET methods .PARAMETER ComputerName The computer to run the command on. Defaults to local computer. .PARAMETER RegistryPath The registry path to remove. .PARAMETER Key The registry key to remove. .PARAMETER Recursive Forces deletion of registry folder and all keys, including nested folders .PARAMETER Suppress Suppresses the output of the command. By default the command outputs PSObject with the results of the operation. .EXAMPLE Remove-PSRegistry -RegistryPath "HKEY_CURRENT_USER\Tests\Ok\MaybeNot" -Recursive .EXAMPLE Remove-PSRegistry -RegistryPath "HKEY_CURRENT_USER\Tests\Ok\MaybeNot" -Key "LimitBlankPass1wordUse" .EXAMPLE Remove-PSRegistry -RegistryPath "HKCU:\Tests\Ok" .NOTES General notes #> [cmdletBinding(SupportsShouldProcess)] param( [string[]] $ComputerName = $Env:COMPUTERNAME, [Parameter(Mandatory)][string] $RegistryPath, [Parameter()][string] $Key, [switch] $Recursive, [switch] $Suppress ) Get-PSRegistryDictionaries [Array] $ComputersSplit = Get-ComputerSplit -ComputerName $ComputerName # Cleans up registry path and makes sure it's as required to be processed further $RegistryPath = Resolve-PrivateRegistry -RegistryPath $RegistryPath [Array] $RegistryTranslated = Get-PSConvertSpecialRegistry -RegistryPath $RegistryPath -Computers $ComputerName -HiveDictionary $Script:HiveDictionary foreach ($Registry in $RegistryTranslated) { $RegistryValue = Get-PrivateRegistryTranslated -RegistryPath $Registry -HiveDictionary $Script:HiveDictionary -Key $Key -ReverseTypesDictionary $Script:ReverseTypesDictionary if ($RegistryValue.HiveKey) { foreach ($Computer in $ComputersSplit[0]) { # Local computer Remove-PrivateRegistry -Key $Key -RegistryValue $RegistryValue -Computer $Computer -Suppress:$Suppress.IsPresent -ErrorAction $ErrorActionPreference -WhatIf:$WhatIfPreference } foreach ($Computer in $ComputersSplit[1]) { # Remote computer Remove-PrivateRegistry -Key $Key -RegistryValue $RegistryValue -Computer $Computer -Remote -Suppress:$Suppress.IsPresent -ErrorAction $ErrorActionPreference -WhatIf:$WhatIfPreference } } else { if ($PSBoundParameters.ErrorAction -eq 'Stop') { Unregister-MountedRegistry throw } else { # This shouldn't really happen Write-Warning "Remove-PSRegistry - Removing registry $RegistryPath have failed (recursive: $($Recursive.IsPresent)). Couldn't translate HIVE." } } } Unregister-MountedRegistry } function Set-PSRegistry { <# .SYNOPSIS Sets/Updates registry entries locally and remotely using .NET methods. .DESCRIPTION Sets/Updates registry entries locally and remotely using .NET methods. If the registry path to key doesn't exists it will be created. .PARAMETER ComputerName The computer to run the command on. Defaults to local computer. .PARAMETER RegistryPath Registry Path to Update .PARAMETER Type Registry type to use. Options are: REG_SZ, REG_EXPAND_SZ, REG_BINARY, REG_DWORD, REG_MULTI_SZ, REG_QWORD, string, expandstring, binary, dword, multistring, qword .PARAMETER Key Registry key to set. If the path to registry key doesn't exists it will be created. .PARAMETER Value Registry value to set. .PARAMETER Suppress Suppresses the output of the command. By default the command outputs PSObject with the results of the operation. .EXAMPLE Set-PSRegistry -RegistryPath 'HKLM\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics' -Type REG_DWORD -Key "16 LDAP Interface Events" -Value 2 -ComputerName AD1 .EXAMPLE Set-PSRegistry -RegistryPath 'HKLM\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics' -Type REG_SZ -Key "LDAP Interface Events" -Value 'test' -ComputerName AD1 .EXAMPLE Set-PSRegistry -RegistryPath "HKCU:\\Tests" -Key "LimitBlankPass1wordUse" -Value "0" -Type REG_DWORD .EXAMPLE Set-PSRegistry -RegistryPath "HKCU:\\Tests\MoreTests\Tests1" -Key "LimitBlankPass1wordUse" -Value "0" -Type REG_DWORD .EXAMPLE # Setting default value $ValueData = [byte[]] @( 0, 1, 0, 0, 9, 0, 0, 0, 128, 0, 0, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 0, 0, 5, 0, 10, 0, 14, 0, 3, 0, 5, 0, 6, 0, 6, 0, 4, 0, 4, 0 ) Set-PSRegistry -RegistryPath "HKEY_CURRENT_USER\Tests" -Key '' -Value $ValueData -Type 'NONE' .NOTES General notes #> [cmdletbinding(SupportsShouldProcess)] param( [string[]] $ComputerName = $Env:COMPUTERNAME, [Parameter(Mandatory)][string] $RegistryPath, [Parameter(Mandatory)][ValidateSet('REG_SZ', 'REG_NONE', 'None', 'REG_EXPAND_SZ', 'REG_BINARY', 'REG_DWORD', 'REG_MULTI_SZ', 'REG_QWORD', 'string', 'binary', 'dword', 'qword', 'multistring', 'expandstring')][string] $Type, [Parameter()][string] $Key, [Parameter(Mandatory)][object] $Value, [switch] $Suppress ) Unregister-MountedRegistry Get-PSRegistryDictionaries [Array] $ComputersSplit = Get-ComputerSplit -ComputerName $ComputerName # Cleans up registry path and makes sure it's as required to be processed further $RegistryPath = Resolve-PrivateRegistry -RegistryPath $RegistryPath [Array] $RegistryTranslated = Get-PSConvertSpecialRegistry -RegistryPath $RegistryPath -Computers $ComputerName -HiveDictionary $Script:HiveDictionary foreach ($Registry in $RegistryTranslated) { $RegistryValue = Get-PrivateRegistryTranslated -RegistryPath $Registry -HiveDictionary $Script:HiveDictionary -Key $Key -Value $Value -Type $Type -ReverseTypesDictionary $Script:ReverseTypesDictionary if ($RegistryValue.HiveKey) { foreach ($Computer in $ComputersSplit[0]) { # Local computer Set-PrivateRegistry -RegistryValue $RegistryValue -Computer $Computer -Suppress:$Suppress.IsPresent -ErrorAction $ErrorActionPreference -WhatIf:$WhatIfPreference } foreach ($Computer in $ComputersSplit[1]) { # Remote computer Set-PrivateRegistry -RegistryValue $RegistryValue -Computer $Computer -Remote -Suppress:$Suppress.IsPresent -ErrorAction $ErrorActionPreference -WhatIf:$WhatIfPreference } } else { if ($PSBoundParameters.ErrorAction -eq 'Stop') { Unregister-MountedRegistry throw } else { # This shouldn't really happen Write-Warning "Set-PSRegistry - Setting registry to $Registry have failed. Couldn't translate HIVE." } } } Unregister-MountedRegistry } function Test-PSRegistry { [cmdletbinding()] param( [alias('Path')][string[]] $RegistryPath, [string] $ComputerName = $Env:COMPUTERNAME, [string] $Key ) $Output = Get-PSRegistry -RegistryPath $RegistryPath -ComputerName $ComputerName if ($Output.PSConnection -eq $true -and $Output.PSError -eq $false) { if ($Key) { if ($null -ne $Output.$Key) { return $true } else { return $false } } else { return $true } } else { return $false } } 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 Start-Runspace { [cmdletbinding()] param ( [ScriptBlock] $ScriptBlock, [System.Collections.IDictionary] $Parameters, [System.Management.Automation.Runspaces.RunspacePool] $RunspacePool ) if ($ScriptBlock -ne '') { $runspace = [PowerShell]::Create() $null = $runspace.AddScript($ScriptBlock) if ($null -ne $Parameters) { $null = $runspace.AddParameters($Parameters) } $runspace.RunspacePool = $RunspacePool # return data [PSCustomObject]@{ Pipe = $runspace Status = $runspace.BeginInvoke() } } } function Stop-Runspace { [cmdletbinding()] param( [Array] $Runspaces, [string] $FunctionName, [System.Management.Automation.Runspaces.RunspacePool] $RunspacePool, [switch] $ExtendedOutput ) #[Array] $List = while ($Runspaces.Status -ne $null) { [Array] $List = While (@($Runspaces | Where-Object -FilterScript { $null -ne $_.Status }).count -gt 0) { 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() if ($List.Count -eq 1) { return , $List } else { return $List } } function Get-PSService { <# .SYNOPSIS Alternative way to Get-Service .DESCRIPTION Alternative way to Get-Service which works using CIM queries .PARAMETER ComputerName ComputerName(s) to query for services .PARAMETER Protocol Protocol to use to gather services .PARAMETER Service Limit output to just few services .PARAMETER All Return all data without filtering .PARAMETER Extended Return more data .EXAMPLE Get-PSService -ComputerName AD1, AD2, AD3, AD4 -Service 'Dnscache', 'DNS', 'PeerDistSvc', 'WebClient', 'Netlogon' .EXAMPLE Get-PSService -ComputerName AD1, AD2 -Extended .EXAMPLE Get-PSService .EXAMPLE Get-PSService -Extended .NOTES General notes #> [cmdletBinding()] param( [alias('Computer', 'Computers')][string[]] $ComputerName = $Env:COMPUTERNAME, [ValidateSet('Default', 'Dcom', 'Wsman')][string] $Protocol = 'Default', [alias('Services')][string[]] $Service, [switch] $All, [switch] $Extended ) [string] $Class = 'win32_service' [string] $Properties = '*' <# Disabled as per https://github.com/EvotecIT/PSSharedGoods/issues/14 if ($All) { [string] $Properties = '*' } else { [string[]] $Properties = @( 'Name' 'Status' 'ExitCode' 'DesktopInteract' 'ErrorControl' 'PathName' 'ServiceType' 'StartMode' 'Caption' 'Description' #'InstallDate' 'Started' 'SystemName' 'AcceptPause' 'AcceptStop' 'DisplayName' 'ServiceSpecificExitCode' 'StartName' 'State' 'TagId' 'CheckPoint' 'DelayedAutoStart' 'ProcessId' 'WaitHint' 'PSComputerName' ) } #> # instead of looping multiple times we create cache for services if ($Service) { $CachedServices = @{} foreach ($S in $Service) { $CachedServices[$S] = $true } } $Information = Get-CimData -ComputerName $ComputerName -Protocol $Protocol -Class $Class -Properties $Properties if ($All) { if ($Service) { foreach ($Entry in $Information) { if ($CachedServices[$Entry.Name]) { $Entry } } } else { $Information } } else { foreach ($Data in $Information) { # # Remember to expand if changing properties above if ($Service) { if (-not $CachedServices[$Data.Name]) { continue } } $OutputService = [ordered] @{ ComputerName = if ($Data.PSComputerName) { $Data.PSComputerName } else { $Env:COMPUTERNAME } Status = $Data.State Name = $Data.Name ServiceType = $Data.ServiceType StartType = $Data.StartMode DisplayName = $Data.DisplayName } if ($Extended) { $OutputServiceExtended = [ordered] @{ StatusOther = $Data.Status ExitCode = $Data.ExitCode DesktopInteract = $Data.DesktopInteract ErrorControl = $Data.ErrorControl PathName = $Data.PathName Caption = $Data.Caption Description = $Data.Description #InstallDate = $Data.InstallDate Started = $Data.Started SystemName = $Data.SystemName AcceptPause = $Data.AcceptPause AcceptStop = $Data.AcceptStop ServiceSpecificExitCode = $Data.ServiceSpecificExitCode StartName = $Data.StartName #State = $Data.State TagId = $Data.TagId CheckPoint = $Data.CheckPoint DelayedAutoStart = $Data.DelayedAutoStart ProcessId = $Data.ProcessId WaitHint = $Data.WaitHint } [PSCustomObject] ($OutputService + $OutputServiceExtended) } else { [PSCustomObject] $OutputService } } } } function Set-ServiceRecovery { <# .SYNOPSIS # .DESCRIPTION Long description .PARAMETER ServiceDisplayName Parameter description .PARAMETER Server Parameter description .PARAMETER action1 Parameter description .PARAMETER time1 Parameter description .PARAMETER action2 Parameter description .PARAMETER time2 Parameter description .PARAMETER actionLast Parameter description .PARAMETER timeLast Parameter description .PARAMETER resetCounter Parameter description .EXAMPLE Set-ServiceRecovery -ServiceDisplayName "Pulseway" -Server "MAIL1" .NOTES General notes #> [alias('Set-Recovery')] param ( [string] [Parameter(Mandatory = $true)] $ServiceDisplayName, [string] [Parameter(Mandatory = $true)] $Server, [string] $action1 = "restart", [int] $time1 = 30000, # in miliseconds [string] $action2 = "restart", [int] $time2 = 30000, # in miliseconds [string] $actionLast = "restart", [int] $timeLast = 30000, # in miliseconds [int] $resetCounter = 4000 # in seconds ) $serverPath = "\\" + $server $services = Get-CimInstance -ClassName 'Win32_Service' -ComputerName $Server | Where-Object { $_.DisplayName -imatch $ServiceDisplayName } $action = $action1 + "/" + $time1 + "/" + $action2 + "/" + $time2 + "/" + $actionLast + "/" + $timeLast foreach ($service in $services) { # https://technet.microsoft.com/en-us/library/cc742019.aspx $output = sc.exe $serverPath failure $($service.Name) actions= $action reset= $resetCounter } } 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 $SqlDatabase = $SqlDatabase.Replace('[', '').Replace(']', '') # makes sure we know what we have $SqlDatabase = "[$SqlDatabase]" $Query = "SELECT * FROM $SqlDatabase.INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '$Table'" $SqlReturn = @( try { Invoke-DbaQuery -ErrorAction Stop -SqlInstance $SqlServer -Query $Query #-Verbose } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " "Error occured (Get-SqlQueryColumnInformation): $ErrorMessage" # return of error } ) return $SQLReturn } 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 ($null -ne $Object) { ## 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) -Force } if (-not $O.AddedWho) { Add-Member -InputObject $O -MemberType NoteProperty -Name "AddedWho" -Value ($Env:USERNAME) -Force } $DuplicateString = [System.Text.StringBuilder]::new() 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 { foreach ($ColumnName in $SqlSettings.SqlCheckBeforeInsert) { $DuplicateColumn = $ColumnName.Replace("[", '').Replace("]", '') # Remove [ ] for comparision if ($MapValueSplit[0] -eq $DuplicateColumn) { if ($DuplicateString.Length -ne 0) { # Means something is already in string so most likely 2nd run $null = $DuplicateString.Append(" AND ") } $null = $DuplicateString.Append("[$DuplicateColumn] = '$FieldValue'") } } Add-ToArray -List $ArrayValues -Element "'$FieldValue'" } } } } if ($ArrayKeys) { if ($null -ne $SqlSettings.SqlCheckBeforeInsert -and $DuplicateString.Length -gt 0) { 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 $($DuplicateString.ToString())" 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", "") } } } 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) { $MapValue = $TableMapping.$MapKey $Field = $MapValue -Split ',' if ($ExistingColumns -notcontains $MapKey -and $ExistingColumns -notcontains $Field[0]) { 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])" } } 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 { # Gets the highest object foreach ($O in $Properties.HighestObject) { # 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" #Write-Verbose "New-SqlTableMapping - FieldName: $FieldName FieldValue: $FieldValue FieldNameSQL: $FieldNameSQL FieldDataType: DateTime" } elseif ($FieldValue -is [int] -or $FieldValue -is [Int64]) { $TableMapping.$FieldName = "$FieldNameSQL,[bigint]" # Write-Verbose "New-SqlTableMapping - FieldName: $FieldName FieldValue: $FieldValue FieldNameSQL: $FieldNameSQL FieldDataType: BigInt" } elseif ($FieldValue -is [bool]) { $TableMapping.$FieldName = "$FieldNameSQL,[bit]" #Write-Verbose "New-SqlTableMapping - FieldName: $FieldName FieldValue: $FieldValue FieldNameSQL: $FieldNameSQL FieldDataType: Bit/Bool" } else { $TableMapping.$FieldName = "$FieldNameSQL" #Write-Verbose "New-SqlTableMapping - FieldName: $FieldName FieldValue: $FieldValue FieldNameSQL: $FieldNameSQL FieldDataType: NvarChar" } } } } } #Write-Verbose 'New-SqlTableMapping - Ending' return $TableMapping } function Send-SqlInsert { [CmdletBinding()] param( [Array] $Object, [System.Collections.IDictionary] $SqlSettings ) 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 ($null -eq $SqlTable) { 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" return "Error occured: SQL Table doesn't exists. SqlTableCreate option is disabled" } } 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 } } } $Queries = @( if ($CreateTableSQL) { foreach ($Sql in $CreateTableSQL) { $Sql } } if ($AlterTableSQL) { foreach ($Sql in $AlterTableSQL) { $Sql } } $SqlQueries = New-SqlQuery -Object $Object -SqlSettings $SqlSettings -TableMapping $TableMapping foreach ($Sql in $SqlQueries) { $Sql } ) $ReturnData = foreach ($Query in $Queries) { try { if ($Query) { $Query # return query to log Invoke-DbaQuery -SqlInstance "$($SqlSettings.SqlServer)" -Database "$($SqlSettings.SqlDatabase)" -Query $Query -ErrorAction Stop # return output } } catch { $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " " "Error occured (Send-SqlInsert): $ErrorMessage" # return data } } return $ReturnData } function Find-TypesNeeded { [CmdletBinding()] param ( [Array] $TypesRequired, [Array] $TypesNeeded ) [bool] $Found = $False foreach ($Type in $TypesNeeded) { if ($TypesRequired -contains $Type) { $Found = $true break } } return $Found } Function Get-ModulesAvailability { [cmdletBinding()] 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 Search-Command { [cmdletbinding()] param ( [string] $CommandName ) return [bool](Get-Command -Name $CommandName -ErrorAction SilentlyContinue) } function Test-AvailabilityCommands { [cmdletBinding()] param ( [string[]] $Commands ) $CommandsStatus = foreach ($Command in $Commands) { $Exists = Get-Command -Name $Command -ErrorAction SilentlyContinue if ($Exists) { Write-Verbose "Test-AvailabilityCommands - Command $Command is available." } else { Write-Verbose "Test-AvailabilityCommands - Command $Command is not available." } $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 = [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).Status } if ($Test -eq 'All' -or '*PortOpen*') { $Output.PortOpen = (Test-ComputerPort -Server $Server -PortTCP $Ports -Timeout $PortsTimeout).Status } [PSCustomObject] $Output } ) return $OutputList } #Test-ComputerAvailability -Servers AD1,AD2 function Test-ComputerPort { [CmdletBinding()] param ( [alias('Server')][string[]] $ComputerName, [int[]] $PortTCP, [int[]] $PortUDP, [int]$Timeout = 5000 ) begin { if ($Global:ProgressPreference -ne 'SilentlyContinue') { $TemporaryProgress = $Global:ProgressPreference $Global:ProgressPreference = 'SilentlyContinue' } } process { foreach ($Computer in $ComputerName) { foreach ($P in $PortTCP) { $Output = [ordered] @{ 'ComputerName' = $Computer 'Port' = $P 'Protocol' = 'TCP' 'Status' = $null 'Summary' = $null 'Response' = $null } <# $TcpClient = [System.Net.Sockets.TcpClient]::new() $Connect = $TcpClient.BeginConnect($Computer, $P, $null, $null) $Wait = $Connect.AsyncWaitHandle.WaitOne($Timeout, $false) if (!$Wait) { $TcpClient.Close() $Output['Status'] = $false $Output['Summary'] = "TCP $P Failed" } else { $TcpClient.EndConnect($Connect) $TcpClient.Close() $Output['Status'] = $true $Output['Summary'] = "TCP $P Successful" } $TcpClient.Close() $TcpClient.Dispose() #> $TcpClient = Test-NetConnection -ComputerName $Computer -Port $P -InformationLevel Detailed -WarningAction SilentlyContinue if ($TcpClient.TcpTestSucceeded) { $Output['Status'] = $TcpClient.TcpTestSucceeded $Output['Summary'] = "TCP $P Successful" } else { $Output['Status'] = $false $Output['Summary'] = "TCP $P Failed" $Output['Response'] = $Warnings } [PSCustomObject]$Output } foreach ($P in $PortUDP) { $Output = [ordered] @{ 'ComputerName' = $Computer 'Port' = $P 'Protocol' = 'UDP' 'Status' = $null 'Summary' = $null } $UdpClient = [System.Net.Sockets.UdpClient]::new($Computer, $P) $UdpClient.Client.ReceiveTimeout = $Timeout # $UdpClient.Connect($Computer, $P) $Encoding = [System.Text.ASCIIEncoding]::new() $byte = $Encoding.GetBytes("Evotec") [void]$UdpClient.Send($byte, $byte.length) $RemoteEndpoint = [System.Net.IPEndPoint]::new([System.Net.IPAddress]::Any, 0) try { $Bytes = $UdpClient.Receive([ref]$RemoteEndpoint) [string]$Data = $Encoding.GetString($Bytes) If ($Data) { $Output['Status'] = $true $Output['Summary'] = "UDP $P Successful" $Output['Response'] = $Data } } catch { $Output['Status'] = $false $Output['Summary'] = "UDP $P Failed" $Output['Response'] = $_.Exception.Message } $UdpClient.Close() $UdpClient.Dispose() [PSCustomObject]$Output } } } end { # Bring back setting as per default if ($TemporaryProgress) { $Global:ProgressPreference = $TemporaryProgress } } } #Test-ComputerPort -ComputerName 'AD1', 'AD2' -PortTCP 25, 88, 389, 464, 636, 5722, 9389 | Format-Table -AutoSize #Test-ComputerPort -ComputerName 'AD1' -PortTCP 25, 88, 389, 464, 636, 5722, 9389 -PortUDP 88, 123, 389, 464 | ft -AutoSize #Test-ComputerPort -ComputerName 'AD2' -PortTCP 53 -PortUDP 53 | ft -AutoSize #Test-ComputerPort -PortUDP 53 -ComputerName AD1,AD2 | ft -AutoSize #Test-ComputerPort -ComputerName AD1,AD7 -PortTCP 53, 3389 | ft -a 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" @{ Status = $false; Output = $User.SamAccountName; Extended = "Credentials configuration $Key is Null or Empty!" } } } return $Object } function Test-ForestConnectivity { [CmdletBinding()] param( ) Try { $null = Get-ADForest return $true } catch { #Write-Warning 'No connectivity to forest/domain.' return $False } } function Test-Key { [CmdletBinding()] param( $ConfigurationTable, $ConfigurationSection = "", $ConfigurationKey, $DisplayProgress = $false ) if ($null -eq $ConfigurationTable) { 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 { [CmdletBinding()] param( ) if (Search-Command -CommandName 'Get-AdForest') { # future use } else { Write-Warning 'Modules required to run not found.' Exit } } function Test-WinRM { [CmdletBinding()] param ( [alias('Server')][string[]] $ComputerName ) $Output = foreach ($Computer in $ComputerName) { $Test = [PSCustomObject] @{ Output = $null Status = $null ComputerName = $Computer } try { $Test.Output = Test-WSMan -ComputerName $Computer -ErrorAction Stop $Test.Status = $true } catch { $Test.Status = $false } $Test } $Output } #Test-WinRM -ComputerName AD1, AD2 <# function Get-TimeSynchronization { [CmdletBinding()] 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-TimeSettings { [alias('Get-TimeSynchronization')] param( [string[]] $ComputerName, # [string] $Domain, [switch] $Formatted, [string] $Splitter ) # https://icookservers.blog/2014/09/12/windows-ntp-server-cookbook/ # https://docs.microsoft.com/en-us/windows-server/networking/windows-time-service/windows-time-service-tools-and-settings $Types = @{ NT5DS = 'The time service synchronizes from the domain hierarchy.' # Use on computers that are joined to a domain. NTP = 'The time service synchronizes from the servers specified in the NtpServer registry entry.' #Use on computers that are not joined to a domain. ALLSync = 'The time service uses all the available synchronization mechanisms.' NoSync = 'The time service does not synchronize with other sources.' # The default value on domain members is NT5DS. The default value on stand-alone clients and servers is NTP. } [flags()] enum NtpServerFlags { None = 0 SpecialInterval = 0x1 # flag indicate sync time with external server in special interval configured in “SpecialPollInterval” registry value. UseAsFallbackOnly = 0x2 # use this as UseAsFallbackOnly time source – if primary is not available then sync to this server. SymmetricActive = 0x4 # For more information about this mode, see Windows Time Server: 3.3 Modes of Operation. Client = 0x8 # use client mode association while sync time to external time source. } $CrossSiteSyncFlags = @{ '0' = 'None' '1' = 'PdcOnly' '2' = 'All' # Entry determines whether the service chooses synchronization partners outside the domain of the computer. The options and values are: # This value is ignored if the NT5DS value is not set. The default value for domain members is 2. The default value for stand-alone clients and servers is 2. } $AnnounceFlags = @{ '0' = 'Not a time server' '1' = 'Always time server' '2' = 'Automatic time server' '4' = 'Always reliable time server' '8' = 'Automatic reliable time server' '10' = 'The default value for domain members is 10. The default value for stand-alone clients and servers is 10.' } # if ($Domain) { # $DomainInformation = Get-ADDomain -Server $Domain # $PDCName = $DomainInformation.PDCEmulator # } foreach ($_ in $ComputerName) { [bool] $AppliedGPO = $false $TimeParameters = Get-PSRegistry -ComputerName $_ -RegistryPath "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\W32time\Parameters" if ($null -eq $TimeParameters.NtpServer) { $TimeParameters = Get-PSRegistry -ComputerName $_ -RegistryPath "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" $AppliedGPO = $true } $TimeConfig = Get-PSRegistry -ComputerName $_ -RegistryPath "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Config" #Get-PSRegistry -ComputerName $ComputerName -RegistryPath "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Security" $TimeNTPClient = Get-PSRegistry -ComputerName $_ -RegistryPath "HKLM\SOFTWARE\Policies\Microsoft\W32time\TimeProviders\NtpClient" if ($null -eq $TimeNTPClient.CrossSiteSyncFlags) { $TimeNTPClient = Get-PSRegistry -ComputerName $_ -RegistryPath "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NTPClient" } $TimeNTPServer = Get-PSRegistry -ComputerName $_ -RegistryPath "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NTPServer" #$TimeSecureLimits = Get-PSRegistry -ComputerName $_ -RegistryPath "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\SecureTimeLimits" $TimeVMProvider = Get-PSRegistry -ComputerName $ComputerName -RegistryPath "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\VMICTimeProvider" #Get-PSRegistry -ComputerName $ComputerName -RegistryPath "HKLM\SYSTEM\CurrentControlSet" $NtpServers = $TimeParameters.NtpServer -split ' ' $Ntp = foreach ($NtpServer in $NtpServers) { $SplitNTP = $NtpServer -split ',' if ($SplitNTP.Count -eq 2) { # sanity check what should be a small hex value if ($flagVal = $SplitNTP[1] -as [int]) { # make sure it's within the bounds of our supported flags if ($flags = $flagVal -as [NtpServerFlags]) { $Intervals = $flags.ToString().Replace(', ', '+') } else { Write-Warning -Message "Get-TimeSettings - NtpServer flag value `"$flagVal`" could not be converted to NtpServerFlags enum" $Intervals = 'Incorrect' } } else { Write-Warning -Message "Get-TimeSettings - NtpServer flag value `"$($SplitNTP[1])`" could not be parsed as an integer" $Intervals = 'Incorrect' } } else { $Intervals = 'Missing' } [PSCustomObject] @{ NtpServer = $SplitNTP[0] Intervals = $Intervals } } #$FullName = Resolve-DnsName -Name $_ -ErrorAction SilentlyContinue [PSCustomObject] @{ ComputerName = $_ #IsPDC = ($PDCName -eq $FullName.Name) NtpServer = if ($Splitter) { $Ntp.NtpServer -join $Splitter } else { $Ntp.NtpServer } NtpServerIntervals = if ($Splitter) { $Ntp.Intervals -join $Splitter } else { $Ntp.Intervals } NtpType = $TimeParameters.Type NtpTypeComment = $Types["$($TimeParameters.Type)"] AppliedGPO = $AppliedGPO VMTimeProvider = [bool] $TimeVMProvider.Enabled AnnounceFlags = $TimeConfig.AnnounceFlags AnnounceFlagsComment = $AnnounceFlags["$($TimeConfig.AnnounceFlags)"] NtpServerEnabled = [bool]$TimeNTPServer.Enabled NtpServerInputProvider = [bool]$TimeNTPServer.InputProvider MaxPosPhaseCorrection = $TimeConfig.MaxPosPhaseCorrection MaxnegPhaseCorrection = $TimeConfig.MaxnegPhaseCorrection MaxAllowedPhaseOffset = $TimeConfig.MaxAllowedPhaseOffset MaxPollInterval = $TimeConfig.MaxPollInterval MinPollInterval = $TimeConfig.MinPollInterval UpdateInterval = $TimeConfig.UpdateInterval ResolvePeerBackoffMinutes = $TimeNTPClient.ResolvePeerBackoffMinutes ResolvePeerBackoffMaxTimes = $TimeNTPClient.ResolvePeerBackoffMaxTimes SpecialPollInterval = $TimeNTPClient.SpecialPollInterval EventLogFlags = $TimeConfig.EventLogFlags NtpClientEnabled = [bool] $TimeNTPClient.Enabled NtpClientCrossSiteSyncFlags = $CrossSiteSyncFlags["$($TimeNTPClient.CrossSiteSyncFlags)"] NtpClientInputProvider = [bool] $TimeNTPClient.InputProvider TimeNTPClient = $TimeNTPClient.SpecialPollInterval } } } #Get-TimeSetttings -ComputerName DC1 | ft -AutoSize * #Get-TimeSetttings -ComputerName AD1.ad.evotec.xyz | ft -AutoSize * #Get-PSRegistry -ComputerName AD1 -RegistryPath "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" #Get-PSRegistry -ComputerName AD1 -RegistryPath "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\W32time\Parameters" #Get-PSRegistry -ComputerName DC1 -RegistryPath "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" #Get-PSRegistry -ComputerName DC1 -RegistryPath "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\W32time\Parameters" #$ComputersDC = @( 'AD1', 'AD2', 'AD3', 'DC1', 'EVOWIN', 'ADPreview2019') #Get-PSRegistry -ComputerName AD1 -RegistryPath "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters", "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Config" #Get-PSRegistry -ComputerName EVOWIN -RegistryPath "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Security" #Get-PSRegistry -ComputerName AD1 -RegistryPath "HKLM\SYSTEM\CurrentControlSet" <# Get-PSRegistry -ComputerName $ComputersDC -RegistryPath "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" | ft -AutoSize * Get-PSRegistry -ComputerName $ComputersDC -RegistryPath "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Config" | ft -AutoSize * Get-PSRegistry -ComputerName $ComputersDC -RegistryPath "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NTPClient" | ft -AutoSize * Get-PSRegistry -ComputerName $ComputersDC -RegistryPath "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NTPServer" | ft -AutoSize * Get-PSRegistry -ComputerName $ComputersDC -RegistryPath "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\VMICTimeProvider" | ft -AutoSize * Get-PSRegistry -ComputerName $ComputersDC -RegistryPath "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\SecureTimeLimits" | ft -AutoSize * Get-PSRegistry -ComputerName $ComputersDC -RegistryPath "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\SecureTimeLimits\RunTime" | ft -AutoSize * #> # https://www.mczerniawski.pl/hyperv/proper-time-configuration-for-virtualized-dc/ #https://docs.microsoft.com/en-us/powershell/scripting/samples/working-with-registry-entries?view=powershell-6 # https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc773263(v=ws.10) # HKLM\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\VMICTimeProvider #$ComputersDC = @('EVOWIN', 'ADPreview2019') #Get-TimeSetttings -ComputerName $ComputersDC | Out-HtmlView #get-TimeSetttings -ComputerName $ComputersDC | ft -a * 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 Measure-Collection { param( [string] $Name, [ScriptBlock] $ScriptBlock ) $Time = [System.Diagnostics.Stopwatch]::StartNew() Invoke-Command -ScriptBlock $ScriptBlock $Time.Stop() "Name: $Name, $($Time.Elapsed.Days) days, $($Time.Elapsed.Hours) hours, $($Time.Elapsed.Minutes) minutes, $($Time.Elapsed.Seconds) seconds, $($Time.Elapsed.Milliseconds) milliseconds, ticks $($Time.Elapsed.Ticks)" } 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 } function Start-TimeLog { [CmdletBinding()] param() [System.Diagnostics.Stopwatch]::StartNew() } 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 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 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 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 Get-ProtocolDefaults { <# .SYNOPSIS Gets a list of default settings for SSL/TLS protocols .DESCRIPTION Gets a list of default settings for SSL/TLS protocols .PARAMETER WindowsVersion Windows Version to search for .PARAMETER AsList If true, returns a list of protocol names for all Windows Versions, otherwise returns a single entry for the specified Windows Version .EXAMPLE Get-ProtocolDefaults -AsList | Format-Table .EXAMPLE Get-ProtocolDefaults -WindowsVersion 'Windows 10 1809' | Format-Table .NOTES Based on: https://docs.microsoft.com/en-us/windows/win32/secauthn/protocols-in-tls-ssl--schannel-ssp- According to this https://github.com/MicrosoftDocs/windowsserverdocs/issues/2783 SCHANNEL service requires direct enablement so the list is kind of half useful #> [cmdletbinding(DefaultParameterSetName = 'WindowsVersion')] param( [Parameter(Mandatory, ParameterSetName = 'WindowsVersion')][string] $WindowsVersion, [Parameter(Mandatory, ParameterSetName = 'AsList')][switch] $AsList ) $Defaults = [ordered] @{ 'Windows Server 2022' = [ordered] @{ 'Version' = 'Windows Server 2022' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Enabled' 'TLS13Server' = 'Enabled' } 'Windows Server 2019 20H2' = [ordered] @{ 'Version' = 'Windows Server 2019 20H2' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } 'Windows Server 2019 2004' = [ordered] @{ 'Version' = 'Windows Server 2019 2004' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } 'Windows Server 2019 1909' = [ordered] @{ 'Version' = 'Windows Server 2019 1909' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } "Windows Server 2019 1903" = [ordered] @{ 'Version' = 'Windows Server 2019 1903' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } "Windows Server 2019 1809" = [ordered] @{ 'Version' = 'Windows Server 2019 1809' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } "Windows Server 2016 1803" = [ordered] @{ 'Version' = 'Windows Server 2016 1803' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } "Windows Server 2016 1607" = [ordered] @{ 'Version' = 'Windows Server 2019 1607' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } 'Windows Server 2012 R2' = [ordered] @{ 'Version' = 'Windows Server 2012 R2' 'PCT10' = 'Not supported' 'SSL2Client' = 'Disabled' 'SSL2Server' = 'Disabled' 'SSL3Client' = 'Enabled' 'SSL3Server' = 'Enabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } 'Windows Server 2012' = [ordered] @{ 'Version' = 'Windows Server 2012' 'PCT10' = 'Not supported' 'SSL2Client' = 'Disabled' 'SSL2Server' = 'Disabled' 'SSL3Client' = 'Enabled' 'SSL3Server' = 'Enabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } 'Windows Server 2008 R2' = [ordered] @{ 'Version' = 'Windows Server 2008 R2' 'PCT10' = 'Not supported' 'SSL2Client' = 'Disabled' 'SSL2Server' = 'Enabled' 'SSL3Client' = 'Enabled' 'SSL3Server' = 'Enabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Disabled' 'TLS11Server' = 'Disabled' 'TLS12Client' = 'Disabled' 'TLS12Server' = 'Disabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } 'Windows Server 2008' = [ordered] @{ 'Version' = 'Windows Server 2008' 'PCT10' = 'Not supported' 'SSL2Client' = 'Disabled' 'SSL2Server' = 'Enabled' 'SSL3Client' = 'Enabled' 'SSL3Server' = 'Enabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Disabled' 'TLS11Server' = 'Disabled' 'TLS12Client' = 'Disabled' 'TLS12Server' = 'Disabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } 'Windows 11 21H2' = [ordered] @{ 'Version' = 'Windows 11 21H2' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Enabled' 'TLS13Server' = 'Enabled' } 'Windows 10 21H1' = [ordered] @{ 'Version' = 'Windows 10 21H1' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } 'Windows 10 20H2' = [ordered] @{ 'Version' = 'Windows 10 20H2' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } 'Windows 10 2004' = [ordered] @{ 'Version' = 'Windows 10 2004' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } 'Windows 10 Insider Preview' = [ordered] @{ 'Version' = 'Windows 10 Insider Preview' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } "Windows 10 1909" = [ordered] @{ 'Version' = 'Windows 10 1909' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } "Windows 10 1903" = [ordered] @{ 'Version' = 'Windows 10 1903' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } "Windows 10 1809" = [ordered] @{ 'Version' = 'Windows 10 1809' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } "Windows 10 1803" = [ordered] @{ 'Version' = 'Windows 10 1803' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } "Windows 10 1709" = [ordered] @{ 'Version' = 'Windows 10 1709' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } "Windows 10 1703" = [ordered] @{ 'Version' = 'Windows 10 1703' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } "Windows 10 1607" = [ordered] @{ 'Version' = 'Windows 10 1607' 'PCT10' = 'Not supported' 'SSL2Client' = 'Not supported' 'SSL2Server' = 'Not supported' 'SSL3Client' = 'Disabled' 'SSL3Server' = 'Disabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } "Windows 10 1511" = [ordered] @{ 'Version' = 'Windows 10 1511' 'PCT10' = 'Not supported' 'SSL2Client' = 'Disabled' 'SSL2Server' = 'Disabled' 'SSL3Client' = 'Enabled' 'SSL3Server' = 'Enabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } "Windows 10 1507" = [ordered] @{ 'Version' = 'Windows 10 1507' 'PCT10' = 'Not supported' 'SSL2Client' = 'Disabled' 'SSL2Server' = 'Disabled' 'SSL3Client' = 'Enabled' 'SSL3Server' = 'Enabled' 'TLS10Client' = 'Enabled' 'TLS10Server' = 'Enabled' 'TLS11Client' = 'Enabled' 'TLS11Server' = 'Enabled' 'TLS12Client' = 'Enabled' 'TLS12Server' = 'Enabled' 'TLS13Client' = 'Not supported' 'TLS13Server' = 'Not supported' } } if ($AsList) { foreach ($Key in $Defaults.Keys) { [PSCustomObject] $Defaults[$Key] } } else { if ($Defaults[$WindowsVersion]) { $Defaults[$WindowsVersion] } else { [ordered] @{ 'Version' = 'Unknown' 'PCT10' = 'Unknown' 'SSL2Client' = 'Unknown' 'SSL2Server' = 'Unknown' 'SSL3Client' = 'Unknown' 'SSL3Server' = 'Unknown' 'TLS10Client' = 'Unknown' 'TLS10Server' = 'Unknown' 'TLS11Client' = 'Unknown' 'TLS11Server' = 'Unknown' 'TLS12Client' = 'Unknown' 'TLS12Server' = 'Unknown' 'TLS13Client' = 'Unknown' 'TLS13Server' = 'Unknown' } } } } Add-Type -TypeDefinition @" public enum RGBColors { None, Black, Navy, DarkBlue, MediumBlue, Blue, DarkGreen, Green, Teal, DarkCyan, DeepSkyBlue, DarkTurquoise, MediumSpringGreen, Lime, SpringGreen, Aqua, Cyan, MidnightBlue, DodgerBlue, LightSeaGreen, ForestGreen, SeaGreen, DarkSlateGray, DarkSlateGrey, LimeGreen, MediumSeaGreen, Turquoise, RoyalBlue, SteelBlue, DarkSlateBlue, MediumTurquoise, Indigo, DarkOliveGreen, CadetBlue, CornflowerBlue, MediumAquamarine, DimGray, DimGrey, SlateBlue, OliveDrab, SlateGray, SlateGrey, LightSlateGray, LightSlateGrey, MediumSlateBlue, LawnGreen, Chartreuse, Aquamarine, Maroon, Purple, Olive, Grey, Gray, //Grey, SkyBlue, LightSkyBlue, BlueViolet, DarkRed, DarkMagenta, SaddleBrown, DarkSeaGreen, LightGreen, MediumPurple, DarkViolet, PaleGreen, DarkOrchid, YellowGreen, Sienna, Brown, DarkGray, DarkGrey, LightBlue, GreenYellow, PaleTurquoise, LightSteelBlue, PowderBlue, FireBrick, DarkGoldenrod, MediumOrchid, RosyBrown, DarkKhaki, Silver, MediumVioletRed, IndianRed, Peru, Chocolate, Tan, LightGray, LightGrey, Thistle, Orchid, Goldenrod, PaleVioletRed, Crimson, Gainsboro, Plum, BurlyWood, LightCyan, Lavender, DarkSalmon, Violet, PaleGoldenrod, LightCoral, Khaki, AliceBlue, Honeydew, Azure, SandyBrown, Wheat, Beige, WhiteSmoke, MintCream, GhostWhite, Salmon, AntiqueWhite, Linen, LightGoldenrodYellow, OldLace, Red, Fuchsia, Magenta, DeepPink, OrangeRed, Tomato, HotPink, Coral, DarkOrange, LightSalmon, Orange, LightPink, Pink, Gold, PeachPuff, NavajoWhite, Moccasin, Bisque, MistyRose, BlanchedAlmond, PapayaWhip, LavenderBlush, Seashell, Cornsilk, LemonChiffon, FloralWhite, Snow, Yellow, LightYellow, Ivory, White } "@ # Export functions and aliases as required Export-ModuleMember -Function @('Add-ToArray', 'Add-ToArrayAdvanced', 'Add-ToHashTable', 'Add-WinADUserGroups', 'Clear-DataInformation', 'Compare-MultipleObjects', 'Compare-ObjectProperties', 'Compare-ObjectsAdvanced', 'Convert-ADGuidToSchema', 'Convert-ADSchemaToGuid', 'Convert-BinaryToHex', 'Convert-BinaryToString', 'Convert-Color', 'Convert-CountryCodeToCountry', 'Convert-CountryToContinent', 'Convert-CountryToCountryCode', 'Convert-DomainFqdnToNetBIOS', 'Convert-DomainToSid', 'Convert-ExchangeEmail', 'Convert-ExchangeItems', 'Convert-ExchangeRecipient', 'Convert-ExchangeSize', 'ConvertFrom-Color', 'ConvertFrom-DistinguishedName', 'ConvertFrom-ErrorRecord', 'ConvertFrom-LanguageCode', 'ConvertFrom-NetbiosName', 'ConvertFrom-ObjectToString', 'ConvertFrom-OperationType', 'ConvertFrom-ScriptBlock', 'ConvertFrom-SID', 'ConvertFrom-X500Address', 'Convert-HexToBinary', 'Convert-Identity', 'Convert-KeyToKeyValue', 'Convert-Office365License', 'Convert-Size', 'Convert-TimeToDays', 'Convert-ToDateTime', 'ConvertTo-DistinguishedName', 'ConvertTo-FlatHashtable', 'ConvertTo-FlatObject', 'ConvertTo-Identity', 'ConvertTo-ImmutableID', 'ConvertTo-JsonLiteral', 'ConvertTo-OperatingSystem', 'ConvertTo-OrderedDictionary', 'ConvertTo-SID', 'Convert-ToTimeSpan', 'Convert-TwoArraysIntoOne', 'Convert-UAC', 'Convert-UserAccountControl', 'Copy-Dictionary', 'Copy-DictionaryManual', 'Dismount-PSRegistryPath', 'Find-ADConnectServer', 'Find-DatesCurrentDayMinusDayX', 'Find-DatesCurrentDayMinuxDaysX', 'Find-DatesCurrentHour', 'Find-DatesDayPrevious', 'Find-DatesDayToday', 'Find-DatesMonthCurrent', 'Find-DatesMonthPast', 'Find-DatesPastHour', 'Find-DatesPastWeek', 'Find-DatesQuarterCurrent', 'Find-DatesQuarterLast', 'Find-ExchangeServer', 'Find-HyperVServer', 'Find-MyProgramData', 'Find-ServerTypes', 'Find-TypesNeeded', 'Find-UsersProxyAddressesStatus', 'Format-AddSpaceToSentence', 'Format-FirstXChars', 'Format-PSTable', 'Format-Stream', 'Format-ToTitleCase', 'Format-TransposeTable', 'Format-View', 'Get-ADADministrativeGroups', 'Get-ADEncryptionTypes', 'Get-ADTrustAttributes', 'Get-CimData', 'Get-Colors', 'Get-Computer', 'Get-ComputerApplication', 'Get-ComputerBios', 'Get-ComputerCPU', 'Get-ComputerCulture', 'Get-ComputerDevice', 'Get-ComputerDisk', 'Get-ComputerDiskLogical', 'Get-ComputerMissingDrivers', 'Get-ComputerNetwork', 'Get-ComputerOemInformation', 'Get-ComputerOperatingSystem', 'Get-ComputerRAM', 'Get-ComputerRDP', 'Get-ComputerRoles', 'Get-ComputerService', 'Get-ComputerSMB', 'Get-ComputerSMBShare', 'Get-ComputerSMBSharePermissions', 'Get-ComputerStartup', 'Get-ComputerSystem', 'Get-ComputerTask', 'Get-ComputerTime', 'Get-ComputerTimeNtp', 'Get-ComputerWindowsFeatures', 'Get-ComputerWindowsUpdates', 'Get-DataInformation', 'Get-FileInformation', 'Get-FileMetaData', 'Get-FileName', 'Get-FileOwner', 'Get-FilePermission', 'Get-FilesInFolder', 'Get-FileSize', 'Get-GitHubLatestRelease', 'Get-GitHubVersion', 'Get-HashMaxValue', 'Get-HTML', 'Get-IPAddressInformation', 'Get-Logger', 'Get-MimeType', 'Get-ModulesAvailability', 'Get-MyIpAddress', 'Get-ObjectCount', 'Get-ObjectData', 'Get-ObjectEnumValues', 'Get-ObjectKeys', 'Get-ObjectProperties', 'Get-ObjectPropertiesAdvanced', 'Get-ObjectTitles', 'Get-ObjectType', 'Get-OperatingSystem', 'Get-PathSeparator', 'Get-PathTemporary', 'Get-ProtocolDefaults', 'Get-PSRegistry', 'Get-PSService', 'Get-RandomCharacters', 'Get-RandomFileName', 'Get-RandomPassword', 'Get-RandomStringName', 'Get-SqlQueryColumnInformation', 'Get-TemporaryDirectory', 'Get-TimeSettings', 'Get-TimeZoneAdvanced', 'Get-TimeZoneLegacy', 'Get-Types', 'Get-WinADForestControllers', 'Get-WinADForestDetails', 'Get-WinADForestOptions', 'Get-WinADOrganizationalUnitData', 'Get-WinADOrganizationalUnitFromDN', 'Get-WinADUsersByDN', 'Get-WinADUsersByOU', 'Get-WinADUserSnapshot', 'Initialize-ModulePortable', 'Invoke-CommandCustom', 'Join-Uri', 'Join-UriQuery', 'Measure-Collection', 'Merge-Array', 'Merge-Objects', 'Mount-PSRegistryPath', 'New-ArrayList', 'New-GenericList', 'New-PSRegistry', 'New-Runspace', 'New-SqlQuery', 'New-SqlQueryAlterTable', 'New-SqlQueryCreateTable', 'New-SqlTableMapping', 'Remove-DuplicateObjects', 'Remove-EmptyValue', 'Remove-FilePermission', 'Remove-FromArray', 'Remove-ObjectsExistingInTarget', 'Remove-PSRegistry', 'Remove-WhiteSpace', 'Remove-WinADUserGroups', 'Rename-LatinCharacters', 'Rename-UserValuesFromHash', 'Save-XML', 'Search-Command', 'Select-Properties', '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-FileInheritance', 'Set-FileOwner', 'Set-FilePermission', 'Set-PasswordRemotely', 'Set-PSRegistry', 'Set-ServiceRecovery', 'Set-TimeSynchronization', 'Set-WinADGroupSynchronization', 'Set-WinADUserFields', 'Set-WinADUserSettingGAL', 'Set-WinADUserStatus', 'Set-XML', 'Show-Array', 'Show-DataInVerbose', 'Show-TableVisualization', 'Split-Array', 'Start-InternalFunction', 'Start-MyProgram', 'Start-Runspace', 'Start-TimeLog', 'Stop-Runspace', 'Stop-TimeLog', 'Test-AvailabilityCommands', 'Test-ComputerAvailability', 'Test-ComputerPort', 'Test-ConfigurationCredentials', 'Test-ForestConnectivity', 'Test-IsDistinguishedName', 'Test-Key', 'Test-ModuleAvailability', 'Test-PSRegistry', 'Test-WinRM') -Alias @('Add-ADUserGroups', 'Convert-ExchangeRecipientDetails', 'Convert-FromColor', 'Copy-Hashtable', 'Copy-OrderedHashtable', 'Dismount-RegistryPath', 'Find-ADSyncServer', 'Format-Debug', 'Format-ListStream', 'Format-TableStream', 'Format-Verbose', 'Format-Warning', 'FS', 'FV', 'Get-ADUserSnapshot', 'Get-ComputerApplications', 'Get-ComputerNetworkCard', 'Get-ComputerServices', 'Get-ComputerTasks', 'Get-FilePermissions', 'Get-MyIP', 'Get-PSPermissions', 'Get-RDPSecurity', 'Get-ServerRoles', 'Get-TimeSynchronization', 'Get-WinADDomainControllers', 'Get-WinADDomainGUIDs', 'Get-WinADForestGUIDs', 'Join-Url', 'Join-UrlQuery', 'Mount-RegistryPath', 'Remove-ADUserGroups', 'Remove-EmptyValues', 'Remove-StringLatinCharacters', 'Set-ADUserName', 'Set-ADUserSettingGAL', 'Set-ADUserStatus', 'Set-EmailBodyTableReplacement', 'Set-Recovery', 'Test-IsDN') # SIG # Begin signature block # MIInPgYJKoZIhvcNAQcCoIInLzCCJysCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBS8pm8ffSpNGWp # ENTdd29q2mYZZilHfzAbaxF/1RD4zaCCITcwggO3MIICn6ADAgECAhAM5+DlF9hG # /o/lYPwb8DA5MA0GCSqGSIb3DQEBBQUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBa # Fw0zMTExMTAwMDAwMDBaMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lD # ZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC # AQoCggEBAK0OFc7kQ4BcsYfzt2D5cRKlrtwmlIiq9M71IDkoWGAM+IDaqRWVMmE8 # tbEohIqK3J8KDIMXeo+QrIrneVNcMYQq9g+YMjZ2zN7dPKii72r7IfJSYd+fINcf # 4rHZ/hhk0hJbX/lYGDW8R82hNvlrf9SwOD7BG8OMM9nYLxj+KA+zp4PWw25EwGE1 # lhb+WZyLdm3X8aJLDSv/C3LanmDQjpA1xnhVhyChz+VtCshJfDGYM2wi6YfQMlqi # uhOCEe05F52ZOnKh5vqk2dUXMXWuhX0irj8BRob2KHnIsdrkVxfEfhwOsLSSplaz # vbKX7aqn8LfFqD+VFtD/oZbrCF8Yd08CAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGG # MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEXroq/0ksuCMS1Ri6enIZ3zbcgP # MB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBBQUA # A4IBAQCiDrzf4u3w43JzemSUv/dyZtgy5EJ1Yq6H6/LV2d5Ws5/MzhQouQ2XYFwS # TFjk0z2DSUVYlzVpGqhH6lbGeasS2GeBhN9/CTyU5rgmLCC9PbMoifdf/yLil4Qf # 6WXvh+DfwWdJs13rsgkq6ybteL59PyvztyY1bV+JAbZJW58BBZurPSXBzLZ/wvFv # hsb6ZGjrgS2U60K3+owe3WLxvlBnt2y98/Efaww2BxZ/N3ypW2168RJGYIPXJwS+ # S86XvsNnKmgR34DnDDNmvxMNFG7zfx9jEB76jRslbWyPpbdhAbHSoyahEHGdreLD # +cOZUbcrBwjOLuZQsqf6CkUvovDyMIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1 # b5VQCDANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGln # aUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtE # aWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgx # MDIyMTIwMDAwWjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j # MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBT # SEEyIEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEF # AAOCAQ8AMIIBCgKCAQEA+NOzHH8OEa9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLX # cep2nQUut4/6kkPApfmJ1DcZ17aq8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSR # I5aQd4L5oYQjZhJUM1B0sSgmuyRpwsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXi # TWAYvqrEsq5wMWYzcT6scKKrzn/pfMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5 # Ng2Q7+S1TqSp6moKq4TzrGdOtcT3jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8 # vYWxYoNzQYIH5DiLanMg0A9kczyen6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYD # VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYB # BQUHAwMweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5k # aWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0 # LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4 # oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv # b3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy # dEFzc3VyZWRJRFJvb3RDQS5jcmwwTwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCow # KAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZI # AYb9bAMwHQYDVR0OBBYEFFrEuXsqCqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaA # FEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPz # ItEVyCx8JSl2qB1dHC06GsTvMGHXfgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRu # pY5a4l4kgU4QpO4/cY5jDhNLrddfRHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKN # JK4kxscnKqEpKBo6cSgCPC6Ro8AlEeKcFEehemhor5unXCBc2XGxDI+7qPjFEmif # z0DLQESlE/DmZAwlCEIysjaKJAL+L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN # 3fYBIM6ZMWM9CBoYs4GbT8aTEAb8B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKy # ZqHnGKSaZFHvMIIFPTCCBCWgAwIBAgIQBNXcH0jqydhSALrNmpsqpzANBgkqhkiG # 9w0BAQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkw # FwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEy # IEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTIwMDYyNjAwMDAwMFoXDTIz # MDcwNzEyMDAwMFowejELMAkGA1UEBhMCUEwxEjAQBgNVBAgMCcWabMSFc2tpZTER # MA8GA1UEBxMIS2F0b3dpY2UxITAfBgNVBAoMGFByemVteXPFgmF3IEvFgnlzIEVW # T1RFQzEhMB8GA1UEAwwYUHJ6ZW15c8WCYXcgS8WCeXMgRVZPVEVDMIIBIjANBgkq # hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv7KB3iyBrhkLUbbFe9qxhKKPBYqDBqln # r3AtpZplkiVjpi9dMZCchSeT5ODsShPuZCIxJp5I86uf8ibo3vi2S9F9AlfFjVye # 3dTz/9TmCuGH8JQt13ozf9niHecwKrstDVhVprgxi5v0XxY51c7zgMA2g1Ub+3ti # i0vi/OpmKXdL2keNqJ2neQ5cYly/GsI8CREUEq9SZijbdA8VrRF3SoDdsWGf3tZZ # zO6nWn3TLYKQ5/bw5U445u/V80QSoykszHRivTj+H4s8ABiforhi0i76beA6Ea41 # zcH4zJuAp48B4UhjgRDNuq8IzLWK4dlvqrqCBHKqsnrF6BmBrv+BXQIDAQABo4IB # xTCCAcEwHwYDVR0jBBgwFoAUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0OBBYE # FBixNSfoHFAgJk4JkDQLFLRNlJRmMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAK # BggrBgEFBQcDAzB3BgNVHR8EcDBuMDWgM6Axhi9odHRwOi8vY3JsMy5kaWdpY2Vy # dC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDA1oDOgMYYvaHR0cDovL2NybDQu # ZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwTAYDVR0gBEUwQzA3 # BglghkgBhv1sAwEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQu # Y29tL0NQUzAIBgZngQwBBAEwgYQGCCsGAQUFBwEBBHgwdjAkBggrBgEFBQcwAYYY # aHR0cDovL29jc3AuZGlnaWNlcnQuY29tME4GCCsGAQUFBzAChkJodHRwOi8vY2Fj # ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyQXNzdXJlZElEQ29kZVNpZ25p # bmdDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAmr1sz4ls # LARi4wG1eg0B8fVJFowtect7SnJUrp6XRnUG0/GI1wXiLIeow1UPiI6uDMsRXPHU # F/+xjJw8SfIbwava2eXu7UoZKNh6dfgshcJmo0QNAJ5PIyy02/3fXjbUREHINrTC # vPVbPmV6kx4Kpd7KJrCo7ED18H/XTqWJHXa8va3MYLrbJetXpaEPpb6zk+l8Rj9y # G4jBVRhenUBUUj3CLaWDSBpOA/+sx8/XB9W9opYfYGb+1TmbCkhUg7TB3gD6o6ES # Jre+fcnZnPVAPESmstwsT17caZ0bn7zETKlNHbc1q+Em9kyBjaQRcEQoQQNpezQu # g9ufqExx6lHYDjCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZI # hvcNAQEMBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ # MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNz # dXJlZCBJRCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVow # YjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ # d3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290 # IEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjww # IjBpM+zCpyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J5 # 8soR0uRf1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMH # hOZ0O21x4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6 # Zu53yEioZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQ # ecN4x7axxLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4b # A3VdeGbZOjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9 # WV1CdoeJl2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCU # tNJhbesz2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvo # ZKYz0YkH4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/J # vNNBERJb5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCP # orF+CiaZ9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMB # Af8wHQYDVR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXr # oq/0ksuCMS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRt # MGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEF # BQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl # ZElEUm9vdENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdp # Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgw # BgYEVR0gADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cH # vZqsoYcs7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8 # UgPITtAq3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTn # f+hZqPC/Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxU # jG/voVA9/HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8j # LfR+cWojayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDCCBq4w # ggSWoAMCAQICEAc2N7ckVHzYR6z9KGYqXlswDQYJKoZIhvcNAQELBQAwYjELMAkG # A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp # Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4X # DTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIzNTk1OVowYzELMAkGA1UEBhMCVVMxFzAV # BgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVk # IEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTCCAiIwDQYJKoZIhvcN # AQEBBQADggIPADCCAgoCggIBAMaGNQZJs8E9cklRVcclA8TykTepl1Gh1tKD0Z5M # om2gsMyD+Vr2EaFEFUJfpIjzaPp985yJC3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE # 2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+QtxnjupRPfDWVtTnKC3r07G1decfBmWN # lCnT2exp39mQh0YAe9tEQYncfGpXevA3eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFo # bjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbFHc02DVzV5huowWR0QKfAcsW6Th+xtVhN # ef7Xj3OTrCw54qVI1vCwMROpVymWJy71h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3Vu # JyWQmDo4EbP29p7mO1vsgd4iFNmCKseSv6De4z6ic/rnH1pslPJSlRErWHRAKKtz # Q87fSqEcazjFKfPKqpZzQmiftkaznTqj1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4O # uGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2LINIsVzV5K6jzRWC8I41Y99xh3pP+OcD5 # sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJjAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm # 4T72wnSyPx4JduyrXUZ14mCjWAkBKAAOhFTuzuldyF4wEr1GnrXTdrnSDmuZDNIz # tM2xAgMBAAGjggFdMIIBWTASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6 # FtltTYUvcyl2mi91jGogj57IbzAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qY # rhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYB # BQUHAQEEazBpMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20w # QQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy # dFRydXN0ZWRSb290RzQuY3J0MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwz # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZ # MBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAfVmO # wJO2b5ipRCIBfmbW2CFC4bAYLhBNE88wU86/GPvHUF3iSyn7cIoNqilp/GnBzx0H # 6T5gyNgL5Vxb122H+oQgJTQxZ822EpZvxFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/ # R3f7cnQU1/+rT4osequFzUNf7WC2qk+RZp4snuCKrOX9jLxkJodskr2dfNBwCnzv # qLx1T7pa96kQsl3p/yhUifDVinF2ZdrM8HKjI/rAJ4JErpknG6skHibBt94q6/ae # sXmZgaNWhqsKRcnfxI2g55j7+6adcq/Ex8HBanHZxhOACcS2n82HhyS7T6NJuXdm # kfFynOlLAlKnN36TU6w7HQhJD5TNOXrd/yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3 # EyTN3B14OuSereU0cZLXJmvkOHOrpgFPvT87eK1MrfvElXvtCl8zOYdBeHo46Zzh # 3SP9HSjTx/no8Zhf+yvYfvJGnXUsHicsJttvFXseGYs2uJPU5vIXmVnKcPA3v5gA # 3yAWTyf7YGcWoWa63VXAOimGsJigK+2VQbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8 # BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsf # gPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr9u3WfPwwggbAMIIEqKADAgECAhAMTWly # S5T6PCpKPSkHgD1aMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYD # VQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBH # NCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjIwOTIxMDAwMDAw # WhcNMzMxMTIxMjM1OTU5WjBGMQswCQYDVQQGEwJVUzERMA8GA1UEChMIRGlnaUNl # cnQxJDAiBgNVBAMTG0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDIyIC0gMjCCAiIwDQYJ # KoZIhvcNAQEBBQADggIPADCCAgoCggIBAM/spSY6xqnya7uNwQ2a26HoFIV0Mxom # rNAcVR4eNm28klUMYfSdCXc9FZYIL2tkpP0GgxbXkZI4HDEClvtysZc6Va8z7GGK # 6aYo25BjXL2JU+A6LYyHQq4mpOS7eHi5ehbhVsbAumRTuyoW51BIu4hpDIjG8b7g # L307scpTjUCDHufLckkoHkyAHoVW54Xt8mG8qjoHffarbuVm3eJc9S/tjdRNlYRo # 44DLannR0hCRRinrPibytIzNTLlmyLuqUDgN5YyUXRlav/V7QG5vFqianJVHhoV5 # PgxeZowaCiS+nKrSnLb3T254xCg/oxwPUAY3ugjZNaa1Htp4WB056PhMkRCWfk3h # 3cKtpX74LRsf7CtGGKMZ9jn39cFPcS6JAxGiS7uYv/pP5Hs27wZE5FX/NurlfDHn # 88JSxOYWe1p+pSVz28BqmSEtY+VZ9U0vkB8nt9KrFOU4ZodRCGv7U0M50GT6Vs/g # 9ArmFG1keLuY/ZTDcyHzL8IuINeBrNPxB9ThvdldS24xlCmL5kGkZZTAWOXlLimQ # prdhZPrZIGwYUWC6poEPCSVT8b876asHDmoHOWIZydaFfxPZjXnPYsXs4Xu5zGcT # B5rBeO3GiMiwbjJ5xwtZg43G7vUsfHuOy2SJ8bHEuOdTXl9V0n0ZKVkDTvpd6kVz # HIR+187i1Dp3AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/ # BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEE # AjALBglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8w # HQYDVR0OBBYEFGKK3tBh/I8xFO2XC809KpQU31KcMFoGA1UdHwRTMFEwT6BNoEuG # SWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQw # OTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQG # CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKG # TGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJT # QTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIB # AFWqKhrzRvN4Vzcw/HXjT9aFI/H8+ZU5myXm93KKmMN31GT8Ffs2wklRLHiIY1UJ # RjkA/GnUypsp+6M/wMkAmxMdsJiJ3HjyzXyFzVOdr2LiYWajFCpFh0qYQitQ/Bu1 # nggwCfrkLdcJiXn5CeaIzn0buGqim8FTYAnoo7id160fHLjsmEHw9g6A++T/350Q # p+sAul9Kjxo6UrTqvwlJFTU2WZoPVNKyG39+XgmtdlSKdG3K0gVnK3br/5iyJpU4 # GYhEFOUKWaJr5yI+RCHSPxzAm+18SLLYkgyRTzxmlK9dAlPrnuKe5NMfhgFknADC # 6Vp0dQ094XmIvxwBl8kZI4DXNlpflhaxYwzGRkA7zl011Fk+Q5oYrsPJy8P7mxNf # arXH4PMFw1nfJ2Ir3kHJU7n/NBBn9iYymHv+XEKUgZSCnawKi8ZLFUrTmJBFYDOA # 4CPe+AOk9kVH5c64A0JH6EE2cXet/aLol3ROLtoeHYxayB6a1cLwxiKoT5u92Bya # UcQvmvZfpyeXupYuhVfAYOd4Vn9q78KVmksRAsiCnMkaBXy6cbVOepls9Oie1FqY # yJ+/jbsYXEP10Cro4mLueATbvdH7WwqocH7wl4R44wgDXUcsY6glOJcB0j862uXl # 9uab3H4szP8XTE0AotjWAQ64i+7m4HJViSwnGWH2dwGMMYIFXTCCBVkCAQEwgYYw # cjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ # d3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVk # IElEIENvZGUgU2lnbmluZyBDQQIQBNXcH0jqydhSALrNmpsqpzANBglghkgBZQME # AgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEM # BgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqG # SIb3DQEJBDEiBCABmvRd5pfjdlyVPgZ7W7g1TbHwhCNYaSdMzA2I4Xr/MzANBgkq # hkiG9w0BAQEFAASCAQCQmEa53ft84/Vh9yqDjnL9400fkJqwikZCm7m5W0AQd3s7 # CsAlDkDEGF3xphLFQinI2Hc2rgZfcgf05jOJTMKq0Mcb4zT39pHbiyLqPPj8wJFF # RMTTohE9tyduR3//UET70o6XFg58FE6JupLx/rd/P3XeviqtPmLr2Ba7Uox/61Tb # LqenpGy5kwR1ntEpTN1BRHuiZtr2B6qep8fON2PhKJBWNP9F6fti24cLlhZjG+Oe # /9TMS+Ek2MxlwTeGMl8TJcprTYSFiTOAGiUobV7TuM5/hVDfam8kQEvapHK9qbtb # hi4lam73AO/WX8unI4SztosLOIjPsfs3Yu8jhmwsoYIDIDCCAxwGCSqGSIb3DQEJ # BjGCAw0wggMJAgEBMHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0 # LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hB # MjU2IFRpbWVTdGFtcGluZyBDQQIQDE1pckuU+jwqSj0pB4A9WjANBglghkgBZQME # AgEFAKBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8X # DTIzMDUyMzA3NDg0NVowLwYJKoZIhvcNAQkEMSIEIF6Rcx52lwGlyHNhngHRR4N+ # TzmkRa4OcszhvZz8GxFeMA0GCSqGSIb3DQEBAQUABIICAKVUpuGkkN59mtcS2KXk # PzA/3KrHRWbIYtiOzdzvXO+xJ2FH91gl0ffjHSmh0j5iPLeYGILzVtTLutmhXSX8 # 4VOVVUXJNUIJrlTdSBFnGR5MUdvvXNFhIpsI/r/jdXopWapH1nhCrhQbygSa/i2e # AWFX35gSfZ5MD9DHIUd4fpQ79MsAoqXpRunTmywWl8nxWUClA9S7BMKQoO1MyAdK # 5ElzYqvcrAONTIkx1UQvmEAuvyeLoVVSkO3vGzrpt5OC8Yx4h09Q1M7q2huVVcgY # rs7Kz3jVOl2k48xfsSb22krwXwUwNRVkuIoBjL7+CiCgtJXt/rWB/hSeRZnwHBH8 # pTSuNTnTgNEoCFllBJRDsLBQ/i8yN75GBTm3oC4uX5ynJH//WwKkDzGvonVuwH0f # C11S9VrXvJQsF0Dm0xaOUgddMdQfNBMBKgo3P6jlL5cpd1D5a5hwizADIbOWeHlE # yu8bggHt3Ix4EbqMmOTloodRq5yZOBh4kpec+OUiKpTLT9DP9A9U4bvqsQsqxz8+ # uUbVFcE3xBrvgl7eJrk5FIpkA1h7KmgtkVQT42H4Gx4utwMqOQOew9mGcwDOaxID # yU4Vw4CULRWBP0MBm0CvWIA/mj0sRs5rkv5xVkBqfWuYCCeiKEboTPOrzvSXrhNn # x61PLfczVxip/DuSIRbdOQ6u # SIG # End signature block |