PSModules/Carbon.Core/1.2.0/Carbon.Core.psm1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 |
# Copyright Aaron Jensen and WebMD Health Services # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License #Requires -Version 5.1 Set-StrictMode -Version 'Latest' # Functions should use $moduleRoot as the relative root from which to find # things. A published module has its function appended to this file, while a # module in development has its functions in the Functions directory. $moduleRoot = $PSScriptRoot # Store each of your module's functions in its own file in the Functions # directory. On the build server, your module's functions will be appended to # this file, so only dot-source files that exist on the file system. This allows # developers to work on a module without having to build it first. Grab all the # functions that are in their own files. $functionsPath = Join-Path -Path $moduleRoot -ChildPath 'Functions\*.ps1' if( (Test-Path -Path $functionsPath) ) { foreach( $functionPath in (Get-Item $functionsPath) ) { . $functionPath.FullName } } function Add-CTypeData { <# .SYNOPSIS Adds type data to a type only if the type data doesn't already exist. .DESCRIPTION The `Add-CTypeData` function uses PowerShell's `Update-TypeData` cmdlet to add type data to a type, but only if the given type data doesn't already exist. Pass the type to the `Type` parameter or the type name to the `TypeName` parameter, the new type data member type to the `MemberType` parameter (e.g. `AliasProperty`, `NoteProperty`, `ScriptProperty`, or `ScriptMethod`), the member name to the `MemberName` parameter, and the member's value/implementation to the `Value` parameter. Note that the `Type` parameter should be the bare name of the type, e.g. `Diagnostics.Process`, *without* square brackets. If the type already has an equivalent member with the name given by the `MemberName` parameter, nothing happens, and the function returns. .EXAMPLE Add-CTypeData -Type Diagnostics.Process -MemberType ScriptProperty -MemberName 'ParentID' -Value $scriptBlock Demonstrates how to create a script property on a type. In this example, the `System.Diagnostics.Process` type will be given a `ParentID` property that runs the code in the script block in the `$scriptBlock` variable. .EXAMPLE Add-CTypeData -Type Diagnostics.Process -MemberType ScriptMethod -MemberName 'GetParentID()' -Value $scriptBlock Demonstrates how to create a script method on a type. In this example, the `System.Diagnostics.Process` type will be given a `GetParentID()` method that runs the code in the script block in the `$scriptBlock` variable. .EXAMPLE Add-CTypeData -Type Diagnostics.Process -MemberType AliasProperty -MemberName 'ProcessId' -Value 'Id' Demonstrates how to create an alias script property on a type. In this example, the `System.Diagnostics.Process` type will be given a `ProcessId` property that is an alias to the 'Id' property. .EXAMPLE Add-CTypeData -Type Diagnostics.Process -MemberType NoteProperty -MemberName 'ParentID' -Value $parentPid Demonstrates how to create a ntoe property on a type. In this example, the `System.Diagnostics.Process` type will be given a `ParentID` property that returns the value in the `$parentPid` variable. #> [CmdletBinding()] param( # The type on which to add the type data. This should be the bare type name, e.g. Diagnostics.Process, *not* # the type surrounded by square brackets, e.g. `[Diagnostics.Process]`. [Parameter(Mandatory, ParameterSetName='ByType')] [Type] $Type, # The name of the type on which to add the type data. [Parameter(Mandatory, ParameterSetName='ByTypeName')] [String] $TypeName, # The member type of the new type data. Only `AliasProperty`, `NoteProperty`, `ScriptProperty`, `ScriptMethod` # are supported. [Parameter(Mandatory)] [ValidateSet('AliasProperty', 'NoteProperty', 'ScriptProperty', 'ScriptMethod')] [Management.Automation.PSMemberTypes] $MemberType, # The type data's member name. [Parameter(Mandatory)] [String] $MemberName, # The value for the member. If `MemberName` is: # # * `AliasProperty`, this should be the name of the target property. # * `NoteProperty`, the literal value of the property. # * `ScriptProperty`, a script block that return's the property value. # * `ScriptMethod`, a script block that implements the method logic. [Parameter(Mandatory)] [Object] $Value ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState $memberTypeMsg = '{0,-14}' -f $MemberType if( -not $TypeName ) { $TypeName = $Type.FullName } if( $Type ) { if( $MemberType -like '*Property' ) { if( ($Type.GetProperties() | Where-Object Name -EQ $MemberName) ) { Write-Debug ("Type $($memberTypeMsg) [$($TypeName)] $($MemberName)") return } } elseif( $MemberType -like '*Method') { if( ($Type.GetMethods() | Where-Object Name -EQ $MemberName) ) { Write-Debug ("Type $($memberTypeMsg) [$($TypeName)] $($MemberName)") return } } } $typeData = Get-TypeData -TypeName $TypeName if( $typeData -and $typeData.Members.ContainsKey($MemberName) ) { Write-Debug ("TypeData $($memberTypeMsg) [$($TypeName)] $($MemberName)") return } Write-Debug ("TypeData + $($memberTypeMsg) [$($TypeName)] $($MemberName)") Update-TypeData -TypeName $TypeName -MemberType $MemberType -MemberName $MemberName -Value $Value } function ConvertTo-CBase64 { <# .SYNOPSIS Base64 encodes things. .DESCRIPTION The `ConvertTo-CBase64` function base64 encodes things. Pipe what you want to encode to `ConvertTo-CBase64`. The function can encode: * [String] * [byte] * [char] * Signed integers: [int16], [int], [int64] (i.e. [long]) * Unsigned integers: [uint16], [uint32], [uint64] * Floating point numbers: [float], [double] * [bool] For each item piped to `ConvertTo-CBase64`, the function returns that item base64 encoded. If you pipe all bytes or all chars to `ConvertTo-CBase64`, it will encode all the bytes and chars together. This allows you to do this: [IO.File]::ReadAllBytes('some file') | ConvertTo-CBase64 and get back a single string for all the bytes/chars. By default, `ConvertTo-CBase64` uses Unicode/UTF-16 encoding when converting strings to base64 (this is the default encoding of strings by .NET and PowerShell). To use a different encoding, pass it to the `Encoding` parameter (`[Text.Encoding] | Get-Member -Static` will show all the default encodings). .EXAMPLE 'Encode me, please!' | ConvertTo-CBase64 Demonstrates how to encode a string in base64. .EXAMPLE 'Encode me, please!' | ConvertTo-CBase64 -Encoding ([Text.Encoding]::ASCII) Demonstrates how to use a custom encoding when converting a string to base64. The parenthesis around the encoding is required by the PowerShell language. .EXAMPLE [IO.File]::ReadAllBytes('path to some file') | ConvertTo-CBase64 Demonstrates that you can pipe an array of bytes to `ConvertTo-CBase64` and you'll get back a single string of all the bytes base64 encoded. .EXAMPLE [IO.File]::ReadAllText('path to some file').ToCharArray() | ConvertTo-CBase64 Demonstrates that you can pipe an array of chars to `ConvertTo-CBase64` and you'll get back a single string of all the chars base64 encoded. .EXAMPLE @( $true, [int16]1, [int]2, [long]3, [uint16]4, [uint32]5, [uint64]6, [float]7.8, [double]9.0) | ConvertTo-CBase64 Demonstrates that `ConvertTo-CBase64` can convert booleans, all sizes of signed and unsigned ints, floats, and doubles to base64. #> [CmdletBinding()] [OutputType([String])] param( [Parameter(Mandatory,ValueFromPipeline,Position=0)] [AllowNull()] [AllowEmptyString()] # The value to base64 encode. [Object]$InputObject, # The encoding to use. Default is Unicode/UTF-16 (the default .NET encoding for strings). This parameter is only # used if encoding a string or char array. [Text.Encoding]$Encoding = ([Text.Encoding]::Unicode) ) begin { Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState $collector = $null $collectingBytes = $false $collectingChars = $false $collecting = $false $stopProcessing = $false $inspectedFirstItem = $false } process { if( $stopProcessing ) { return } if( $null -eq $InputObject ) { return } if( $InputObject -is [Collections.IEnumerable] -and $InputObject -isnot [String] ) { Write-Debug "$($InputObject.GetType().FullName)" $InputObject | ConvertTo-CBase64 -Encoding $Encoding return } $isByte = $InputObject -is [byte] $isChar = $InputObject -is [char] if( $PSCmdlet.MyInvocation.ExpectingInput -and -not $inspectedFirstItem ) { $inspectedFirstItem = $true if( $isByte ) { $collecting = $true $collectingBytes = $true $collector = [Collections.Generic.List[byte]]::New() Write-Debug -Message ("Collecting bytes.") } elseif( $isChar ) { $collecting = $true $collectingChars = $true $collector = [Collections.Generic.List[char]]::New() Write-Debug -Message ("Collecting chars.") } } if( $collecting ) { # Looks like we didn't get passed an array of bytes or chars, but an array of mixed object types. if( (-not $isByte -and $collectingBytes) -or (-not $isChar -and $collectingChars) ) { $collecting = $false # Since we are no longer collecting, we need to encode all the previous items we collected. foreach( $item in $collector ) { ConvertTo-CBase64 -InputObject $item -Encoding $Encoding } ConvertTo-CBase64 -InputObject $InputObject -Encoding $Encoding return } [void]$collector.Add($InputObject) return } if( $InputObject -is [String] ) { return [Convert]::ToBase64String($Encoding.GetBytes($InputObject)) } if( $isByte ) { return [Convert]::ToBase64String([byte[]]$InputObject) } if( $InputObject -is [bool] -or $isChar -or $InputObject -is [int16] -or $InputObject -is [int] -or ` $InputObject -is [long] -or $InputObject -is [uint16] -or $InputObject -is [uint32] -or ` $InputObject -is [uint64] -or $InputObject -is [float] -or $InputObject -is [double] ) { return [Convert]::ToBase64String([BitConverter]::GetBytes($InputObject)) } $stopProcessing = $true $msg = "Failed to base64 encode ""$($InputObject.GetType().FullName)"" object. The " + 'ConvertTo-CBase64 function can only convert strings, chars, bytes, bools, all signed and unsigned ' + 'integers, floats, and doubles.' Write-Error -Message $msg -ErrorAction $ErrorActionPreference } end { if( $stopProcessing ) { return } if( -not $collecting ) { return } if( $collectingChars ) { $bytes = $Encoding.GetBytes($collector.ToArray()) } elseif( $collectingBytes ) { $bytes = $collector.ToArray() } [Convert]::ToBase64String($bytes) } } function Get-CPowershellPath { <# .SYNOPSIS Gets the path to powershell.exe. .DESCRIPTION Returns the path to the powershell.exe binary for the machine's default architecture (i.e. x86 or x64). If you're on a x64 machine and want to get the path to x86 PowerShell, set the `x86` switch. Here are the possible combinations of operating system, PowerShell, and desired path architectures, and the path they map to. +-----+-----+------+--------------------------------------------------------------+ | OS | PS | Path | Result | +-----+-----+------+--------------------------------------------------------------+ | x64 | x64 | x64 | $env:windir\System32\Windows PowerShell\v1.0\powershell.exe | | x64 | x64 | x86 | $env:windir\SysWOW64\Windows PowerShell\v1.0\powershell.exe | | x64 | x86 | x64 | $env:windir\sysnative\Windows PowerShell\v1.0\powershell.exe | | x64 | x86 | x86 | $env:windir\SysWOW64\Windows PowerShell\v1.0\powershell.exe | | x86 | x86 | x64 | $env:windir\System32\Windows PowerShell\v1.0\powershell.exe | | x86 | x86 | x86 | $env:windir\System32\Windows PowerShell\v1.0\powershell.exe | +-----+-----+------+--------------------------------------------------------------+ .EXAMPLE Get-CPowerShellPath Returns the path to the version of PowerShell that matches the computer's architecture (i.e. x86 or x64). .EXAMPLE Get-CPowerShellPath -x86 Returns the path to the x86 version of PowerShell. Only valid on Windows. #> [CmdletBinding()] param( # The architecture of the PowerShell executable to run. The default is the architecture of the current # process. [switch]$x86 ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState Write-Debug "[Carbon\Get-CPowerShellPath]" # Map the system directory name from the current PowerShell architecture to the requested architecture. $sysDirNames = @{ # If PowerShell is 64-bit 'x64' = @{ # These are paths to PowerShell matching requested architecture. 'x64' = 'System32'; 'x86' = 'SysWOW64'; }; # If PowerShell is 32-bit. 'x86' = @{ # These are the paths to get to the appropriate architecture. 'x64' = 'sysnative'; 'x86' = 'System32'; } } $executableName = 'powershell.exe' $edition = 'Desktop' if( (Test-CPowerShell -IsCore) ) { $edition = 'Core' $executableName = 'pwsh' if( (Test-COperatingSystem -IsWindows) ) { $executableName = "$($executableName).exe" } } Write-Debug -Message " Edition $($edition)" # PowerShell is always in the same place on x86 Windows. $osArchitecture = 'x64' if( (Test-COperatingSystem -Is32Bit) ) { $osArchitecture = 'x32' return Join-Path -Path $PSHOME -ChildPath $executableName } Write-Debug -Message " Operating System Architecture $($osArchitecture)" $architecture = 'x64' if( $x86 ) { $architecture = 'x86' } $psArchitecture = 'x64' if( (Test-CPowerShell -Is32Bit) ) { $psArchitecture = 'x86' } Write-Debug -Message " PowerShell Architecture $($psArchitecture)" Write-Debug -Message " Requested Architecture $($architecture)" $sysDirName = $sysDirNames[$psArchitecture][$architecture] Write-Debug -Message " Architecture SysDirName $($sysDirName)" $path = $PSHOME -replace '\b(System32|SysWOW64)\b', $sysDirName return Join-Path -Path $path -ChildPath $executableName } function Invoke-CPowerShell { <# .SYNOPSIS Invokes a new `powershell.exe` process. .DESCRIPTION The `Invoke-CPowerShell` scripts executes a new PowerShell process. Pass the parameters to pass to the executable to the ArgumentList parameter. The function uses the `&` operator to run PowerShell. By default, the PowerShell executable in `$PSHOME` is used. In Windows PowerShell (i.e. powershell.exe), the PowerShell executable in "$PSHOME" that matches the architecture of the operating system. Use the `x86` switch to use 32-bit `powershell.exe`. Because this function uses the `&` operator to execute PowerShell, all the PowerShell streams from the invoked command are returned (e.g. stdout, verbose, warning, error, stderr, etc.). To use a different PowerShell executable, like PowerShell Core (i.e. pwsh), pass the path to the PowerShell executable to the `Path` parameter. If the PowerShell executable is in your PATH, you can pass just the executable name. If you want to run an encoded command, pass it to the `Command` parameter. The value of the Command parameter will be base64 encoded and added to the end of the arguments in the ArgumentList parameter, along with the "-EncodedCommand" switch. You can run the PowerShell process as a different user by passing that user's credentials to the `Credential` parameter. `Invoke-CPowerShell` uses the Start-Job cmdlet to start a background job with those credentials. `Start-Job` runs PowerShell with the `&` operator. There is a known issue on Linux and macOS that prevents the `Start-Job` cmdlet (what `Invoke-CPowerShell` uses to run PowerShell as another user) from starting PowerShell as another user. See https://github.com/PowerShell/PowerShell/issues/7172 for more information. .EXAMPLE Invoke-CPowerShell -ArgumentList '-NoProfile','-NonInteractive','-Command','$PID' Demonstrates how to start a new PowerShell process. .EXAMPLE Invoke-CPowerShell -Command $aLargePSScript -ArgumentList '-NoProfile','-NonInteractive' Demonstrates how to run an encoded command. In this example, `Invoke-CPowerShell` encodes the command in the `Command` parameter, then runs PowerShell with `-NoProfile -NonInteractive -EncodedCommand $encodedCommand` parameters. .EXAMPLE Invoke-CPowerShell -Credential $cred -ArgumentList '-NoProfile','-NonInteractive','-Command','[Environment]::UserName' Demonstrates how to run PowerShell as a different user by passing that user's credentials to the `Credential` parameter. This credential is passed to the `Start-Job` cmdlet's `Credential` parameter, then PowerShell is executed using the `&` operator. .EXAMPLE Invoke-CPowerShell -x86 -ArgumentList '-Command','[Environment]::Is64BitProcess' Demonstrates how to run PowerShell in a 32-bit process. This switch only has an effect on 64-bit Windows operating systems. On other systems, use the `-Path` parameter to run PowerShell with a different architecture (which must be installed). .EXAMPLE Invoke-CPowerShell -Path 'pwsh' -ArgumentList '-Command','$PSVersionTable.Edition' Demonstrates how to use a custom PowerShell executable. In this case the first `pwsh` command found in your PATH environment variable is used. #> [CmdletBinding()] param( [Parameter(Mandatory)] # Any arguments to pass to PowerShell. They are passed as-is, so you'll need to handle any necessary # escaping. # # If you need to run an encoded command, use the `Command` parameter to pass the command and this parameter to # pass other parameters. The encoded command will be added to the end of the arguments. [Object[]]$ArgumentList, # The command to run, as a string. The command will be base64 encoded first and passed to PowerShell's # `EncodedCommand` parameter. [String]$Command, # Run PowerShell as a specific user. Pass that user's credentials. # # There is a known issue on Linux and macOS that prevents the `Start-Job` cmdlet (what `Invoke-CPowerShell` # uses to run PowerShell as another user) from starting PowerShell as another user. See # https://github.com/PowerShell/PowerShell/issues/7172 for more information. [pscredential]$Credential, # Run the x86 (32-bit) version of PowerShell. If not provided, the version which matches the OS architecture # is used, *regardless of the architecture of the currently running process*. I.e. this command is run under # a 32-bit PowerShell on a 64-bit operating system, without this switch, `Invoke-CPowerShell` will start a # 64-bit "PowerShell". # # This switch is only used on Windows. [switch]$x86, # The path to the PowerShell executable to use. The default is to use the executable in "$PSHOME". On Windows, # the PowerShell executable in the "$PSHOME" that matches the operating system's architecture is used. # # If the PowerShell executable is in your `PATH`, you can pass the executable name instead. [String]$Path ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState if( -not $Path ) { $params = @{ } if( $x86 ) { $params.x86 = $true } $Path = Get-CPowerShellPath @params } $ArgumentList = & { if( $ArgumentList ) { $ArgumentList | Write-Output } if( $Command ) { '-EncodedCommand' | Write-Output $Command | ConvertTo-CBase64 | Write-Output } } Write-Verbose -Message $Path $ArgumentList | ForEach-Object { Write-Verbose -Message " $($_)" } if( $Credential ) { $location = Get-Location $currentDir = [Environment]::CurrentDirectory $output = $null $WhatIfPreference = $false Start-Job -Credential $Credential -ScriptBlock { Set-Location $using:location [Environment]::CurrentDirectory = $using:currentDir & $using:Path $using:ArgumentList $LASTEXITCODE exit $LASTEXITCODE } | Receive-Job -Wait -AutoRemoveJob | Tee-Object 'output' | Select-Object -SkipLast 1 $LASTEXITCODE = $output | Select-Object -Last 1 } else { & $Path $ArgumentList } Write-Verbose -Message " LASTEXITCODE $($LASTEXITCODE)" } function Resolve-CFullPath { <# .SYNOPSIS Converts a relative path to an absolute path. .DESCRIPTION Unlike `Resolve-Path`, this function does not check whether the path exists. It just converts relative paths to absolute paths using .NET's `[IO.Path]::GetFullPath()` method. Unrooted paths (e.g. `..\..\See\I\Do\Not\Have\A\Root`) are first joined with the current directory (as returned by `Get-Location`). .EXAMPLE Resolve-CFullPath -Path 'C:\Projects\Carbon\Test\..\Carbon\FileSystem.ps1' Returns `C:\Projects\Carbon\Carbon\FileSystem.ps1`. .EXAMPLE Resolve-CFullPath -Path 'C:\Projects\Carbon\..\I\Do\Not\Exist' Returns `C:\Projects\I\Do\Not\Exist`. .EXAMPLE Resolve-CFullPath -Path ..\..\Foo\..\Bar Because the `Path` isn't rooted, joins `Path` with the current directory (as returned by `Get-Location`), and returns the full path. If the current directory is `C:\Projects\Carbon`, returns `C:\Bar`. #> [CmdletBinding()] param( # The path to resolve. [Parameter(Mandatory)] [String] $Path ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState if (-not ([System.IO.Path]::IsPathRooted($Path))) { $Path = Join-Path -Path (Get-Location) -ChildPath $Path } return [IO.Path]::GetFullPath($Path) } function Test-COperatingSystem { <# .SYNOPSIS Tests attributes of the current operating system. .DESCRIPTION The `Test-COperatingSystem` function tests atrributes of the current operating system, returning `$true` if they are `$true` and `$false` otherwise. It supports the following switches (only one can be given at at time) that return the following attributes: * `Is32Bit`: is the architecture 32-bit? Uses `[Environment]::Is64BitOperatingSystem`. * `Is64Bit`: is the architecture 64-bit? Uses `[Environment]::Is64BitOperatingSystem`. * `IsWindows`: is the operating system Windows? Uses the `$IsWindows` built-in variable, it it exists. If it doesn't, returns `$true` (only Windows operating systems don't have this variable). * `IsLinux`: is the operating system Linux? Uses the `$IsLinux` built-in variable, if it exists. If it doesn't, returns `$false` (all Linux systems have the `IsLinux` variable). * `IsMacOS`: is the operating system macOS? Uses the `$IsMacOS` built-in variable, if it exists. If it doesn't, returns `$false` (all macOS systems have the `IsMacOS` variable). .OUTPUTS System.Boolean. .LINK http://msdn.microsoft.com/en-us/library/system.environment.is64bitoperatingsystem.aspx .EXAMPLE Test-COperatingSystem -Is32Bit Demonstrates how to test if the current operating system is 32-bit/x86. .EXAMPLE Test-COperatingSystem -Is64Bit Demonstrates how to test if the current operating system is 64-bit/x64. .EXAMPLE Test-COperatingSystem -IsWindows Demonstrates how to test if the current operating system is Windows. .EXAMPLE Test-COperatingSystem -IsLinux Demonstrates how to test if the current operating system is Linux. .EXAMPLE Test-COperatingSystem -IsMacOS Demonstrates how to test if the current operating system is macOS. #> [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory,ParameterSetName='Is32Bit')] [switch]$Is32Bit, [Parameter(Mandatory,ParameterSetName='Is64Bit')] [switch]$Is64Bit, [Parameter(Mandatory,ParameterSetName='IsWindows')] [Alias('IsWindows')] [switch]$Windows, [Parameter(Mandatory,ParameterSetName='IsLinux')] [Alias('IsLinux')] [switch]$Linux, [Parameter(Mandatory,ParameterSetName='IsMacOS')] [Alias('IsMacOS')] [switch]$MacOS ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState switch( $PSCmdlet.ParameterSetName ) { 'Is32Bit' { return -not [Environment]::Is64BitOperatingSystem } 'Is64Bit' { return [Environment]::Is64BitOperatingSystem } 'IsWindows' { if( (Test-Path -Path 'variable:IsWindows') ) { return $IsWindows } return $true } 'IsLinux' { return (Test-Path -Path 'variable:IsLinux') -and $IsLinux } 'IsMacOS' { return (Test-Path -Path 'variable:IsMacOS') -and $IsMacOS } } } function Test-CPowerShell { <# .SYNOPSIS Tests attributes of the current PowerShell process. .DESCRIPTION The `Test-CPowerShell` function tests attributes of the current PowerShell process (or process hosting the current PowerShell runspace). It uses the following switches to test the following conditions: * `Is32Bit`: if the process architecture is 32-bit/x86 (uses `[Environment]::Is64BitProcess`). * `Is64Bit`: if the process architecture is 64-bit/x64 (uses `[Environment]::Is64BitProcess`). * `IsDesktop`: if the process is running on Windows PowerShell (uses `$PSVersionTable.Edition`; if this property doesn't exist, always returns `$true`). * `IsCore`: if the process is running PowerShell Core (uses `$PSVersionTable.Edition`). .OUTPUTS System.Boolean. .EXAMPLE Test-CPowerShell -Is32Bit Demonstrates how to test if the current PowerShell process architecture is 32-bit/x86. .EXAMPLE Test-CPowerShell -Is64Bit Demonstrates how to test if the current PowerShell process architecture is 64-bit/x64. .EXAMPLE Test-CPowerShell -IsDesktop Demonstrates how to test if the current PowerShell process is Windows PowerShell. .EXAMPLE Test-CPowerShell -IsCore Demonstrates how to test if the current PowerShell process is Windows Core. #> [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory,ParameterSetName='Is32Bit')] [switch]$Is32Bit, [Parameter(Mandatory,ParameterSetName='Is64Bit')] [switch]$Is64Bit, [Parameter(Mandatory,ParameterSetName='IsDesktop')] [switch]$IsDesktop, [Parameter(Mandatory,ParameterSetName='IsCore')] [switch]$IsCore ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState switch( $PSCmdlet.ParameterSetName ) { 'Is32Bit' { return -not [Environment]::Is64BitProcess } 'Is64Bit' { return [Environment]::Is64BitProcess } 'IsDesktop' { return -not $PSVersionTable['PSEdition'] -or $PSVersionTable['PSEdition'] -eq 'Desktop' } 'IsCore' { return $PSVersionTable['PSEdition'] -eq 'Core' } } } function Test-CTypeDataMember { <# .SYNOPSIS Tests if a type has an extended type member defined. .DESCRIPTION `Test-CTypeDataMember` tests if a type has an extended type member defined. If the type isn't found, you'll get an error. Returns `$true` if the type is found and the member is defined. Otherwise, returns `$false`. .EXAMPLE Test-CTypeDataMember -TypeName 'Microsoft.Web.Administration.Site' -MemberName 'PhysicalPath' Tests if the `Microsoft.Web.Administration.Site` type has a `PhysicalPath` extended type member defined. #> [CmdletBinding()] [OutputType([bool])] param( # The type name to check. [Parameter(Mandatory)] [String] $TypeName, # The name of the member to check. [Parameter(Mandatory)] [String] $MemberName ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState $typeData = Get-TypeData -TypeName $TypeName if( -not $typeData ) { # The type isn't defined or there is no extended type data on it. return $false } return $typeData.Members.ContainsKey( $MemberName ) } # Copyright 2012 Aaron Jensen # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. function Use-CallerPreference { <# .SYNOPSIS Sets the PowerShell preference variables in a module's function based on the callers preferences. .DESCRIPTION Script module functions do not automatically inherit their caller's variables, including preferences set by common parameters. This means if you call a script with switches like `-Verbose` or `-WhatIf`, those that parameter don't get passed into any function that belongs to a module. When used in a module function, `Use-CallerPreference` will grab the value of these common parameters used by the function's caller: * ErrorAction * Debug * Confirm * InformationAction * Verbose * WarningAction * WhatIf This function should be used in a module's function to grab the caller's preference variables so the caller doesn't have to explicitly pass common parameters to the module function. This function is adapted from the [`Get-CallerPreference` function written by David Wyatt](https://gallery.technet.microsoft.com/scriptcenter/Inherit-Preference-82343b9d). There is currently a [bug in PowerShell](https://connect.microsoft.com/PowerShell/Feedback/Details/763621) that causes an error when `ErrorAction` is implicitly set to `Ignore`. If you use this function, you'll need to add explicit `-ErrorAction $ErrorActionPreference` to every function/cmdlet call in your function. Please vote up this issue so it can get fixed. .LINK about_Preference_Variables .LINK about_CommonParameters .LINK https://gallery.technet.microsoft.com/scriptcenter/Inherit-Preference-82343b9d .LINK http://powershell.org/wp/2014/01/13/getting-your-script-module-functions-to-inherit-preference-variables-from-the-caller/ .EXAMPLE Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState Demonstrates how to set the caller's common parameter preference variables in a module function. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] #[Management.Automation.PSScriptCmdlet] # The module function's `$PSCmdlet` object. Requires the function be decorated with the `[CmdletBinding()]` attribute. $Cmdlet, [Parameter(Mandatory = $true)] [Management.Automation.SessionState] # The module function's `$ExecutionContext.SessionState` object. Requires the function be decorated with the `[CmdletBinding()]` attribute. # # Used to set variables in its callers' scope, even if that caller is in a different script module. $SessionState ) Set-StrictMode -Version 'Latest' # List of preference variables taken from the about_Preference_Variables and their common parameter name (taken from about_CommonParameters). $commonPreferences = @{ 'ErrorActionPreference' = 'ErrorAction'; 'DebugPreference' = 'Debug'; 'ConfirmPreference' = 'Confirm'; 'InformationPreference' = 'InformationAction'; 'VerbosePreference' = 'Verbose'; 'WarningPreference' = 'WarningAction'; 'WhatIfPreference' = 'WhatIf'; } foreach( $prefName in $commonPreferences.Keys ) { $parameterName = $commonPreferences[$prefName] # Don't do anything if the parameter was passed in. if( $Cmdlet.MyInvocation.BoundParameters.ContainsKey($parameterName) ) { continue } $variable = $Cmdlet.SessionState.PSVariable.Get($prefName) # Don't do anything if caller didn't use a common parameter. if( -not $variable ) { continue } if( $SessionState -eq $ExecutionContext.SessionState ) { Set-Variable -Scope 1 -Name $variable.Name -Value $variable.Value -Force -Confirm:$false -WhatIf:$false } else { $SessionState.PSVariable.Set($variable.Name, $variable.Value) } } } |