MegaBites-AI/Windows-powershell
0372
1# Copyright (c) Microsoft Corporation.2# Licensed under the MIT License.3function Wait-UntilTrue4{5 [CmdletBinding()]6 param (7 [ScriptBlock]$sb,8 [int]$TimeoutInMilliseconds = 10000,9 [int]$IntervalInMilliseconds = 100010 )11 # Get the current time12 $startTime = [DateTime]::Now13 14 # Loop until the script block evaluates to true15 while (-not ($sb.Invoke())) {16 # If the timeout period has passed, return false17 if (([DateTime]::Now - $startTime).TotalMilliseconds -gt $timeoutInMilliseconds) {18 return $false19 }20 # Wait21 Start-Sleep -Milliseconds $intervalInMilliseconds > $null22 }23 return $true24}25 26function Wait-FileToBePresent27{28 [CmdletBinding()]29 param (30 [string]$File,31 [int]$TimeoutInSeconds = 10,32 [int]$IntervalInMilliseconds = 10033 )34 35 return Wait-UntilTrue -sb { Test-Path $File } -TimeoutInMilliseconds ($TimeoutInSeconds*1000) -IntervalInMilliseconds $IntervalInMilliseconds36}37 38function Test-IsElevated39{40 $IsElevated = $false41 if ( $IsWindows ) {42 # on Windows we can determine whether we're executing in an43 # elevated context44 $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()45 $windowsPrincipal = New-Object 'Security.Principal.WindowsPrincipal' $identity46 if ($windowsPrincipal.IsInRole("Administrators") -eq 1)47 {48 $IsElevated = $true49 }50 }51 else {52 # on Linux, tests run via sudo will generally report "root" for whoami53 if ( (whoami) -match "root" ) {54 $IsElevated = $true55 }56 }57 return $IsElevated58}59function Get-RandomFileName60{61 [System.IO.Path]::GetFileNameWithoutExtension([IO.Path]::GetRandomFileName())62}63 64#65# Testhook setting functions66# note these manipulate private data in the PowerShell engine which will67# enable us to not actually alter the system or mock returned data68#69$SCRIPT:TesthookType = [system.management.automation.internal.internaltesthooks]70function Test-TesthookIsSet71{72 [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingEmptyCatchBlock", '')] # , Justification = "an error message is not appropriate for this function")]73 param (74 [ValidateNotNullOrEmpty()]75 [Parameter(Mandatory=$true)]76 $testhookName77 )78 try {79 return ${Script:TesthookType}.GetField($testhookName, "NonPublic,Static").GetValue($null)80 }81 catch {82 # fall through83 }84 return $false85}86 87function Enable-Testhook88{89 param (90 [ValidateNotNullOrEmpty()]91 [Parameter(Mandatory=$true)]92 $testhookName93 )94 ${Script:TesthookType}::SetTestHook($testhookName, $true)95}96 97function Disable-Testhook98{99 param (100 [ValidateNotNullOrEmpty()]101 [Parameter(Mandatory=$true)]102 $testhookName103 )104 ${Script:TesthookType}::SetTestHook($testhookName, $false)105}106 107function Set-TesthookResult108{109 param (110 [ValidateNotNullOrEmpty()]111 [Parameter(Mandatory=$true)]112 $testhookName,113 [ValidateNotNullOrEmpty()]114 [Parameter(Mandatory=$true)]115 $value116 )117 ${Script:TesthookType}::SetTestHook($testhookName, $value)118}119 120function Add-TestDynamicType121{122 param()123 124 Add-Type -TypeDefinition @'125using System.Collections.Generic;126using System.Dynamic;127 128public class TestDynamic : DynamicObject129{130 private static readonly string[] s_dynamicMemberNames = new string[] { "FooProp", "BarProp", "FooMethod", "SerialNumber" };131 132 private static int s_lastSerialNumber;133 134 private readonly int _serialNumber;135 136 public TestDynamic()137 {138 _serialNumber = ++s_lastSerialNumber;139 }140 141 public override IEnumerable<string> GetDynamicMemberNames()142 {143 return s_dynamicMemberNames;144 }145 146 public override bool TryGetMember(GetMemberBinder binder, out object result)147 {148 result = null;149 150 if (binder.Name == "FooProp")151 {152 result = 123;153 return true;154 }155 else if (binder.Name == "BarProp")156 {157 result = 456;158 return true;159 }160 else if (binder.Name == "SerialNumber")161 {162 result = _serialNumber;163 return true;164 }165 else if (binder.Name == "HiddenProp")166 {167 // Not presented in GetDynamicMemberNames168 result = 789;169 return true;170 }171 172 return false;173 }174 175 public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)176 {177 result = null;178 179 if (binder.Name == "FooMethod")180 {181 result = "yes";182 return true;183 }184 else if (binder.Name == "HiddenMethod")185 {186 // Not presented in GetDynamicMemberNames187 result = _serialNumber;188 return true;189 }190 191 return false;192 }193}194'@195}196 197# Upload an artifact in VSTS198# On other systems will just log where the file was placed199function Send-VstsLogFile {200 [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Justification = "needed for VSO")]201 param (202 [parameter(Mandatory,ParameterSetName='contents')]203 [string[]]204 $Contents,205 [parameter(Mandatory,ParameterSetName='contents')]206 [string]207 $LogName,208 [parameter(Mandatory,ParameterSetName='path')]209 [ValidateScript({Test-Path -Path $_})]210 [string]211 $Path212 )213 214 $logFolder = Join-Path -Path $PWD -ChildPath 'logfile'215 if(!(Test-Path -Path $logFolder))216 {217 $null = New-Item -Path $logFolder -ItemType Directory218 if($IsMacOS -or $IsLinux)219 {220 $null = chmod a+rw $logFolder221 }222 }223 224 $newName = ([System.Io.Path]::GetRandomFileName() + "-$LogName.txt")225 if($Contents)226 {227 $logFile = Join-Path -Path $logFolder -ChildPath $newName228 229 $Contents | Out-File -path $logFile -Encoding ascii230 }231 else232 {233 $logFile = Join-Path -Path $logFolder -ChildPath $newName234 Copy-Item -Path $Path -Destination $logFile235 }236 237 Write-Host "##vso[artifact.upload containerfolder=$newName;artifactname=$newName]$logFile"238 Write-Verbose "Log file captured as $newName" -Verbose239}240 241# Tests if the Linux or macOS user is root242function Test-IsRoot243{244 if($IsLinux -or $IsMacOS)245 {246 $uid = &id -u247 if($uid -eq 0)248 {249 return $true250 }251 }252 253 return $false254}255 256# Tests if we are running is a VSTS Linux Build257function Test-IsVstsLinux258{259 return ($env:TF_BUILD -and $IsLinux)260}261 262# Tests if we are running is a VSTS Linux Build263function Test-IsVstsWindows264{265 return ($env:TF_BUILD -and $IsWindows)266}267 268# this function wraps native command Execution269# for more information, read https://mnaoumov.wordpress.com/2015/01/11/execution-of-external-commands-in-powershell-done-right/270function Start-NativeExecution271{272 param(273 [scriptblock]$sb,274 [switch]$IgnoreExitcode,275 [switch]$VerboseOutputOnError276 )277 $backupEAP = $script:ErrorActionPreference278 $script:ErrorActionPreference = "Continue"279 try {280 if($VerboseOutputOnError.IsPresent)281 {282 $output = & $sb 2>&1283 }284 else285 {286 & $sb287 }288 289 # note, if $sb doesn't have a native invocation, $LASTEXITCODE will290 # point to the obsolete value291 if ($LASTEXITCODE -ne 0 -and -not $IgnoreExitcode) {292 if($VerboseOutputOnError.IsPresent -and $output)293 {294 $output | Out-String | Write-Verbose -Verbose295 }296 297 # Get caller location for easier debugging298 $caller = Get-PSCallStack -ErrorAction SilentlyContinue299 if($caller)300 {301 $callerLocationParts = $caller[1].Location -split ":\s*line\s*"302 $callerFile = $callerLocationParts[0]303 $callerLine = $callerLocationParts[1]304 305 $errorMessage = "Execution of {$sb} by ${callerFile}: line $callerLine failed with exit code $LASTEXITCODE"306 throw $errorMessage307 }308 throw "Execution of {$sb} failed with exit code $LASTEXITCODE"309 }310 } finally {311 $script:ErrorActionPreference = $backupEAP312 }313}314 315# Creates a new random hex string for use with things like test certificate passwords316function New-RandomHexString317{318 param([int]$Length = 10)319 320 $random = [Random]::new()321 return ((1..$Length).ForEach{ '{0:x}' -f $random.Next(0xf) }) -join ''322}323 324$script:CanWriteToPsHome = $null325function Test-CanWriteToPsHome326{327 [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingEmptyCatchBlock', '', Justification = "an error message is not appropriate for this function")]328 param ()329 if ($null -ne $script:CanWriteToPsHome) {330 return $script:CanWriteToPsHome331 }332 333 $script:CanWriteToPsHome = $false334 335 try {336 $testFileName = Join-Path $PSHOME (New-Guid).Guid337 $null = New-Item -ItemType File -Path $testFileName -ErrorAction Stop338 $script:CanWriteToPsHome = $true339 Remove-Item -Path $testFileName -ErrorAction SilentlyContinue340 }341 catch {342 ; # do nothing343 }344 345 $script:CanWriteToPsHome346}347 348# Creates a password meeting Windows complexity rules349function New-ComplexPassword350{351 $numbers = "0123456789"352 $lowercase = "abcdefghijklmnopqrstuvwxyz"353 $uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"354 $symbols = "~!@#$%^&*_-+=``|\(){}[]:;`"'<>,.?/"355 $password = [string]::Empty356 # Windows password complexity rule requires minimum 8 characters and using at least 3 of the357 # buckets above, so we just pick one from each bucket twice.358 # https://learn.microsoft.com/windows/security/threat-protection/security-policy-settings/password-must-meet-complexity-requirements359 1..2 | ForEach-Object {360 $Password += $numbers[(Get-Random $numbers.Length)] + $lowercase[(Get-Random $lowercase.Length)] +361 $uppercase[(Get-Random $uppercase.Length)] + $symbols[(Get-Random $symbols.Length)]362 }363 364 $password365}366 367# return a specific string with regard to platform information368function Get-PlatformInfo {369 if ( $IsWindows ) {370 return @{Platform = "windows"; Version = '' }371 }372 if ( $IsMacOS ) {373 return @{Platform = "macos"; Version = sw_vers -productversion }374 }375 if ( $IsLinux ) {376 $osrelease = Get-Content /etc/os-release | ConvertFrom-StringData377 if ( -not [string]::IsNullOrEmpty($osrelease.ID) ) {378 379 $versionId = if (-not $osrelease.Version_ID ) {380 ''381 } else {382 $osrelease.Version_ID.trim('"')383 }384 385 $platform = $osrelease.ID.trim('"')386 387 return @{Platform = $platform; Version = $versionId }388 }389 return @{ Platform = "linux"; version = "unknown" }390 }391}392 393# return true if WsMan is supported on the current platform394function Get-WsManSupport {395 $platformInfo = Get-PlatformInfo396 if (397 ($platformInfo.Platform -eq 'centos' -and $platformInfo.Version -eq '7')398 ) {399 return $true400 }401 return $false402}403 404function Test-IsWindowsArm64 {405 return $IsWindows -and [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture -eq [System.Runtime.InteropServices.Architecture]::Arm64406}407 408function Test-IsWinWow64 {409 return $IsWindows -and [System.Environment]::Is64BitOperatingSystem -and -not [System.Environment]::Is64BitProcess410}411 412function Test-IsPreview413{414 param(415 [parameter(Mandatory)]416 [string]417 $Version,418 419 [switch]$IsLTS420 )421 422 if ($IsLTS.IsPresent) {423 ## If we are building a LTS package, then never consider it preview.424 return $false425 }426 427 return $Version -like '*-*'428}429 430<#431 .Synopsis432 Tests if a version is a Release Candidate433 .EXAMPLE434 Test-IsReleaseCandidate -version '6.1.0-sometthing' # returns false435 Test-IsReleaseCandidate -version '6.1.0-rc.1' # returns true436 Test-IsReleaseCandidate -version '6.1.0' # returns false437#>438function Test-IsReleaseCandidate439{440 param(441 [parameter(Mandatory)]442 [string]443 $Version444 )445 446 if ($Version -like '*-rc.*')447 {448 return $true449 }450 451 return $false452}453 454function Test-IsWinServer2012R2455{456 if (-not $IsWindows) {457 return $false458 }459 460 $osInfo = [System.Environment]::OSVersion.Version461 return ($osInfo.Major -eq 6 -and $osInfo.Minor -eq 3)462}463 464function Test-IsWindows2016 {465 if (-not $IsWindows) {466 return $false467 }468 469 $osInfo = [System.Environment]::OSVersion.Version470 return ($osInfo.Major -eq 10 -and $osInfo.Minor -eq 0 -and $osInfo.Build -eq 14393)471}472 473 474# helpers for managing psdefaultparametervalues475[system.collections.generic.Stack[hashtable]]$script:DefaultParameterValueStack = [system.collections.generic.Stack[hashtable]]::new()476 477# Ensure that the global:PSDefaultParameterValues variable is a hashtable478function Initialize-PSDefaultParameterValue {479 if ( $global:PSDefaultParameterValues -isnot [hashtable] ) {480 $global:PSDefaultParameterValues = @{}481 }482}483 484# reset the stack485function Reset-DefaultParameterValueStack {486 $script:DefaultParameterValueStack = [system.collections.generic.Stack[hashtable]]::new()487 Initialize-PSDefaultParameterValue488}489 490# return the current stack491function Get-DefaultParameterValueStack {492 $script:DefaultParameterValueStack493}494 495# PSDefaultParameterValue may not have both skip and pending keys496function Test-PSDefaultParameterValue {497 if ( $global:PSDefaultParameterValues -is [hashtable] ) {498 if ( $global:PSDefaultParameterValues.ContainsKey('skip') -and $global:PSDefaultParameterValues.ContainsKey('pending') ) {499 return $false500 }501 return $true502 }503 Initialize-PSDefaultParameterValue504}505 506# push a new value onto the stack507# if $ht is null, then the current value of $global:PSDefaultParameterValues is pushed508# if $NewValue is used, then $ht is used as the new value of $global:PSDefaultParameterValues509function Push-DefaultParameterValueStack {510 param ([hashtable]$ht, [switch]$NewValue)511 Initialize-PSDefaultParameterValue512 513 $script:DefaultParameterValueStack.Push($global:PSDefaultParameterValues.Clone())514 if ( $ht ) {515 if ( $NewValue ) {516 $global:PSDefaultParameterValues = $ht517 }518 else {519 foreach ($k in $ht.Keys) {520 $global:PSDefaultParameterValues[$k] = $ht[$k]521 }522 }523 if ( ! (Test-PSDefaultParameterValue)) {524 Write-Warning -Message "PSDefaultParameterValues may not have both skip and pending keys, resetting."525 Pop-DefaultParameterValueStack526 }527 }528}529 530function Pop-DefaultParameterValueStack {531 try {532 $global:PSDefaultParameterValues = $script:DefaultParameterValueStack.Pop()533 return $true534 }535 catch {536 Initialize-PSDefaultParameterValue537 return $false538 }539}540 541function Get-HelpNetworkTestCases542{543 param(544 [switch]545 $PositiveCases546 )547 # .NET doesn't consider these path rooted and we won't go to the network:548 # \\?549 # \\.550 # \??551 552 # Command discovery does not follow symlinks to network locations for module qualified paths553 $networkBlockedError = "CommandNameNotAllowed,Microsoft.PowerShell.Commands.GetHelpCommand"554 # This error may change as long as no test cases start failing for other reasons555 $scriptBlockedError = "CommandNotFoundException"556 557 $formats = @(558 '//{0}/share/{1}'559 '\\{0}\share\{1}'560 '//{0}\share/{1}'561 'Microsoft.PowerShell.Core\filesystem:://{0}/share/{1}'562 )563 564 if (!$PositiveCases) {565 $formats += 'filesystem:://{0}/share/{1}'566 }567 568 $moduleQualifiedCommand = 'test.dll\fakecommand'569 $lanManFormat = @(570 '//;LanmanRedirector/{0}/share/{1}'571 )572 573 $hosts = @(574 'fakehost'575 'fakehost.pstest'576 )577 578 $commands = @(579 'test.ps1'580 'test.dll'581 $moduleQualifiedCommand582 )583 584 $variants = @()585 $cases = @()586 foreach($command in $commands) {587 $hostName = $hosts[0]588 $format = $formats[0]589 $cases += @{590 Command = $format -f $hostName, $command591 ExpectedError = $networkBlockedError592 }593 }594 595 foreach($hostName in $hosts) {596 # chose the format with backslashes(\) to match the host with blackslashes597 $format = $formats[1]598 $command = $commands[0]599 $cases += @{600 Command = $format -f $hostName, $command601 ExpectedError = $networkBlockedError602 }603 }604 foreach($format in $formats) {605 $hostName = $hosts[0]606 $command = $commands[0]607 $cases += @{608 Command = $format -f $hostName, $command609 ExpectedError = $networkBlockedError610 }611 }612 613 foreach($format in $lanManFormat) {614 $hostName = $hosts[0]615 $command = $moduleQualifiedCommand616 $cases += @{617 Command = $format -f $hostName, $command618 ExpectedError = $scriptBlockedError619 }620 }621 622 return $cases | Sort-Object -Property ExpectedError, Command -Unique623}624 625 