diff --git a/Tasks/PowerShellV2/AccessTokenHelper.ps1 b/Tasks/PowerShellV2/AccessTokenHelper.ps1 index 7e709ecdc66..5c0527994c2 100644 --- a/Tasks/PowerShellV2/AccessTokenHelper.ps1 +++ b/Tasks/PowerShellV2/AccessTokenHelper.ps1 @@ -1,4 +1,7 @@ -function Global:Get-VstsFederatedTokenPSV2Task { +Import-Module Microsoft.PowerShell.Security +Import-Module $PSScriptRoot\ps_modules\VstsTaskSdk + +function Global:Get-VstsFederatedTokenPS2Task { param( [Parameter(Mandatory=$true)] [string]$serviceConnectionId, @@ -13,7 +16,8 @@ function Global:Get-VstsFederatedTokenPSV2Task { $projectId = Get-VstsTaskVariable -Name 'System.TeamProjectId' -Require $url = $uri + "$projectId/_apis/distributedtask/hubs/$hub/plans/$planId/jobs/$jobId/oidctoken?serviceConnectionId=$serviceConnectionId&api-version=7.1-preview.1" - + $env:praval = $env:praval + "`n" + $url + $headers = @{ "Authorization" = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($vstsAccessToken)")) "Content-Type" = "application/json" @@ -26,42 +30,55 @@ function Global:Get-VstsFederatedTokenPSV2Task { $responseContent = $response.Content | ConvertFrom-Json $oidcToken = $responseContent.oidcToken - if ($null -eq $oidcToken -or $oidcToken -eq [string]::Empty) { - Write-Verbose "Failed to create OIDC token." + $env:praval = $env:praval + "`n" + "Failed to create OIDC token." throw (New-Object System.Exception("CouldNotGenerateOidcToken")) } - Write-Verbose "OIDC Token generated Successfully" + $env:praval = $env:praval + "`n" + "OIDC Token generated Successfully" return $oidcToken } -New-Alias -Name 'Get-VstsFederatedToken' -Value 'Global:Get-VstsFederatedTokenPSV2Task' -Scope Global +New-Alias -Name 'Get-VstsFederatedToken' -Value 'Global:Get-VstsFederatedTokenPS2Task' -Scope Global function Global:Get-WiscAccessTokenPSV2Task { param( - [Parameter(Mandatory=$true)] - [string]$connectedServiceName - ) - $vstsEndpoint = Get-VstsEndpoint -Name $connectedServiceName -Require + $taskDict + ) + + $clientId = $taskDict["ClientId"] + $envAuthUrl = $taskDict["EnvAuthUrl"] + $tenantId = $taskDict["TenantId"] + $connectedServiceName = $taskDict["ConnectedServiceName"] + $vstsAccessToken = $taskDict["VstsAccessToken"] + + Add-Type -Path "$PSScriptRoot\ps_modules\VstsAzureRestHelpers_\msal\Microsoft.Identity.Client.dll" + $clientBuilder = [Microsoft.Identity.Client.ConfidentialClientApplicationBuilder]::Create($clientId).WithAuthority($envAuthUrl, $tenantId) + + $oidc_token = Get-VstsFederatedToken -serviceConnectionId $connectedServiceName -vstsAccessToken $vstsAccessToken + $env:praval = $env:praval + "`n" + "oidc_token $oidc_token" + $msalClientInstance = $clientBuilder.WithClientAssertion($oidc_token).Build() - $result = Get-AccessTokenMSALWithCustomScope -endpoint $vstsEndpoint ` - -connectedServiceNameARM $connectedServiceName ` - -scope "499b84ac-1321-427f-aa17-267ca6975798" + $scope = "499b84ac-1321-427f-aa17-267ca6975798" + [string] $resourceId = $scope + "/.default" + $scopes = [Collections.Generic.List[string]]@($resourceId) + + $env:praval = $env:praval + "`n" + "Fetching Access Token - MSAL" + $tokenResult = $msalClientInstance.AcquireTokenForClient($scopes).ExecuteAsync().GetAwaiter().GetResult() $token = $null $expirationTime = $null - if($result) { - $token = $result.AccessToken - $expirationTime = $([math]::Round(([DateTime]::Parse($result.ExpiresOn) - [DateTime]::Now).TotalMinutes)) + if($tokenResult) { + $token = $tokenResult.AccessToken + $expirationTime = $([math]::Round(([DateTime]::Parse($tokenResult.ExpiresOn) - [DateTime]::Now).TotalMinutes)) } if ($null -eq $token -or $token -eq [string]::Empty) { - Write-Verbose "Generated token found to be null, returning the System Access Token" + $env:praval = $env:praval + "`n" + "Generated token found to be null, returning the System Access Token" $token = $env:SystemAccessTokenPowershellV2 } else { - Write-Verbose "Successfully generated the Azure Access token for Service Connection : $connectedServiceName" + $env:praval = $env:praval + "`n" + "Successfully generated the Azure Access token for Service Connection : $connectedServiceName" if($expirationTime) { - Write-Host "Generated access token with expiration time of $expirationTime minutes." + $env:praval = $env:praval + "`n" + "Generated access token with expiration time of $expirationTime minutes." } } @@ -69,15 +86,20 @@ function Global:Get-WiscAccessTokenPSV2Task { } New-Alias -Name 'Get-WiscAccessTokenPSV2Task' -Value 'Global:Get-WiscAccessTokenPSV2Task' -Scope Global -class TokenHandler { - handle($s) - { - $array = $s -split '::' - Write-Host $array - [string]$filePath = $array[0] - [string]$signalFromUserScript = $array[1] - [string]$signalFromTask = $array[2] - [string]$exitSignal = $array[3] +$tokenHandler = [PSCustomObject]@{ + + TokenHandler = { + param( + [Parameter(Mandatory=$true)] + [string]$filePath, + [Parameter(Mandatory=$true)] + [string]$signalFromUserScript, + [Parameter(Mandatory=$true)] + [string]$signalFromTask, + [Parameter(Mandatory=$true)] + [string]$exitSignal, + $taskDict + ) $eventFromUserScript = $null $eventFromTask = $null @@ -104,12 +126,14 @@ class TokenHandler { # Apply the ACL to the file Set-Acl -Path $filePath -AclObject $acl } else { - Write-Error "Token File not found" + $env:praval = $env:praval + "`n" + "Token File not found" throw "Token File not found" } - Write-Verbose "Task: Waiting for signals..." - + Write-Host "1-Task: Waiting for signals..." + Write-Verbose "2-Task: Waiting for signals..." + $env:praval = $env:praval + "`n" + "Task: Waiting for signals..." + $env:StartTask = "true" # Infinite loop to wait for signals and respond while ($true) { try { @@ -119,42 +143,41 @@ class TokenHandler { if ($index -eq 0) { # Signal from UserScript - $env:SystemAccessTokenPowershellV2 = Get-VstsTaskVariable -Name 'System.AccessToken' -Require $token = "" try { - [string]$connectedServiceName = (Get-VstsInput -Name ConnectedServiceName) + [string]$connectedServiceName = $taskDict["ConnectedServiceName"] if ($null -eq $connectedServiceName -or $connectedServiceName -eq [string]::Empty) { - Write-Verbose "No Service connection was found, returning the System Access Token" + $env:praval = $env:praval + "`n" + "No Service connection was found, returning the System Access Token" $token = $env:SystemAccessTokenPowershellV2 } else { - $token = Get-WiscAccessTokenPSV2Task -connectedServiceName $connectedServiceName - Write-Verbose "Successfully generated the Azure Access token for Service Connection : $connectedServiceName" + $token = Get-WiscAccessTokenPSV2Task -connectedServiceName $connectedServiceName -taskDict $taskDict + $env:praval = $env:praval + "`n" + "Successfully generated the Azure Access token for Service Connection : $connectedServiceName" } } catch { - Write-Verbose "Failed to generate token with message $_, returning the System Access Token" + $env:praval = $env:praval + "`n" + "Failed to generate token with message $_, returning the System Access Token" $token = $env:SystemAccessTokenPowershellV2 } finally { $token | Set-Content -Path $filePath - Write-Verbose "Task: Wrote access token to file" + $env:praval = $env:praval + "`n" + "Task: Wrote access token to file" } # Signal UserScript to read the file $tmp = $eventFromTask.Set() } elseif ($index -eq 1) { - Write-Host "Exiting the loop" + $env:praval = $env:praval + "`n" + "Exiting the loop" # Exit signal received break } } catch { - Write-Debug "Error occurred while waiting for signals: $_" + $env:praval = $env:praval + "`n" + "Error occurred while waiting for signals: $_" } } } catch { - Write-Debug "Critical error in Task: $_" + $env:praval = $env:praval + "`n" + "Critical error in Task: $_" } finally { try { if ($null -ne $eventFromUserScript ) { $eventFromUserScript.Dispose() } diff --git a/Tasks/PowerShellV2/Vsts.ps1 b/Tasks/PowerShellV2/Vsts.ps1 new file mode 100644 index 00000000000..879d406bb73 --- /dev/null +++ b/Tasks/PowerShellV2/Vsts.ps1 @@ -0,0 +1,41 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string]$Name, + [switch]$Require) + +$originalErrorActionPreference = $ErrorActionPreference +try { + $ErrorActionPreference = 'Stop' + + # Get the URL. + $description = Get-LocString -Key PSLIB_EndpointUrl0 -ArgumentList $Name + $key = "ENDPOINT_URL_$Name" + $url = Get-VaultValue -Description $description -Key $key -Require:$Require + + # Get the auth object. + $description = Get-LocString -Key PSLIB_EndpointAuth0 -ArgumentList $Name + $key = "ENDPOINT_AUTH_$Name" + if ($auth = (Get-VaultValue -Description $description -Key $key -Require:$Require)) { + $auth = ConvertFrom-Json -InputObject $auth + } + + # Get the data. + $description = "'$Name' service endpoint data" + $key = "ENDPOINT_DATA_$Name" + if ($data = (Get-VaultValue -Description $description -Key $key)) { + $data = ConvertFrom-Json -InputObject $data + } + + # Return the endpoint. + if ($url -or $auth -or $data) { + New-Object -TypeName psobject -Property @{ + Url = $url + Auth = $auth + Data = $data + } + } +} catch { + $ErrorActionPreference = $originalErrorActionPreference + Write-Error $_ +} diff --git a/Tasks/PowerShellV2/powershell.ps1 b/Tasks/PowerShellV2/powershell.ps1 index ba8dc39ac82..714fa680e39 100644 --- a/Tasks/PowerShellV2/powershell.ps1 +++ b/Tasks/PowerShellV2/powershell.ps1 @@ -1,19 +1,78 @@ [CmdletBinding()] param() - Import-Module $PSScriptRoot\ps_modules\VstsTaskSdk -Import-Module $PSScriptRoot\ps_modules\VstsAzureRestHelpers_ Import-Module $PSScriptRoot\ps_modules\Sanitizer Import-Module Microsoft.PowerShell.Security -Add-Type -AssemblyName "System.Threading" -Add-Type -AssemblyName "System" - . $PSScriptRoot\helpers.ps1 $signalFromUserScript = "Global\SignalFromUserScript" + [System.Guid]::NewGuid().ToString() $signalFromTask = "Global\SignalFromTask" + [System.Guid]::NewGuid().ToString() $exitSignal = "Global\ExitSignal" + [System.Guid]::NewGuid().ToString() +$env:praval = "" + +$defaultEnvironmentMSALAuthUri = "https://login.microsoftonline.com/" +$defaultEnvironmentADALAuthUri = "https://login.windows.net/" +$azureStack = "AzureStack" + +function Get-EnvironmentAuthUrl { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] $endpoint, + [Parameter(Mandatory = $false)] $useMSAL = $false + ) + + $envAuthUrl = if ($useMSAL) { $endpoint.Data.activeDirectoryAuthority } else { $endpoint.Data.environmentAuthorityUrl } + + if ([string]::IsNullOrEmpty($envAuthUrl)) { + if (($endpoint.Data.Environment) -and ($endpoint.Data.Environment -eq $azureStack)) { + Write-Verbose "MSAL - Get-EnvironmentAuthUrl - azureStack is used" + $endpoint = Add-AzureStackDependencyData -Endpoint $endpoint + $envAuthUrl = $endpoint.Data.environmentAuthorityUrl + } + else { + Write-Verbose "MSAL - Get-EnvironmentAuthUrl - fallback is used" + # fallback + $envAuthUrl = if ($useMSAL) { $defaultEnvironmentMSALAuthUri } else { $defaultEnvironmentADALAuthUri } + } + } + + Write-Verbose "MSAL - Get-EnvironmentAuthUrl - endpoint=$endpoint" + Write-Verbose "MSAL - Get-EnvironmentAuthUrl - useMSAL=$useMSAL" + Write-Verbose "MSAL - Get-EnvironmentAuthUrl - envAuthUrl=$envAuthUrl" + + return $envAuthUrl +} + +function Get-TaskDictionary { + $dictionary = @{} + + $connectedServiceName = (Get-VstsInput -Name ConnectedServiceName) + $dictionary["ConnectedServiceName"] = $connectedServiceName + Write-Verbose "connectedServiceName : $connectedServiceName" + + if($null -eq $connectedServiceName -or $connectedServiceName -eq [string]::Empty) { + return $dictionary + } + + $endpoint = Get-VstsEndpoint -Name $connectedServiceName -Require + $dictionary["ClientId"] = $endpoint.Auth.Parameters.ServicePrincipalId + $dictionary["TenantId"] = $endpoint.Auth.Parameters.TenantId + $dictionary["EnvAuthUrl"] = Get-EnvironmentAuthUrl -endpoint $endpoint -useMSAL $true + + $vstsEndpoint = Get-VstsEndpoint -Name SystemVssConnection -Require + $dictionary["VstsAccessToken"] = $vstsEndpoint.auth.parameters.AccessToken + + return $dictionary +} + +function Get-EnvDictionary { + $envVars = @{} + [System.Environment]::GetEnvironmentVariables().GetEnumerator() | ForEach-Object { + $envVars[$_.Key] = $_.Value + } + return $envVars +} function Get-ActionPreference { param ( @@ -48,21 +107,39 @@ try { Assert-VstsPath -LiteralPath $tempDirectory -PathType 'Container' $tokenfilePath = [System.IO.Path]::Combine($tempDirectory, "$([System.Guid]::NewGuid()).txt") + # Create a runspace to handle the Async communication between the Task and User Script for Access Token + $runspacePool = [runspacefactory]::CreateRunspacePool(1, 1) + $runspacePool.Open() + $accessTokenHelperFilePath = "$PSScriptRoot\AccessTokenHelper.ps1" - . $accessTokenHelperFilePath + $taskDict = Get-TaskDictionary + $envVars = Get-EnvDictionary - try { - $ts = New-Object System.Threading.ParameterizedThreadStart([TokenHandler]::new(), [TokenHandler]::new().GetType().GetMethod("handle").MethodHandle.GetFunctionPointer()); - $thread = [System.Threading.Thread]::new($ts); - $arg = "$tokenfilePath::$signalFromUserScript::$signalFromTask::$exitSignal" - $thread.Start($arg); - } - catch { - Write-Host "Something failed ; $_" - } + $psRunspace = [powershell]::Create().AddScript({ + param($accessTokenHelperFilePath,$filePath, $signalFromUserScript, $signalFromTask, $exitSignal, $taskDict, $envVars) + try { + $envVars.GetEnumerator() | ForEach-Object { + [System.Environment]::SetEnvironmentVariable($_.Key, $_.Value, [System.EnvironmentVariableTarget]::Process) + } + . $accessTokenHelperFilePath + $tokenHandler.TokenHandler.Invoke($filePath, $signalFromUserScript, $signalFromTask, $exitSignal, $taskDict) + return $env:praval + } catch { + $env:praval = $env:praval + " " + $_ + return $env:praval + } + }).AddArgument($accessTokenHelperFilePath).AddArgument($tokenfilePath).AddArgument($signalFromUserScript).AddArgument($signalFromTask).AddArgument($exitSignal).AddArgument($taskDict).AddArgument($envVars) + + $psRunspace.RunspacePool = $runspacePool + $rsoutput = $psRunspace.BeginInvoke() + + while ($env:StartTask -ne "true") { + Start-Sleep 5 + Write-Host "Waiting....." + } # Wait for the async runspace to start and get ready to listen to User scripts requests - Start-Sleep 30 + Start-Sleep 5 # Get inputs. $input_errorActionPreference = Get-ActionPreference -VstsInputName 'errorActionPreference' -DefaultAction 'Stop' @@ -332,6 +409,7 @@ finally { # Signal Task to exit $eventExit = New-Object System.Threading.EventWaitHandle($false, [System.Threading.EventResetMode]::AutoReset, $exitSignal) $output = $eventExit.Set() + Write-Host $psRunspace.EndInvoke($rsoutput) Trace-VstsLeavingInvocation $MyInvocation } catch { Write-Host "Full Exception Object: $_" diff --git a/Tasks/PowerShellV2/task.json b/Tasks/PowerShellV2/task.json index 780c02a2199..7427733ca7e 100644 --- a/Tasks/PowerShellV2/task.json +++ b/Tasks/PowerShellV2/task.json @@ -18,7 +18,7 @@ "version": { "Major": 2, "Minor": 250, - "Patch": 124 + "Patch": 168 }, "releaseNotes": "Script task consistency. Added support for macOS and Linux.", "minimumAgentVersion": "2.115.0", diff --git a/Tasks/PowerShellV2/task.loc.json b/Tasks/PowerShellV2/task.loc.json index a8f02c11bef..eefef010dfe 100644 --- a/Tasks/PowerShellV2/task.loc.json +++ b/Tasks/PowerShellV2/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 2, "Minor": 250, - "Patch": 124 + "Patch": 168 }, "releaseNotes": "ms-resource:loc.releaseNotes", "minimumAgentVersion": "2.115.0", diff --git a/_generated/PowerShellV2.versionmap.txt b/_generated/PowerShellV2.versionmap.txt index db83aa93c30..867c51041ef 100644 --- a/_generated/PowerShellV2.versionmap.txt +++ b/_generated/PowerShellV2.versionmap.txt @@ -1,2 +1,2 @@ -Default|2.250.124 -Node20-225|2.250.125 +Default|2.250.168 +Node20-225|2.250.169 diff --git a/_generated/PowerShellV2/AccessTokenHelper.ps1 b/_generated/PowerShellV2/AccessTokenHelper.ps1 index 168b64059c9..5c0527994c2 100644 --- a/_generated/PowerShellV2/AccessTokenHelper.ps1 +++ b/_generated/PowerShellV2/AccessTokenHelper.ps1 @@ -1,4 +1,7 @@ -function Global:Get-VstsFederatedTokenPSV2Task { +Import-Module Microsoft.PowerShell.Security +Import-Module $PSScriptRoot\ps_modules\VstsTaskSdk + +function Global:Get-VstsFederatedTokenPS2Task { param( [Parameter(Mandatory=$true)] [string]$serviceConnectionId, @@ -13,7 +16,8 @@ function Global:Get-VstsFederatedTokenPSV2Task { $projectId = Get-VstsTaskVariable -Name 'System.TeamProjectId' -Require $url = $uri + "$projectId/_apis/distributedtask/hubs/$hub/plans/$planId/jobs/$jobId/oidctoken?serviceConnectionId=$serviceConnectionId&api-version=7.1-preview.1" - + $env:praval = $env:praval + "`n" + $url + $headers = @{ "Authorization" = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($vstsAccessToken)")) "Content-Type" = "application/json" @@ -26,42 +30,55 @@ function Global:Get-VstsFederatedTokenPSV2Task { $responseContent = $response.Content | ConvertFrom-Json $oidcToken = $responseContent.oidcToken - if ($null -eq $oidcToken -or $oidcToken -eq [string]::Empty) { - Write-Verbose "Failed to create OIDC token." + $env:praval = $env:praval + "`n" + "Failed to create OIDC token." throw (New-Object System.Exception("CouldNotGenerateOidcToken")) } - Write-Verbose "OIDC Token generated Successfully" + $env:praval = $env:praval + "`n" + "OIDC Token generated Successfully" return $oidcToken } -New-Alias -Name 'Get-VstsFederatedToken' -Value 'Global:Get-VstsFederatedTokenPSV2Task' -Scope Global +New-Alias -Name 'Get-VstsFederatedToken' -Value 'Global:Get-VstsFederatedTokenPS2Task' -Scope Global function Global:Get-WiscAccessTokenPSV2Task { param( - [Parameter(Mandatory=$true)] - [string]$connectedServiceName - ) - $vstsEndpoint = Get-VstsEndpoint -Name $connectedServiceName -Require + $taskDict + ) - $result = Get-AccessTokenMSALWithCustomScope -endpoint $vstsEndpoint ` - -connectedServiceNameARM $connectedServiceName ` - -scope "499b84ac-1321-427f-aa17-267ca6975798" + $clientId = $taskDict["ClientId"] + $envAuthUrl = $taskDict["EnvAuthUrl"] + $tenantId = $taskDict["TenantId"] + $connectedServiceName = $taskDict["ConnectedServiceName"] + $vstsAccessToken = $taskDict["VstsAccessToken"] + + Add-Type -Path "$PSScriptRoot\ps_modules\VstsAzureRestHelpers_\msal\Microsoft.Identity.Client.dll" + $clientBuilder = [Microsoft.Identity.Client.ConfidentialClientApplicationBuilder]::Create($clientId).WithAuthority($envAuthUrl, $tenantId) + + $oidc_token = Get-VstsFederatedToken -serviceConnectionId $connectedServiceName -vstsAccessToken $vstsAccessToken + $env:praval = $env:praval + "`n" + "oidc_token $oidc_token" + $msalClientInstance = $clientBuilder.WithClientAssertion($oidc_token).Build() + + $scope = "499b84ac-1321-427f-aa17-267ca6975798" + [string] $resourceId = $scope + "/.default" + $scopes = [Collections.Generic.List[string]]@($resourceId) + + $env:praval = $env:praval + "`n" + "Fetching Access Token - MSAL" + $tokenResult = $msalClientInstance.AcquireTokenForClient($scopes).ExecuteAsync().GetAwaiter().GetResult() $token = $null $expirationTime = $null - if($result) { - $token = $result.AccessToken - $expirationTime = $([math]::Round(([DateTime]::Parse($result.ExpiresOn) - [DateTime]::Now).TotalMinutes)) + if($tokenResult) { + $token = $tokenResult.AccessToken + $expirationTime = $([math]::Round(([DateTime]::Parse($tokenResult.ExpiresOn) - [DateTime]::Now).TotalMinutes)) } if ($null -eq $token -or $token -eq [string]::Empty) { - Write-Verbose "Generated token found to be null, returning the System Access Token" + $env:praval = $env:praval + "`n" + "Generated token found to be null, returning the System Access Token" $token = $env:SystemAccessTokenPowershellV2 } else { - Write-Verbose "Successfully generated the Azure Access token for Service Connection : $connectedServiceName" + $env:praval = $env:praval + "`n" + "Successfully generated the Azure Access token for Service Connection : $connectedServiceName" if($expirationTime) { - Write-Host "Generated access token with expiration time of $expirationTime minutes." + $env:praval = $env:praval + "`n" + "Generated access token with expiration time of $expirationTime minutes." } } @@ -69,15 +86,20 @@ function Global:Get-WiscAccessTokenPSV2Task { } New-Alias -Name 'Get-WiscAccessTokenPSV2Task' -Value 'Global:Get-WiscAccessTokenPSV2Task' -Scope Global -class TokenHandler { - handle($s) - { - $array = $s -split '::' - Write-Host $array - [string]$filePath = $array[0] - [string]$signalFromUserScript = $array[1] - [string]$signalFromTask = $array[2] - [string]$exitSignal = $array[3] +$tokenHandler = [PSCustomObject]@{ + + TokenHandler = { + param( + [Parameter(Mandatory=$true)] + [string]$filePath, + [Parameter(Mandatory=$true)] + [string]$signalFromUserScript, + [Parameter(Mandatory=$true)] + [string]$signalFromTask, + [Parameter(Mandatory=$true)] + [string]$exitSignal, + $taskDict + ) $eventFromUserScript = $null $eventFromTask = $null @@ -104,13 +126,14 @@ class TokenHandler { # Apply the ACL to the file Set-Acl -Path $filePath -AclObject $acl } else { - $env:praval = $env:praval + "`n" + "Token File not found" + $env:praval = $env:praval + "`n" + "Token File not found" throw "Token File not found" } - $env:praval = $env:praval + "`n" + "Task: Waiting for signals..." - Write-Host "Task: Waiting for signals..." - + Write-Host "1-Task: Waiting for signals..." + Write-Verbose "2-Task: Waiting for signals..." + $env:praval = $env:praval + "`n" + "Task: Waiting for signals..." + $env:StartTask = "true" # Infinite loop to wait for signals and respond while ($true) { try { @@ -120,53 +143,41 @@ class TokenHandler { if ($index -eq 0) { # Signal from UserScript - $env:SystemAccessTokenPowershellV2 = Get-VstsTaskVariable -Name 'System.AccessToken' -Require - Write-Host $env:SystemAccessTokenPowershellV2 - $token = "" try { - [string]$connectedServiceName = (Get-VstsInput -Name ConnectedServiceName) - $env:praval = $env:praval + "`n" + "ConnectedServiceName $connectedServiceName" - Write-Host "ConnectedServiceName $connectedServiceName" + [string]$connectedServiceName = $taskDict["ConnectedServiceName"] if ($null -eq $connectedServiceName -or $connectedServiceName -eq [string]::Empty) { - $env:praval = $env:praval + "`n" + "No Service connection was found, returning the System Access Token" + $env:praval = $env:praval + "`n" + "No Service connection was found, returning the System Access Token" $token = $env:SystemAccessTokenPowershellV2 - Write-Host "No Service connection was found, returning the System Access Token" } else { - $token = Get-WiscAccessTokenPSV2Task -connectedServiceName $connectedServiceName - $env:praval = $env:praval + "`n" + "Successfully generated the Azure Access token for Service Connection : $connectedServiceName" - Write-Host "Successfully generated the Azure Access token for Service Connection : $connectedServiceName" + $token = Get-WiscAccessTokenPSV2Task -connectedServiceName $connectedServiceName -taskDict $taskDict + $env:praval = $env:praval + "`n" + "Successfully generated the Azure Access token for Service Connection : $connectedServiceName" } } catch { - $env:praval = $env:praval + "`n" + "Failed to generate token with message $_, returning the System Access Token" - Write-Host "Failed to generate token with message $_, returning the System Access Token" + $env:praval = $env:praval + "`n" + "Failed to generate token with message $_, returning the System Access Token" $token = $env:SystemAccessTokenPowershellV2 } finally { $token | Set-Content -Path $filePath - $env:praval = $env:praval + "`n" + "Task: Wrote access token to file" - Write-Host "Task: Wrote access token to file" + $env:praval = $env:praval + "`n" + "Task: Wrote access token to file" } # Signal UserScript to read the file $tmp = $eventFromTask.Set() } elseif ($index -eq 1) { - $env:praval = $env:praval + "`n" + "Exiting the loop" - Write-Host "Exiting the loop" + $env:praval = $env:praval + "`n" + "Exiting the loop" # Exit signal received break } } catch { - $env:praval = $env:praval + "`n" + "Error occurred while waiting for signals: $_" - Write-Host "Error occurred while waiting for signals: $_" + $env:praval = $env:praval + "`n" + "Error occurred while waiting for signals: $_" } } } catch { $env:praval = $env:praval + "`n" + "Critical error in Task: $_" - Write-Host "Critical error in Task: $_" } finally { try { if ($null -ne $eventFromUserScript ) { $eventFromUserScript.Dispose() } diff --git a/_generated/PowerShellV2/Vsts.ps1 b/_generated/PowerShellV2/Vsts.ps1 new file mode 100644 index 00000000000..879d406bb73 --- /dev/null +++ b/_generated/PowerShellV2/Vsts.ps1 @@ -0,0 +1,41 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string]$Name, + [switch]$Require) + +$originalErrorActionPreference = $ErrorActionPreference +try { + $ErrorActionPreference = 'Stop' + + # Get the URL. + $description = Get-LocString -Key PSLIB_EndpointUrl0 -ArgumentList $Name + $key = "ENDPOINT_URL_$Name" + $url = Get-VaultValue -Description $description -Key $key -Require:$Require + + # Get the auth object. + $description = Get-LocString -Key PSLIB_EndpointAuth0 -ArgumentList $Name + $key = "ENDPOINT_AUTH_$Name" + if ($auth = (Get-VaultValue -Description $description -Key $key -Require:$Require)) { + $auth = ConvertFrom-Json -InputObject $auth + } + + # Get the data. + $description = "'$Name' service endpoint data" + $key = "ENDPOINT_DATA_$Name" + if ($data = (Get-VaultValue -Description $description -Key $key)) { + $data = ConvertFrom-Json -InputObject $data + } + + # Return the endpoint. + if ($url -or $auth -or $data) { + New-Object -TypeName psobject -Property @{ + Url = $url + Auth = $auth + Data = $data + } + } +} catch { + $ErrorActionPreference = $originalErrorActionPreference + Write-Error $_ +} diff --git a/_generated/PowerShellV2/powershell.ps1 b/_generated/PowerShellV2/powershell.ps1 index a2a02836340..390096730a1 100644 --- a/_generated/PowerShellV2/powershell.ps1 +++ b/_generated/PowerShellV2/powershell.ps1 @@ -1,19 +1,78 @@ [CmdletBinding()] param() - Import-Module $PSScriptRoot\ps_modules\VstsTaskSdk -Import-Module $PSScriptRoot\ps_modules\VstsAzureRestHelpers_ Import-Module $PSScriptRoot\ps_modules\Sanitizer Import-Module Microsoft.PowerShell.Security -Add-Type -AssemblyName "System.Threading" -Add-Type -AssemblyName "System" - . $PSScriptRoot\helpers.ps1 $signalFromUserScript = "Global\SignalFromUserScript" + [System.Guid]::NewGuid().ToString() $signalFromTask = "Global\SignalFromTask" + [System.Guid]::NewGuid().ToString() $exitSignal = "Global\ExitSignal" + [System.Guid]::NewGuid().ToString() +$env:praval = "" + +$defaultEnvironmentMSALAuthUri = "https://login.microsoftonline.com/" +$defaultEnvironmentADALAuthUri = "https://login.windows.net/" +$azureStack = "AzureStack" + +function Get-EnvironmentAuthUrl { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] $endpoint, + [Parameter(Mandatory = $false)] $useMSAL = $false + ) + + $envAuthUrl = if ($useMSAL) { $endpoint.Data.activeDirectoryAuthority } else { $endpoint.Data.environmentAuthorityUrl } + + if ([string]::IsNullOrEmpty($envAuthUrl)) { + if (($endpoint.Data.Environment) -and ($endpoint.Data.Environment -eq $azureStack)) { + Write-Verbose "MSAL - Get-EnvironmentAuthUrl - azureStack is used" + $endpoint = Add-AzureStackDependencyData -Endpoint $endpoint + $envAuthUrl = $endpoint.Data.environmentAuthorityUrl + } + else { + Write-Verbose "MSAL - Get-EnvironmentAuthUrl - fallback is used" + # fallback + $envAuthUrl = if ($useMSAL) { $defaultEnvironmentMSALAuthUri } else { $defaultEnvironmentADALAuthUri } + } + } + + Write-Verbose "MSAL - Get-EnvironmentAuthUrl - endpoint=$endpoint" + Write-Verbose "MSAL - Get-EnvironmentAuthUrl - useMSAL=$useMSAL" + Write-Verbose "MSAL - Get-EnvironmentAuthUrl - envAuthUrl=$envAuthUrl" + + return $envAuthUrl +} + +function Get-TaskDictionary { + $dictionary = @{} + + $connectedServiceName = (Get-VstsInput -Name ConnectedServiceName) + $dictionary["ConnectedServiceName"] = $connectedServiceName + Write-Verbose "connectedServiceName : $connectedServiceName" + + if($null -eq $connectedServiceName -or $connectedServiceName -eq [string]::Empty) { + return $dictionary + } + + $endpoint = Get-VstsEndpoint -Name $connectedServiceName -Require + $dictionary["ClientId"] = $endpoint.Auth.Parameters.ServicePrincipalId + $dictionary["TenantId"] = $endpoint.Auth.Parameters.TenantId + $dictionary["EnvAuthUrl"] = Get-EnvironmentAuthUrl -endpoint $endpoint -useMSAL $true + + $vstsEndpoint = Get-VstsEndpoint -Name SystemVssConnection -Require + $dictionary["VstsAccessToken"] = $vstsEndpoint.auth.parameters.AccessToken + + return $dictionary +} + +function Get-EnvDictionary { + $envVars = @{} + [System.Environment]::GetEnvironmentVariables().GetEnumerator() | ForEach-Object { + $envVars[$_.Key] = $_.Value + } + return $envVars +} function Get-ActionPreference { param ( @@ -48,24 +107,40 @@ try { Assert-VstsPath -LiteralPath $tempDirectory -PathType 'Container' $tokenfilePath = [System.IO.Path]::Combine($tempDirectory, "$([System.Guid]::NewGuid()).txt") + # Create a runspace to handle the Async communication between the Task and User Script for Access Token + $runspacePool = [runspacefactory]::CreateRunspacePool(1, 1) + $runspacePool.Open() + $accessTokenHelperFilePath = "$PSScriptRoot\AccessTokenHelper.ps1" - . $accessTokenHelperFilePath + $taskDict = Get-TaskDictionary + $envVars = Get-EnvDictionary - try { - $ts = New-Object System.Threading.ParameterizedThreadStart([TokenHandler]::new(), [TokenHandler]::new().GetType().GetMethod("handle").MethodHandle.GetFunctionPointer()); - $thread = [System.Threading.Thread]::new($ts); - $arg = "$tokenfilePath::$signalFromUserScript::$signalFromTask::$exitSignal" - $thread.Start($arg); - } - catch { - Write-Host "Something failed ; $_" - } - + $psRunspace = [powershell]::Create().AddScript({ + param($accessTokenHelperFilePath,$filePath, $signalFromUserScript, $signalFromTask, $exitSignal, $taskDict, $envVars) + try { + $envVars.GetEnumerator() | ForEach-Object { + [System.Environment]::SetEnvironmentVariable($_.Key, $_.Value, [System.EnvironmentVariableTarget]::Process) + } + . $accessTokenHelperFilePath + $tokenHandler.TokenHandler.Invoke($filePath, $signalFromUserScript, $signalFromTask, $exitSignal, $taskDict) + return $env:praval + } catch { + $env:praval = $env:praval + " " + $_ + return $env:praval + } + }).AddArgument($accessTokenHelperFilePath).AddArgument($tokenfilePath).AddArgument($signalFromUserScript).AddArgument($signalFromTask).AddArgument($exitSignal).AddArgument($taskDict).AddArgument($envVars) + + $psRunspace.RunspacePool = $runspacePool + $rsoutput = $psRunspace.BeginInvoke() + + while ($env:StartTask -ne "true") { + Start-Sleep 5 + Write-Host "Waiting....." + } # Wait for the async runspace to start and get ready to listen to User scripts requests - Start-Sleep 20 + Start-Sleep 5 - # Get inputs. $input_errorActionPreference = Get-ActionPreference -VstsInputName 'errorActionPreference' -DefaultAction 'Stop' $input_warningPreference = Get-ActionPreference -VstsInputName 'warningPreference' -DefaultAction 'Default' @@ -334,6 +409,8 @@ finally { # Signal Task to exit $eventExit = New-Object System.Threading.EventWaitHandle($false, [System.Threading.EventResetMode]::AutoReset, $exitSignal) $output = $eventExit.Set() + Write-Host $psRunspace.EndInvoke($rsoutput) + Write-Host $env:praval Trace-VstsLeavingInvocation $MyInvocation } catch { Write-Host "Full Exception Object: $_" diff --git a/_generated/PowerShellV2/task.json b/_generated/PowerShellV2/task.json index a1900873ef1..64e06926074 100644 --- a/_generated/PowerShellV2/task.json +++ b/_generated/PowerShellV2/task.json @@ -18,7 +18,7 @@ "version": { "Major": 2, "Minor": 250, - "Patch": 124 + "Patch": 168 }, "releaseNotes": "Script task consistency. Added support for macOS and Linux.", "minimumAgentVersion": "2.115.0", @@ -269,8 +269,8 @@ "ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" }, "_buildConfigMapping": { - "Default": "2.250.124", + "Default": "2.250.168", "LocalPackages": "2.249.4", - "Node20-225": "2.250.125" + "Node20-225": "2.250.169" } } \ No newline at end of file diff --git a/_generated/PowerShellV2/task.loc.json b/_generated/PowerShellV2/task.loc.json index 74c9c18eaef..412be52a217 100644 --- a/_generated/PowerShellV2/task.loc.json +++ b/_generated/PowerShellV2/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 2, "Minor": 250, - "Patch": 124 + "Patch": 168 }, "releaseNotes": "ms-resource:loc.releaseNotes", "minimumAgentVersion": "2.115.0", @@ -269,8 +269,8 @@ "ScriptArgsSanitized": "ms-resource:loc.messages.ScriptArgsSanitized" }, "_buildConfigMapping": { - "Default": "2.250.124", + "Default": "2.250.168", "LocalPackages": "2.249.4", - "Node20-225": "2.250.125" + "Node20-225": "2.250.169" } } \ No newline at end of file diff --git a/_generated/PowerShellV2_Node20/AccessTokenHelper.ps1 b/_generated/PowerShellV2_Node20/AccessTokenHelper.ps1 index 168b64059c9..5c0527994c2 100644 --- a/_generated/PowerShellV2_Node20/AccessTokenHelper.ps1 +++ b/_generated/PowerShellV2_Node20/AccessTokenHelper.ps1 @@ -1,4 +1,7 @@ -function Global:Get-VstsFederatedTokenPSV2Task { +Import-Module Microsoft.PowerShell.Security +Import-Module $PSScriptRoot\ps_modules\VstsTaskSdk + +function Global:Get-VstsFederatedTokenPS2Task { param( [Parameter(Mandatory=$true)] [string]$serviceConnectionId, @@ -13,7 +16,8 @@ function Global:Get-VstsFederatedTokenPSV2Task { $projectId = Get-VstsTaskVariable -Name 'System.TeamProjectId' -Require $url = $uri + "$projectId/_apis/distributedtask/hubs/$hub/plans/$planId/jobs/$jobId/oidctoken?serviceConnectionId=$serviceConnectionId&api-version=7.1-preview.1" - + $env:praval = $env:praval + "`n" + $url + $headers = @{ "Authorization" = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($vstsAccessToken)")) "Content-Type" = "application/json" @@ -26,42 +30,55 @@ function Global:Get-VstsFederatedTokenPSV2Task { $responseContent = $response.Content | ConvertFrom-Json $oidcToken = $responseContent.oidcToken - if ($null -eq $oidcToken -or $oidcToken -eq [string]::Empty) { - Write-Verbose "Failed to create OIDC token." + $env:praval = $env:praval + "`n" + "Failed to create OIDC token." throw (New-Object System.Exception("CouldNotGenerateOidcToken")) } - Write-Verbose "OIDC Token generated Successfully" + $env:praval = $env:praval + "`n" + "OIDC Token generated Successfully" return $oidcToken } -New-Alias -Name 'Get-VstsFederatedToken' -Value 'Global:Get-VstsFederatedTokenPSV2Task' -Scope Global +New-Alias -Name 'Get-VstsFederatedToken' -Value 'Global:Get-VstsFederatedTokenPS2Task' -Scope Global function Global:Get-WiscAccessTokenPSV2Task { param( - [Parameter(Mandatory=$true)] - [string]$connectedServiceName - ) - $vstsEndpoint = Get-VstsEndpoint -Name $connectedServiceName -Require + $taskDict + ) - $result = Get-AccessTokenMSALWithCustomScope -endpoint $vstsEndpoint ` - -connectedServiceNameARM $connectedServiceName ` - -scope "499b84ac-1321-427f-aa17-267ca6975798" + $clientId = $taskDict["ClientId"] + $envAuthUrl = $taskDict["EnvAuthUrl"] + $tenantId = $taskDict["TenantId"] + $connectedServiceName = $taskDict["ConnectedServiceName"] + $vstsAccessToken = $taskDict["VstsAccessToken"] + + Add-Type -Path "$PSScriptRoot\ps_modules\VstsAzureRestHelpers_\msal\Microsoft.Identity.Client.dll" + $clientBuilder = [Microsoft.Identity.Client.ConfidentialClientApplicationBuilder]::Create($clientId).WithAuthority($envAuthUrl, $tenantId) + + $oidc_token = Get-VstsFederatedToken -serviceConnectionId $connectedServiceName -vstsAccessToken $vstsAccessToken + $env:praval = $env:praval + "`n" + "oidc_token $oidc_token" + $msalClientInstance = $clientBuilder.WithClientAssertion($oidc_token).Build() + + $scope = "499b84ac-1321-427f-aa17-267ca6975798" + [string] $resourceId = $scope + "/.default" + $scopes = [Collections.Generic.List[string]]@($resourceId) + + $env:praval = $env:praval + "`n" + "Fetching Access Token - MSAL" + $tokenResult = $msalClientInstance.AcquireTokenForClient($scopes).ExecuteAsync().GetAwaiter().GetResult() $token = $null $expirationTime = $null - if($result) { - $token = $result.AccessToken - $expirationTime = $([math]::Round(([DateTime]::Parse($result.ExpiresOn) - [DateTime]::Now).TotalMinutes)) + if($tokenResult) { + $token = $tokenResult.AccessToken + $expirationTime = $([math]::Round(([DateTime]::Parse($tokenResult.ExpiresOn) - [DateTime]::Now).TotalMinutes)) } if ($null -eq $token -or $token -eq [string]::Empty) { - Write-Verbose "Generated token found to be null, returning the System Access Token" + $env:praval = $env:praval + "`n" + "Generated token found to be null, returning the System Access Token" $token = $env:SystemAccessTokenPowershellV2 } else { - Write-Verbose "Successfully generated the Azure Access token for Service Connection : $connectedServiceName" + $env:praval = $env:praval + "`n" + "Successfully generated the Azure Access token for Service Connection : $connectedServiceName" if($expirationTime) { - Write-Host "Generated access token with expiration time of $expirationTime minutes." + $env:praval = $env:praval + "`n" + "Generated access token with expiration time of $expirationTime minutes." } } @@ -69,15 +86,20 @@ function Global:Get-WiscAccessTokenPSV2Task { } New-Alias -Name 'Get-WiscAccessTokenPSV2Task' -Value 'Global:Get-WiscAccessTokenPSV2Task' -Scope Global -class TokenHandler { - handle($s) - { - $array = $s -split '::' - Write-Host $array - [string]$filePath = $array[0] - [string]$signalFromUserScript = $array[1] - [string]$signalFromTask = $array[2] - [string]$exitSignal = $array[3] +$tokenHandler = [PSCustomObject]@{ + + TokenHandler = { + param( + [Parameter(Mandatory=$true)] + [string]$filePath, + [Parameter(Mandatory=$true)] + [string]$signalFromUserScript, + [Parameter(Mandatory=$true)] + [string]$signalFromTask, + [Parameter(Mandatory=$true)] + [string]$exitSignal, + $taskDict + ) $eventFromUserScript = $null $eventFromTask = $null @@ -104,13 +126,14 @@ class TokenHandler { # Apply the ACL to the file Set-Acl -Path $filePath -AclObject $acl } else { - $env:praval = $env:praval + "`n" + "Token File not found" + $env:praval = $env:praval + "`n" + "Token File not found" throw "Token File not found" } - $env:praval = $env:praval + "`n" + "Task: Waiting for signals..." - Write-Host "Task: Waiting for signals..." - + Write-Host "1-Task: Waiting for signals..." + Write-Verbose "2-Task: Waiting for signals..." + $env:praval = $env:praval + "`n" + "Task: Waiting for signals..." + $env:StartTask = "true" # Infinite loop to wait for signals and respond while ($true) { try { @@ -120,53 +143,41 @@ class TokenHandler { if ($index -eq 0) { # Signal from UserScript - $env:SystemAccessTokenPowershellV2 = Get-VstsTaskVariable -Name 'System.AccessToken' -Require - Write-Host $env:SystemAccessTokenPowershellV2 - $token = "" try { - [string]$connectedServiceName = (Get-VstsInput -Name ConnectedServiceName) - $env:praval = $env:praval + "`n" + "ConnectedServiceName $connectedServiceName" - Write-Host "ConnectedServiceName $connectedServiceName" + [string]$connectedServiceName = $taskDict["ConnectedServiceName"] if ($null -eq $connectedServiceName -or $connectedServiceName -eq [string]::Empty) { - $env:praval = $env:praval + "`n" + "No Service connection was found, returning the System Access Token" + $env:praval = $env:praval + "`n" + "No Service connection was found, returning the System Access Token" $token = $env:SystemAccessTokenPowershellV2 - Write-Host "No Service connection was found, returning the System Access Token" } else { - $token = Get-WiscAccessTokenPSV2Task -connectedServiceName $connectedServiceName - $env:praval = $env:praval + "`n" + "Successfully generated the Azure Access token for Service Connection : $connectedServiceName" - Write-Host "Successfully generated the Azure Access token for Service Connection : $connectedServiceName" + $token = Get-WiscAccessTokenPSV2Task -connectedServiceName $connectedServiceName -taskDict $taskDict + $env:praval = $env:praval + "`n" + "Successfully generated the Azure Access token for Service Connection : $connectedServiceName" } } catch { - $env:praval = $env:praval + "`n" + "Failed to generate token with message $_, returning the System Access Token" - Write-Host "Failed to generate token with message $_, returning the System Access Token" + $env:praval = $env:praval + "`n" + "Failed to generate token with message $_, returning the System Access Token" $token = $env:SystemAccessTokenPowershellV2 } finally { $token | Set-Content -Path $filePath - $env:praval = $env:praval + "`n" + "Task: Wrote access token to file" - Write-Host "Task: Wrote access token to file" + $env:praval = $env:praval + "`n" + "Task: Wrote access token to file" } # Signal UserScript to read the file $tmp = $eventFromTask.Set() } elseif ($index -eq 1) { - $env:praval = $env:praval + "`n" + "Exiting the loop" - Write-Host "Exiting the loop" + $env:praval = $env:praval + "`n" + "Exiting the loop" # Exit signal received break } } catch { - $env:praval = $env:praval + "`n" + "Error occurred while waiting for signals: $_" - Write-Host "Error occurred while waiting for signals: $_" + $env:praval = $env:praval + "`n" + "Error occurred while waiting for signals: $_" } } } catch { $env:praval = $env:praval + "`n" + "Critical error in Task: $_" - Write-Host "Critical error in Task: $_" } finally { try { if ($null -ne $eventFromUserScript ) { $eventFromUserScript.Dispose() } diff --git a/_generated/PowerShellV2_Node20/Vsts.ps1 b/_generated/PowerShellV2_Node20/Vsts.ps1 new file mode 100644 index 00000000000..879d406bb73 --- /dev/null +++ b/_generated/PowerShellV2_Node20/Vsts.ps1 @@ -0,0 +1,41 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string]$Name, + [switch]$Require) + +$originalErrorActionPreference = $ErrorActionPreference +try { + $ErrorActionPreference = 'Stop' + + # Get the URL. + $description = Get-LocString -Key PSLIB_EndpointUrl0 -ArgumentList $Name + $key = "ENDPOINT_URL_$Name" + $url = Get-VaultValue -Description $description -Key $key -Require:$Require + + # Get the auth object. + $description = Get-LocString -Key PSLIB_EndpointAuth0 -ArgumentList $Name + $key = "ENDPOINT_AUTH_$Name" + if ($auth = (Get-VaultValue -Description $description -Key $key -Require:$Require)) { + $auth = ConvertFrom-Json -InputObject $auth + } + + # Get the data. + $description = "'$Name' service endpoint data" + $key = "ENDPOINT_DATA_$Name" + if ($data = (Get-VaultValue -Description $description -Key $key)) { + $data = ConvertFrom-Json -InputObject $data + } + + # Return the endpoint. + if ($url -or $auth -or $data) { + New-Object -TypeName psobject -Property @{ + Url = $url + Auth = $auth + Data = $data + } + } +} catch { + $ErrorActionPreference = $originalErrorActionPreference + Write-Error $_ +} diff --git a/_generated/PowerShellV2_Node20/powershell.ps1 b/_generated/PowerShellV2_Node20/powershell.ps1 index a2a02836340..390096730a1 100644 --- a/_generated/PowerShellV2_Node20/powershell.ps1 +++ b/_generated/PowerShellV2_Node20/powershell.ps1 @@ -1,19 +1,78 @@ [CmdletBinding()] param() - Import-Module $PSScriptRoot\ps_modules\VstsTaskSdk -Import-Module $PSScriptRoot\ps_modules\VstsAzureRestHelpers_ Import-Module $PSScriptRoot\ps_modules\Sanitizer Import-Module Microsoft.PowerShell.Security -Add-Type -AssemblyName "System.Threading" -Add-Type -AssemblyName "System" - . $PSScriptRoot\helpers.ps1 $signalFromUserScript = "Global\SignalFromUserScript" + [System.Guid]::NewGuid().ToString() $signalFromTask = "Global\SignalFromTask" + [System.Guid]::NewGuid().ToString() $exitSignal = "Global\ExitSignal" + [System.Guid]::NewGuid().ToString() +$env:praval = "" + +$defaultEnvironmentMSALAuthUri = "https://login.microsoftonline.com/" +$defaultEnvironmentADALAuthUri = "https://login.windows.net/" +$azureStack = "AzureStack" + +function Get-EnvironmentAuthUrl { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] $endpoint, + [Parameter(Mandatory = $false)] $useMSAL = $false + ) + + $envAuthUrl = if ($useMSAL) { $endpoint.Data.activeDirectoryAuthority } else { $endpoint.Data.environmentAuthorityUrl } + + if ([string]::IsNullOrEmpty($envAuthUrl)) { + if (($endpoint.Data.Environment) -and ($endpoint.Data.Environment -eq $azureStack)) { + Write-Verbose "MSAL - Get-EnvironmentAuthUrl - azureStack is used" + $endpoint = Add-AzureStackDependencyData -Endpoint $endpoint + $envAuthUrl = $endpoint.Data.environmentAuthorityUrl + } + else { + Write-Verbose "MSAL - Get-EnvironmentAuthUrl - fallback is used" + # fallback + $envAuthUrl = if ($useMSAL) { $defaultEnvironmentMSALAuthUri } else { $defaultEnvironmentADALAuthUri } + } + } + + Write-Verbose "MSAL - Get-EnvironmentAuthUrl - endpoint=$endpoint" + Write-Verbose "MSAL - Get-EnvironmentAuthUrl - useMSAL=$useMSAL" + Write-Verbose "MSAL - Get-EnvironmentAuthUrl - envAuthUrl=$envAuthUrl" + + return $envAuthUrl +} + +function Get-TaskDictionary { + $dictionary = @{} + + $connectedServiceName = (Get-VstsInput -Name ConnectedServiceName) + $dictionary["ConnectedServiceName"] = $connectedServiceName + Write-Verbose "connectedServiceName : $connectedServiceName" + + if($null -eq $connectedServiceName -or $connectedServiceName -eq [string]::Empty) { + return $dictionary + } + + $endpoint = Get-VstsEndpoint -Name $connectedServiceName -Require + $dictionary["ClientId"] = $endpoint.Auth.Parameters.ServicePrincipalId + $dictionary["TenantId"] = $endpoint.Auth.Parameters.TenantId + $dictionary["EnvAuthUrl"] = Get-EnvironmentAuthUrl -endpoint $endpoint -useMSAL $true + + $vstsEndpoint = Get-VstsEndpoint -Name SystemVssConnection -Require + $dictionary["VstsAccessToken"] = $vstsEndpoint.auth.parameters.AccessToken + + return $dictionary +} + +function Get-EnvDictionary { + $envVars = @{} + [System.Environment]::GetEnvironmentVariables().GetEnumerator() | ForEach-Object { + $envVars[$_.Key] = $_.Value + } + return $envVars +} function Get-ActionPreference { param ( @@ -48,24 +107,40 @@ try { Assert-VstsPath -LiteralPath $tempDirectory -PathType 'Container' $tokenfilePath = [System.IO.Path]::Combine($tempDirectory, "$([System.Guid]::NewGuid()).txt") + # Create a runspace to handle the Async communication between the Task and User Script for Access Token + $runspacePool = [runspacefactory]::CreateRunspacePool(1, 1) + $runspacePool.Open() + $accessTokenHelperFilePath = "$PSScriptRoot\AccessTokenHelper.ps1" - . $accessTokenHelperFilePath + $taskDict = Get-TaskDictionary + $envVars = Get-EnvDictionary - try { - $ts = New-Object System.Threading.ParameterizedThreadStart([TokenHandler]::new(), [TokenHandler]::new().GetType().GetMethod("handle").MethodHandle.GetFunctionPointer()); - $thread = [System.Threading.Thread]::new($ts); - $arg = "$tokenfilePath::$signalFromUserScript::$signalFromTask::$exitSignal" - $thread.Start($arg); - } - catch { - Write-Host "Something failed ; $_" - } - + $psRunspace = [powershell]::Create().AddScript({ + param($accessTokenHelperFilePath,$filePath, $signalFromUserScript, $signalFromTask, $exitSignal, $taskDict, $envVars) + try { + $envVars.GetEnumerator() | ForEach-Object { + [System.Environment]::SetEnvironmentVariable($_.Key, $_.Value, [System.EnvironmentVariableTarget]::Process) + } + . $accessTokenHelperFilePath + $tokenHandler.TokenHandler.Invoke($filePath, $signalFromUserScript, $signalFromTask, $exitSignal, $taskDict) + return $env:praval + } catch { + $env:praval = $env:praval + " " + $_ + return $env:praval + } + }).AddArgument($accessTokenHelperFilePath).AddArgument($tokenfilePath).AddArgument($signalFromUserScript).AddArgument($signalFromTask).AddArgument($exitSignal).AddArgument($taskDict).AddArgument($envVars) + + $psRunspace.RunspacePool = $runspacePool + $rsoutput = $psRunspace.BeginInvoke() + + while ($env:StartTask -ne "true") { + Start-Sleep 5 + Write-Host "Waiting....." + } # Wait for the async runspace to start and get ready to listen to User scripts requests - Start-Sleep 20 + Start-Sleep 5 - # Get inputs. $input_errorActionPreference = Get-ActionPreference -VstsInputName 'errorActionPreference' -DefaultAction 'Stop' $input_warningPreference = Get-ActionPreference -VstsInputName 'warningPreference' -DefaultAction 'Default' @@ -334,6 +409,8 @@ finally { # Signal Task to exit $eventExit = New-Object System.Threading.EventWaitHandle($false, [System.Threading.EventResetMode]::AutoReset, $exitSignal) $output = $eventExit.Set() + Write-Host $psRunspace.EndInvoke($rsoutput) + Write-Host $env:praval Trace-VstsLeavingInvocation $MyInvocation } catch { Write-Host "Full Exception Object: $_" diff --git a/_generated/PowerShellV2_Node20/task.json b/_generated/PowerShellV2_Node20/task.json index 654a0c32d4b..058e2b54ef6 100644 --- a/_generated/PowerShellV2_Node20/task.json +++ b/_generated/PowerShellV2_Node20/task.json @@ -18,7 +18,7 @@ "version": { "Major": 2, "Minor": 250, - "Patch": 125 + "Patch": 169 }, "releaseNotes": "Script task consistency. Added support for macOS and Linux.", "minimumAgentVersion": "2.115.0", @@ -273,8 +273,8 @@ "ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" }, "_buildConfigMapping": { - "Default": "2.250.124", + "Default": "2.250.168", "LocalPackages": "2.249.4", - "Node20-225": "2.250.125" + "Node20-225": "2.250.169" } } \ No newline at end of file diff --git a/_generated/PowerShellV2_Node20/task.loc.json b/_generated/PowerShellV2_Node20/task.loc.json index ae3c52d2672..f323bded07b 100644 --- a/_generated/PowerShellV2_Node20/task.loc.json +++ b/_generated/PowerShellV2_Node20/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 2, "Minor": 250, - "Patch": 125 + "Patch": 169 }, "releaseNotes": "ms-resource:loc.releaseNotes", "minimumAgentVersion": "2.115.0", @@ -273,8 +273,8 @@ "ScriptArgsSanitized": "ms-resource:loc.messages.ScriptArgsSanitized" }, "_buildConfigMapping": { - "Default": "2.250.124", + "Default": "2.250.168", "LocalPackages": "2.249.4", - "Node20-225": "2.250.125" + "Node20-225": "2.250.169" } } \ No newline at end of file