From 5fcdabc5a4790d9dc047373bb0b8ed791d4dc504 Mon Sep 17 00:00:00 2001 From: Roshan Kumar Date: Mon, 18 Jun 2018 15:39:20 +0530 Subject: [PATCH] Powershell on target machine version V1 and window machine file copy V1 in master --- .../ConfigureWinRM.ps1 | 182 +++++++++++++ .../PowerShellJob.ps1 | 108 ++++++++ .../PowerShellOnTargetMachines.ps1 | 209 +++++++++++++++ Tasks/PowerShellOnTargetMachinesV1/README.md | 37 +++ .../resources.resjson/de-de/resources.resjson | 32 +++ .../resources.resjson/en-US/resources.resjson | 32 +++ .../resources.resjson/es-es/resources.resjson | 32 +++ .../resources.resjson/fr-fr/resources.resjson | 32 +++ .../resources.resjson/it-IT/resources.resjson | 32 +++ .../resources.resjson/ja-jp/resources.resjson | 32 +++ .../resources.resjson/ko-KR/resources.resjson | 32 +++ .../resources.resjson/ru-RU/resources.resjson | 32 +++ .../resources.resjson/zh-CN/resources.resjson | 32 +++ .../resources.resjson/zh-TW/resources.resjson | 32 +++ .../PowerShellOnTargetMachinesV1/Tests/L0.ts | 95 +++++++ .../Tests/L0HttpAndNoSkipCA.ps1 | 28 ++ .../Tests/L0HttpProperty.ps1 | 27 ++ .../Tests/L0InvalidEnvFail.ps1 | 27 ++ .../Tests/L0NoHttp(s)AndskipCAProperty.ps1 | 30 +++ .../Tests/L0ParallelRunDuplicate.ps1 | 34 +++ .../Tests/L0ParallelRunFail.ps1 | 37 +++ .../Tests/L0SkipCAPropertyOnly.ps1 | 16 ++ .../Tests/L0SkipCAandHttpsPort.ps1 | 27 ++ .../Tests/L0SkipCAandNoHttpPort.ps1 | 28 ++ .../Tests/L0SkipCAandNoHttpsPort.ps1 | 28 ++ .../Tests/L0ValidParallelRun.ps1 | 35 +++ .../Tests/L0ValidSequentialRun.ps1 | 25 ++ .../Tests/L0ValidateEnvProperty.ps1 | 20 ++ .../Tests/L0ValidateEnvResources.ps1 | 54 ++++ .../Tests/MockModule.ps1 | 29 +++ .../Tests/MockVariable.ps1 | 159 ++++++++++++ .../PowerShellOnTargetMachinesV1/Utility.ps1 | 184 ++++++++++++++ Tasks/PowerShellOnTargetMachinesV1/icon.png | Bin 0 -> 783 bytes Tasks/PowerShellOnTargetMachinesV1/icon.svg | 106 ++++++++ Tasks/PowerShellOnTargetMachinesV1/make.json | 8 + Tasks/PowerShellOnTargetMachinesV1/task.json | 156 ++++++++++++ .../task.loc.json | 156 ++++++++++++ .../tsconfig.json | 9 + Tasks/WindowsMachineFileCopyV1/README.md | 13 + .../WindowsMachineFileCopyV1/RoboCopyJob.ps1 | 239 ++++++++++++++++++ .../resources.resjson/de-de/resources.resjson | 26 ++ .../resources.resjson/en-US/resources.resjson | 26 ++ .../resources.resjson/es-es/resources.resjson | 26 ++ .../resources.resjson/fr-fr/resources.resjson | 26 ++ .../resources.resjson/it-IT/resources.resjson | 26 ++ .../resources.resjson/ja-jp/resources.resjson | 26 ++ .../resources.resjson/ko-KR/resources.resjson | 26 ++ .../resources.resjson/ru-RU/resources.resjson | 26 ++ .../resources.resjson/zh-CN/resources.resjson | 26 ++ .../resources.resjson/zh-TW/resources.resjson | 26 ++ Tasks/WindowsMachineFileCopyV1/Tests/L0.ts | 66 +++++ .../Tests/L0GetResourceConnectionDetails.ps1 | 26 ++ .../Tests/L0GetResourcesProperties.ps1 | 18 ++ .../Tests/L0InvalidEnvironmentResource.ps1 | 29 +++ .../Tests/L0ParallelCopyFail.ps1 | 37 +++ .../Tests/L0SequentialCopyFail.ps1 | 21 ++ .../Tests/L0ShouldCopyOnLocalMachine.ps1 | 18 ++ .../Tests/L0ValidInputParallelCopy.ps1 | 36 +++ .../Tests/L0ValidInputSequentialCopy.ps1 | 14 + .../Tests/L0ValidateDestinationPath.ps1 | 17 ++ .../Tests/L0ValidateSourcePath.ps1 | 16 ++ .../Tests/MockHelper.ps1 | 13 + .../Tests/MockVariable.ps1 | 126 +++++++++ Tasks/WindowsMachineFileCopyV1/Utility.ps1 | 105 ++++++++ .../WindowsMachineFileCopy.ps1 | 122 +++++++++ .../WindowsMachineFileCopyJob.ps1 | 38 +++ Tasks/WindowsMachineFileCopyV1/icon.png | Bin 0 -> 620 bytes Tasks/WindowsMachineFileCopyV1/icon.svg | 136 ++++++++++ Tasks/WindowsMachineFileCopyV1/task.json | 124 +++++++++ Tasks/WindowsMachineFileCopyV1/task.loc.json | 124 +++++++++ Tasks/WindowsMachineFileCopyV1/tsconfig.json | 9 + 71 files changed, 3781 insertions(+) create mode 100644 Tasks/PowerShellOnTargetMachinesV1/ConfigureWinRM.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/PowerShellJob.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/PowerShellOnTargetMachines.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/README.md create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/de-de/resources.resjson create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/en-US/resources.resjson create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/es-es/resources.resjson create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/fr-fr/resources.resjson create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/it-IT/resources.resjson create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/ja-jp/resources.resjson create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/ko-KR/resources.resjson create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/ru-RU/resources.resjson create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/zh-CN/resources.resjson create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/zh-TW/resources.resjson create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/L0.ts create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/L0HttpAndNoSkipCA.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/L0HttpProperty.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/L0InvalidEnvFail.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/L0NoHttp(s)AndskipCAProperty.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/L0ParallelRunDuplicate.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/L0ParallelRunFail.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/L0SkipCAPropertyOnly.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/L0SkipCAandHttpsPort.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/L0SkipCAandNoHttpPort.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/L0SkipCAandNoHttpsPort.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/L0ValidParallelRun.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/L0ValidSequentialRun.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/L0ValidateEnvProperty.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/L0ValidateEnvResources.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/MockModule.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Tests/MockVariable.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/Utility.ps1 create mode 100644 Tasks/PowerShellOnTargetMachinesV1/icon.png create mode 100644 Tasks/PowerShellOnTargetMachinesV1/icon.svg create mode 100644 Tasks/PowerShellOnTargetMachinesV1/make.json create mode 100644 Tasks/PowerShellOnTargetMachinesV1/task.json create mode 100644 Tasks/PowerShellOnTargetMachinesV1/task.loc.json create mode 100644 Tasks/PowerShellOnTargetMachinesV1/tsconfig.json create mode 100644 Tasks/WindowsMachineFileCopyV1/README.md create mode 100644 Tasks/WindowsMachineFileCopyV1/RoboCopyJob.ps1 create mode 100644 Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/de-de/resources.resjson create mode 100644 Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/en-US/resources.resjson create mode 100644 Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/es-es/resources.resjson create mode 100644 Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/fr-fr/resources.resjson create mode 100644 Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/it-IT/resources.resjson create mode 100644 Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/ja-jp/resources.resjson create mode 100644 Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/ko-KR/resources.resjson create mode 100644 Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/ru-RU/resources.resjson create mode 100644 Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/zh-CN/resources.resjson create mode 100644 Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/zh-TW/resources.resjson create mode 100644 Tasks/WindowsMachineFileCopyV1/Tests/L0.ts create mode 100644 Tasks/WindowsMachineFileCopyV1/Tests/L0GetResourceConnectionDetails.ps1 create mode 100644 Tasks/WindowsMachineFileCopyV1/Tests/L0GetResourcesProperties.ps1 create mode 100644 Tasks/WindowsMachineFileCopyV1/Tests/L0InvalidEnvironmentResource.ps1 create mode 100644 Tasks/WindowsMachineFileCopyV1/Tests/L0ParallelCopyFail.ps1 create mode 100644 Tasks/WindowsMachineFileCopyV1/Tests/L0SequentialCopyFail.ps1 create mode 100644 Tasks/WindowsMachineFileCopyV1/Tests/L0ShouldCopyOnLocalMachine.ps1 create mode 100644 Tasks/WindowsMachineFileCopyV1/Tests/L0ValidInputParallelCopy.ps1 create mode 100644 Tasks/WindowsMachineFileCopyV1/Tests/L0ValidInputSequentialCopy.ps1 create mode 100644 Tasks/WindowsMachineFileCopyV1/Tests/L0ValidateDestinationPath.ps1 create mode 100644 Tasks/WindowsMachineFileCopyV1/Tests/L0ValidateSourcePath.ps1 create mode 100644 Tasks/WindowsMachineFileCopyV1/Tests/MockHelper.ps1 create mode 100644 Tasks/WindowsMachineFileCopyV1/Tests/MockVariable.ps1 create mode 100644 Tasks/WindowsMachineFileCopyV1/Utility.ps1 create mode 100644 Tasks/WindowsMachineFileCopyV1/WindowsMachineFileCopy.ps1 create mode 100644 Tasks/WindowsMachineFileCopyV1/WindowsMachineFileCopyJob.ps1 create mode 100644 Tasks/WindowsMachineFileCopyV1/icon.png create mode 100644 Tasks/WindowsMachineFileCopyV1/icon.svg create mode 100644 Tasks/WindowsMachineFileCopyV1/task.json create mode 100644 Tasks/WindowsMachineFileCopyV1/task.loc.json create mode 100644 Tasks/WindowsMachineFileCopyV1/tsconfig.json diff --git a/Tasks/PowerShellOnTargetMachinesV1/ConfigureWinRM.ps1 b/Tasks/PowerShellOnTargetMachinesV1/ConfigureWinRM.ps1 new file mode 100644 index 000000000000..590499b0c802 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/ConfigureWinRM.ps1 @@ -0,0 +1,182 @@ +################################################################################################################################# +# Name : Configure-WinRM.ps1 # +# # +# Description : Configures the WinRM on a local machine # +# # +# Arguments : HostName, specifies the ipaddress or FQDN of machine # +################################################################################################################################# + +param +( + [string] $hostname, + [string] $protocol +) + +################################################################################################################################# +# Helper Functions # +################################################################################################################################# + +$ErrorActionPreference="Stop" +$winrmHttpPort=5985 +$winrmHttpsPort=5986 + +$helpMsg = "Usage: + To configure WinRM over Https: + ./ConfigureWinRM.ps1 + + To configure WinRM over Http: + ./ConfigureWinRM.ps1 http" + + +function Is-InputValid +{ + param([string] $hostname) + + $isInputValid = $true + + if(-not $hostname -or ($protocol -ne "http" -and $protocol -ne "https")) + { + $isInputValid = $false + } + + return $isInputValid +} + +function Download-Files +{ + Write-Verbose -Verbose "Downloading makecert.exe and winrmconf.cmd files" + + $source="https://azurergtaskstorage.blob.core.windows.net/winrm/makecert.exe" + Invoke-WebRequest $source -OutFile .\makecert.exe -ErrorAction Stop + + $source="https://azurergtaskstorage.blob.core.windows.net/winrm/winrmconf.cmd" + Invoke-WebRequest $source -OutFile .\winrmconf.cmd -ErrorAction Stop +} + +function Delete-WinRMListener +{ + $config = winrm enumerate winrm/config/listener + foreach($conf in $config) + { + if($conf.Contains("HTTPS")) + { + Write-Verbose -Verbose "HTTPS is already configured. Deleting the exisiting configuration." + + winrm delete winrm/config/Listener?Address=*+Transport=HTTPS + break + } + } +} + +function Configure-WinRMListener +{ + param([string] $hostname, + [string] $protocol) + + Write-Verbose -Verbose "Configuring the WinRM listener for $hostname over $protocol protocol" + + if($protocol -ne "http") + { + Configure-WinRMHttpsListener -hostname $hostname -port $winrmHttpsPort + } + else + { + Configure-WinRMHttpListener + } + + Write-Verbose -Verbose "Successfully Configured the WinRM listener for $hostname over $protocol protocol" +} + +function Configure-WinRMHttpListener +{ + winrm delete winrm/config/Listener?Address=*+Transport=HTTP + winrm create winrm/config/Listener?Address=*+Transport=HTTP +} + +function Configure-WinRMHttpsListener +{ + param([string] $hostname, + [string] $port) + + # Delete the WinRM Https listener if it is already configured + Delete-WinRMListener + + # Create a test certificate + $thumbprint = (Get-ChildItem cert:\LocalMachine\My | Where-Object { $_.Subject -eq "CN=" + $hostname } | Select-Object -Last 1).Thumbprint + if(-not $thumbprint) + { + .\makecert -r -pe -n CN=$hostname -b 01/01/2012 -e 01/01/2022 -eku 1.3.6.1.5.5.7.3.1 -ss my -sr localmachine -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 + $thumbprint=(Get-ChildItem cert:\Localmachine\my | Where-Object { $_.Subject -eq "CN=" + $hostname } | Select-Object -Last 1).Thumbprint + + if(-not $thumbprint) + { + throw "Failed to create the test certificate." + } + } + + # Configure WinRM + cmd.exe /c .\winrmconf.cmd $hostname $thumbprint +} + +function Add-FirewallException +{ + param([string] $protocol) + + if( $protocol -ne "http") + { + $port = $winrmHttpsPort + } + else + { + $port = $winrmHttpPort + } + + # Delete an exisitng rule + Write-Verbose -Verbose "Deleting the existing firewall exception for port $port" + netsh advfirewall firewall delete rule name="Windows Remote Management (HTTPS-In)" dir=in protocol=TCP localport=$port | Out-Null + + # Add a new firewall rule + Write-Verbose -Verbose "Adding the firewall exception for port $port" + netsh advfirewall firewall add rule name="Windows Remote Management (HTTPS-In)" dir=in action=allow protocol=TCP localport=$port | Out-Null +} + + +################################################################################################################################# +# Configure WinRM # +################################################################################################################################# + +netsh advfirewall firewall set rule group="File and Printer Sharing" new enable=yes +winrm quickconfig + +# The default MaxEnvelopeSizekb on Windows Server is 500 Kb which is very less. It needs to be at 8192 Kb. The small envelop size if not changed +# results in WS-Management service responding with error that the request size exceeded the configured MaxEnvelopeSize quota. +winrm set winrm/config '@{MaxEnvelopeSizekb = "8192"}' + +# Validate script arguments +if(-not (Is-InputValid -hostname $hostname)) +{ + Write-Warning "Invalid Argument exception:" + Write-Host $helpMsg + + return +} + +# Download files +Download-Files + +# Configure WinRM listener +Configure-WinRMListener -hostname $hostname -protocol $protocol + +# Add firewall exception +Add-FirewallException -protocol $protocol + +# List the listeners +Write-Verbose -Verbose "Listing the WinRM listeners:" +$config = winrm enumerate winrm/config/listener + +Write-Verbose -Verbose "Querying WinRM listeners by running command: winrm enumerate winrm/config/listener" +$config + +################################################################################################################################# +################################################################################################################################# + diff --git a/Tasks/PowerShellOnTargetMachinesV1/PowerShellJob.ps1 b/Tasks/PowerShellOnTargetMachinesV1/PowerShellJob.ps1 new file mode 100644 index 000000000000..36a03ac77a4d --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/PowerShellJob.ps1 @@ -0,0 +1,108 @@ +$RunPowershellJob = { +param ( + [string]$fqdn, + [string]$scriptPath, + [string]$port, + [string]$scriptArguments, + [string]$initializationScriptPath, + [object]$credential, + [string]$httpProtocolOption, + [string]$skipCACheckOption, + [string]$enableDetailedLogging, + [string]$sessionVariables + ) + + Write-Verbose "fqdn = $fqdn" + Write-Verbose "scriptPath = $scriptPath" + Write-Verbose "port = $port" + Write-Verbose "scriptArguments = $scriptArguments" + Write-Verbose "initializationScriptPath = $initializationScriptPath" + Write-Verbose "protocolOption = $httpProtocolOption" + Write-Verbose "skipCACheckOption = $skipCACheckOption" + Write-Verbose "enableDetailedLogging = $enableDetailedLogging" + + if(Test-Path "$env:AGENT_HOMEDIRECTORY\Agent\Worker") + { + Get-ChildItem $env:AGENT_HOMEDIRECTORY\Agent\Worker\*.dll | % { + [void][reflection.assembly]::LoadFrom( $_.FullName ) + Write-Verbose "Loading .NET assembly:`t$($_.name)" + } + + Get-ChildItem $env:AGENT_HOMEDIRECTORY\Agent\Worker\Modules\Microsoft.TeamFoundation.DistributedTask.Task.DevTestLabs\*.dll | % { + [void][reflection.assembly]::LoadFrom( $_.FullName ) + Write-Verbose "Loading .NET assembly:`t$($_.name)" + } + } + else + { + if(Test-Path "$env:AGENT_HOMEDIRECTORY\externals\vstshost") + { + [void][reflection.assembly]::LoadFrom("$env:AGENT_HOMEDIRECTORY\externals\vstshost\Microsoft.TeamFoundation.DistributedTask.Task.LegacySDK.dll") + } + } + + $enableDetailedLoggingOption = '' + if ($enableDetailedLogging -eq "true") + { + $enableDetailedLoggingOption = '-EnableDetailedLogging' + } + + $parsedSessionVariables = Get-ParsedSessionVariables -inputSessionVariables $sessionVariables + + Write-Verbose "Initiating deployment on $fqdn" + [String]$psOnRemoteScriptBlockString = "Invoke-PsOnRemote -MachineDnsName $fqdn -ScriptPath `$scriptPath -WinRMPort $port -Credential `$credential -ScriptArguments `$scriptArguments -InitializationScriptPath `$initializationScriptPath -SessionVariables `$parsedSessionVariables $skipCACheckOption $httpProtocolOption $enableDetailedLoggingOption" + [scriptblock]$psOnRemoteScriptBlock = [scriptblock]::Create($psOnRemoteScriptBlockString) + $deploymentResponse = Invoke-Command -ScriptBlock $psOnRemoteScriptBlock + + # Telemetry data logic through ps session + try{ + if($skipCACheckOption) + { + $sessionOption = New-PSSessionOption -SkipCACheck + } + else + { + $sessionOption = New-PSSessionOption + } + $secpasswd = ConvertTo-SecureString $credential.Password -AsPlainText -Force + $psCredential = New-Object System.Management.Automation.PSCredential($credential.UserName, $secpasswd) + if($httpProtocolOption -eq '-UseHttp') + { + $session = New-PSSession -Computer $fqdn -Port $port -Credential $psCredential -SessionOption ($sessionOption ) + } + else + { + $session = New-PSSession -Computer $fqdn -Port $port -Credential $psCredential -SessionOption ($sessionOption ) -UseSSL + } + $VmUuidHash = Invoke-Command -Session $session -ScriptBlock { + $sha = New-Object System.Security.Cryptography.SHA512CryptoServiceProvider + $computerDetails = Get-WmiObject -class Win32_ComputerSystemProduct -namespace root\CIMV2 + $encoding = [system.Text.Encoding]::ASCII + $uuidHash = [System.BitConverter]::ToString( $sha.ComputeHash($encoding.GetBytes($computerDetails.UUID))) + $uuidHash = $uuidHash -replace "-" , "" + return $uuidHash + } + $isAzureVm = Invoke-Command -Session $session -ScriptBlock { + (Get-Process -Name 'WindowsAzureGuestAgent' -ErrorAction Ignore) | Select-Object -First 1 | ForEach-Object { + if($_.Path) + { + return $true + } + else + { + return $false + } + } + } + $deploymentResponse | Add-Member "IsAzureVm" $IsAzureVm -Force + $deploymentResponse | Add-Member "VmUuidHash" $VmUuidHash -Force + } + catch + { + Write-Verbose "Error during fetching telemetry = $_" + $deploymentResponse | Add-Member "TelemetryError" $_ -Force + } + + + Write-Output $deploymentResponse +} diff --git a/Tasks/PowerShellOnTargetMachinesV1/PowerShellOnTargetMachines.ps1 b/Tasks/PowerShellOnTargetMachinesV1/PowerShellOnTargetMachines.ps1 new file mode 100644 index 000000000000..d407599b531d --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/PowerShellOnTargetMachines.ps1 @@ -0,0 +1,209 @@ +param ( + [string]$environmentName, + [string]$adminUserName, + [string]$adminPassword, + [string]$protocol, + [string]$testCertificate, + [string]$resourceFilteringMethod, + [string]$machineNames, + [string]$scriptPath, + [string]$scriptArguments, + [string]$initializationScriptPath, + [string]$runPowershellInParallel, + [string]$sessionVariables + ) + +Write-Verbose "Entering script PowerShellOnTargetMachines.ps1" +Write-Verbose "environmentName = $environmentName" +Write-Verbose "adminUserName = $adminUserName" +Write-Verbose "protocol = $protocol" +Write-Verbose "testCertificate = $testCertificate" +Write-Verbose "resourceFilteringMethod = $resourceFilteringMethod" +Write-Verbose "machineNames = $machineNames" +Write-Verbose "scriptPath = $scriptPath" +Write-Verbose "scriptArguments = $scriptArguments" +Write-Verbose "initializationScriptPath = $initializationScriptPath" +Write-Verbose "runPowershellInParallel = $runPowershellInParallel" +Write-Verbose "sessionVariables = $sessionVariables" + +. $PSScriptRoot/PowerShellJob.ps1 +. $PSScriptRoot/Utility.ps1 + +import-module "Microsoft.TeamFoundation.DistributedTask.Task.Internal" +import-module "Microsoft.TeamFoundation.DistributedTask.Task.Common" +import-module "Microsoft.TeamFoundation.DistributedTask.Task.DevTestLabs" +Import-Module "Microsoft.TeamFoundation.DistributedTask.Task.Deployment.Internal" + +# keep machineNames parameter name unchanged due to back compatibility +$machineFilter = $machineNames +$scriptPath = $scriptPath.Trim('"') +$initializationScriptPath = $initializationScriptPath.Trim('"') + +# Getting resource tag key name for corresponding tag +$resourceFQDNKeyName = Get-ResourceFQDNTagKey +$resourceWinRMHttpPortKeyName = Get-ResourceHttpTagKey +$resourceWinRMHttpsPortKeyName = Get-ResourceHttpsTagKey + +# Constants # +$useHttpProtocolOption = '-UseHttp' +$useHttpsProtocolOption = '' + +$doSkipCACheckOption = '-SkipCACheck' +$doNotSkipCACheckOption = '' +$ErrorActionPreference = 'Stop' +$deploymentOperation = 'Deployment' + +$envOperationStatus = "Passed" +$jobId = $env:SYSTEM_JOBID; + +# enabling detailed logging only when system.debug is true +$enableDetailedLoggingString = $env:system_debug +if ($enableDetailedLoggingString -ne "true") +{ + $enableDetailedLoggingString = "false" +} + +# Telemetry +Import-Module $PSScriptRoot\ps_modules\TelemetryHelper + +function Publish-AzureTelemetry + { + param([object] $deploymentResponse, + [string] $jobId ) + if($deploymentResponse){ + $jsonString = -join("{") + if([bool]($deploymentResponse.PSobject.Properties.name -match "IsAzureVm")){ + $jsonString = -join( $jsonString, + "`"IsAzureVm`" : `"$($deploymentResponse.IsAzureVm)`"" , + ",") + } + if([bool]($deploymentResponse.PSobject.Properties.name -match "VmUuidHash")){ + $jsonString = -join( $jsonString, + "`"VmUuidHash`" : `"$($deploymentResponse.VmUuidHash)`"", + ",") + } + if([bool]($deploymentResponse.PSobject.Properties.name -match "TelemetryError")){ + $jsonString = -join( $jsonString, + "`"TelemetryError`" : `"$($deploymentResponse.TelemetryError)`"", + ",") + } + + $jsonString = -join( $jsonString, + "`"JobId`" : `"$jobId`"" , "}") + } + + $telemetryString ="##vso[telemetry.publish area=TaskHub;feature=PowerShellOnTargetMachines]$jsonString" + Write-Host $telemetryString + } + +try +{ + $connection = Get-VssConnection -TaskContext $distributedTaskContext + + Write-Verbose "Starting Register-Environment cmdlet call for environment : $environmentName with filter $machineFilter" + $environment = Register-Environment -EnvironmentName $environmentName -EnvironmentSpecification $environmentName -UserName $adminUserName -Password $adminPassword -WinRmProtocol $protocol -TestCertificate ($testCertificate -eq "true") -Connection $connection -TaskContext $distributedTaskContext -ResourceFilter $machineFilter + Write-Verbose "Completed Register-Environment cmdlet call for environment : $environmentName" + + Write-Verbose "Starting Get-EnvironmentResources cmdlet call on environment name: $environmentName" + $resources = Get-EnvironmentResources -Environment $environment + + if ($resources.Count -eq 0) + { + Write-Telemetry "Input_Validation" "No machine exists for given environment" + throw (Get-LocalizedString -Key "No machine exists under environment: '{0}' for deployment" -ArgumentList $environmentName) + } + + $resourcesPropertyBag = Get-ResourcesProperties -resources $resources +} +catch +{ + Write-Telemetry "Task_InternalError" $_.exception.Message + + throw +} + +if($runPowershellInParallel -eq "false" -or ( $resources.Count -eq 1 ) ) +{ + foreach($resource in $resources) + { + $resourceProperties = $resourcesPropertyBag.Item($resource.Id) + $machine = $resourceProperties.fqdn + $displayName = $resourceProperties.displayName + Write-Output (Get-LocalizedString -Key "Deployment started for machine: '{0}'" -ArgumentList $displayName) + + $deploymentResponse = Invoke-Command -ScriptBlock $RunPowershellJob -ArgumentList $machine, $scriptPath, $resourceProperties.winrmPort, $scriptArguments, $initializationScriptPath, $resourceProperties.credential, $resourceProperties.protocolOption, $resourceProperties.skipCACheckOption, $enableDetailedLoggingString, $sessionVariables + Write-ResponseLogs -operationName $deploymentOperation -fqdn $displayName -deploymentResponse $deploymentResponse + $status = $deploymentResponse.Status + + Write-Output (Get-LocalizedString -Key "Deployment status for machine '{0}' : '{1}'" -ArgumentList $displayName, $status) + Publish-AzureTelemetry -deploymentResponse $deploymentResponse -jobId $jobId + + if ($status -ne "Passed") + { + Write-Telemetry "DTLSDK_Error" $deploymentResponse.DeploymentSummary + Write-Verbose $deploymentResponse.Error.ToString() + $errorMessage = $deploymentResponse.Error.Message + throw $errorMessage + } + } +} +else +{ + [hashtable]$Jobs = @{} + $dtlsdkErrors = @() + + foreach($resource in $resources) + { + $resourceProperties = $resourcesPropertyBag.Item($resource.Id) + $machine = $resourceProperties.fqdn + $displayName = $resourceProperties.displayName + Write-Output (Get-LocalizedString -Key "Deployment started for machine: '{0}'" -ArgumentList $displayName) + + $job = Start-Job -ScriptBlock $RunPowershellJob -ArgumentList $machine, $scriptPath, $resourceProperties.winrmPort, $scriptArguments, $initializationScriptPath, $resourceProperties.credential, $resourceProperties.protocolOption, $resourceProperties.skipCACheckOption, $enableDetailedLoggingString, $sessionVariables + $Jobs.Add($job.Id, $resourceProperties) + } + While ($Jobs.Count -gt 0) + { + Start-Sleep 10 + foreach($job in Get-Job) + { + if($Jobs.ContainsKey($job.Id) -and $job.State -ne "Running") + { + $output = Receive-Job -Id $job.Id + Remove-Job $Job + $status = $output.Status + $displayName = $Jobs.Item($job.Id).displayName + $resOperationId = $Jobs.Item($job.Id).resOperationId + + Write-ResponseLogs -operationName $deploymentOperation -fqdn $displayName -deploymentResponse $output + Write-Output (Get-LocalizedString -Key "Deployment status for machine '{0}' : '{1}'" -ArgumentList $displayName, $status) + Publish-AzureTelemetry -deploymentResponse $output -jobId $jobId + + if($status -ne "Passed") + { + $envOperationStatus = "Failed" + $errorMessage = "" + if($output.Error -ne $null) + { + $errorMessage = $output.Error.Message + } + Write-Output (Get-LocalizedString -Key "Deployment failed on machine '{0}' with following message : '{1}'" -ArgumentList $displayName, $errorMessage) + $dtlsdkErrors += $output.DeploymentSummary + } + $Jobs.Remove($job.Id) + } + } + } +} + +if($envOperationStatus -ne "Passed") +{ + foreach ($error in $dtlsdkErrors) { + Write-Telemetry "DTLSDK_Error" $error + } + + $errorMessage = (Get-LocalizedString -Key 'Deployment on one or more machines failed.') + throw $errorMessage +} + +Write-Verbose "Leaving script PowerShellOnTargetMachines.ps1" diff --git a/Tasks/PowerShellOnTargetMachinesV1/README.md b/Tasks/PowerShellOnTargetMachinesV1/README.md new file mode 100644 index 000000000000..1d85e36e989c --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/README.md @@ -0,0 +1,37 @@ +# PowerShell on Target Machines + +### Overview +The task is used run PowerShell on the target machines. The task can run both PowerShell scripts and PowerShell-DSC scripts. For PowerShell scripts, PowerShell 2.0 is needed on the machines and for PowerShell-DSC scripts [Windows Management Framework 4.0](https://www.microsoft.com/en-in/download/details.aspx?id=40855&40ddd5bd-f9e7-49a6-3526-f86656931a02=True) needs to be installed on the machines. WMF 4.0 ships in-the-box in Windows 8.1 and Windows Server 20012 R2. + +### WinRM setup +This task uses the [Windows Remote Management](https://msdn.microsoft.com/en-us/library/aa384426.aspx) (WinRM) to access domain-joined or workgroup, on-premises physical or virtual machines. + +#### Windows Remote Management (WinRM) Setup for On-premises Physical or Virtual Machines +To easily **setup WinRM** on the **host machines** follow the directions for [domain-joined machines](https://www.visualstudio.com/en-us/docs/release/examples/other-servers/net-to-vm) or the [workgroup machines](https://www.visualstudio.com/en-us/docs/release/examples/other-servers/net-to-workgroup-vm). + +#### Windows Remote Management (WinRM) Setup for Azure Virtual Machines +Azure virtual machines only work with the WinRM HTTPS protocol. With the WinRM protocol selected as HTTPS, you have an option to use the Test Certificate. Selecting the Test Certificate option means that the certificate is a self-signed certificate, and the automation agent will skip validating the authenticity of the machine's certificate from a trusted certification authority. + +- **Classic Virtual machines:** When creating [classic virtual machine](https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-windows-tutorial-classic-portal/) from the [new Azure portal](https://portal.azure.com/) or the [classic Azure portal](https://manage.windowsazure.com/), the virtual machine is already setup for WinRM HTTPS, with the default port 5986 already open in Firewall, and a self-signed certificate installed on the machine. These virtual machines can be directly added to the WinRM. The existing [classic virtual machine](https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-windows-tutorial-classic-portal/) can be also selected by using the [Azure Resource Group Deployment task](https://github.com/Microsoft/vso-agent-tasks/tree/master/Tasks/DeployAzureResourceGroup). + +- **Azure Resource Group:** If an [Azure resource group](https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-windows-hero-tutorial/) has been created in the [new Azure portal](https://portal.azure.com/), then it needs to be setup for the WinRM HTTPS protocol (WinRM HTTPS, with the default port 5986 already open in Firewall, and a self-signed certificate installed on the machine). + +To dynamically deploy Azure resource groups with virtual machines in them use the [Azure Resource Group Deployment task](https://github.com/Microsoft/vso-agent-tasks/tree/master/Tasks/DeployAzureResourceGroup). The task has a checkbox titled - **Enable Deployment Pre-requisites**. Select this option to setup the WinRM HTTPS protocol on the virtual machines, and to open the 5986 port in the Firewall, and to install the test certificate. After this the virtual machines are ready for use in the deployment task. + +### The different parameters of the task are explained below: + + * **Machines**: Specify comma separated list of machine FQDNs/ip addresses along with port(optional). For example dbserver.fabrikam.com, dbserver_int.fabrikam.com:5986,192.168.34:5986. Port when not specified will be defaulted to WinRM defaults based on the specified protocol. i.e., (For *WinRM 2.0*): The default HTTP port is 5985, and the default HTTPS port is 5986. + * **Admin Login**: Domain/Local administrator of the target host. Format: <Domain or hostname>\ < Admin User>. + * **Password**: Password for the admin login. It can accept variable defined in Build/Release definitions as '$(passwordVariable)'. You may mark variable type as 'secret' to secure it. + * **Protocol**: Specify the protocol that will be used to connect to target host, either HTTP or HTTPS. + * **Test Certificate**: Select the option to skip validating the authenticity of the machine's certificate by a trusted certification authority. The parameter is required for the WinRM HTTPS protocol. +* **PowerShell Script**: The location of the PowerShell script on the target machine like c:\FabrikamFibre\Web\deploy.ps1. Environment variables can be also used like $env:windir, $env:systemroot etc. +* **Script Arguments**: The arguments needed by the script, if any provided in the following format -applicationPath $(applicationPath) -username $(vmusername) -password $(vmpassword). +* **Initialization Script**: The location of the data script that is used by PowerShell-DSC and the location has to be on the target machine. It is advisable to use arguments in place of the initialization script. +* **Session Variables**: Used for setting-up the session variables for the PowerShell scripts and the input is a comma separated list like $varx=valuex, $vary=valuey. This is mostly used for backward compatibility with the earlier versions of Release Management product and it is advisable to use arguments in place of the session variables. +* **Advanced Options**: The advanced options provide more fine-grained control on the deployment. +* **Run PowerShell in Parallel**: Checking this option will execute the PowerShell in-parallel on all the target machines. + +### Known Issues : + +Write-Host command is not supported in PowerShell script. diff --git a/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/de-de/resources.resjson b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/de-de/resources.resjson new file mode 100644 index 000000000000..b5e5641f8ef7 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/de-de/resources.resjson @@ -0,0 +1,32 @@ +{ + "loc.friendlyName": "PowerShell auf Zielcomputern", + "loc.helpMarkDown": "[Weitere Informationen](https://go.microsoft.com/fwlink/?linkid=627414)", + "loc.description": "PowerShell-Skripte auf Remotecomputern ausführen", + "loc.instanceNameFormat": "PowerShell in $(EnvironmentName) ausführen", + "loc.group.displayName.deployment": "Bereitstellung", + "loc.group.displayName.advanced": "Erweiterte Optionen", + "loc.input.label.EnvironmentName": "Computer", + "loc.input.help.EnvironmentName": "Stellen Sie eine durch Kommas getrennte Liste der IP-Computeradressen oder FQDNs zusammen mit den Ports bereit. Die Standardeinstellung für den Port basiert auf dem ausgewählten Protokoll.
Beispiel: \"dbserver.fabrikam.com,dbserver_int.fabrikam.com:5986,192.168.12.34:5986\"
Sie können auch die Ausgabevariable anderer Tasks angeben. Beispiel: \"$(variableName)\".
Wenn Sie HTTPS verwenden, müssen Name und IP des Computers mit dem CN im Zertifikat übereinstimmen.", + "loc.input.label.AdminUserName": "Administratoranmeldung", + "loc.input.help.AdminUserName": "Die Administratoranmeldung für die Zielcomputer.", + "loc.input.label.AdminPassword": "Kennwort", + "loc.input.help.AdminPassword": "Das Administratorkennwort für die Zielcomputer.
Es kann die Variable annehmen, die in Build-/Releasedefinitionen als\"$(passwordVariable)\" definiert wird.
Sie können den Variablentyp als \"secret\" markieren, um die Variable zu sichern.", + "loc.input.label.Protocol": "Protokoll", + "loc.input.help.Protocol": "Wählen Sie das Protokoll aus, das für die WinRM-Verbindung mit dem Computer bzw. den Computern verwendet werden soll. Der Standardwert ist HTTPS.", + "loc.input.label.TestCertificate": "Testzertifikat", + "loc.input.help.TestCertificate": "Wählen Sie die Option aus, um die Überprüfung der Authentizität des Zertifikats des Computers durch eine vertrauenswürdige Zertifizierungsstelle zu überspringen. Der Parameter ist für das WinRM HTTPS-Protokoll erforderlich.", + "loc.input.label.ScriptPath": "PowerShell-Skript", + "loc.input.help.ScriptPath": "Speicherort des PowerShell-Skripts auf den Zielcomputern oder in einem UNC-Pfad wie C:\\BudgetIT\\Web\\Deploy\\Website.ps1", + "loc.input.label.ScriptArguments": "Skriptargumente", + "loc.input.help.ScriptArguments": "Argumente für das PowerShell-Skript. Entweder Ordnungszahlparameter oder benannte Parameter wie \"-testParam test\"", + "loc.input.label.InitializationScriptPath": "Initialisierungsskript", + "loc.input.help.InitializationScriptPath": "Speicherort des Datenskripts für DSC auf den Zielcomputern oder in einem UNC-Pfad wie C:\\BudgetIT\\Web\\Deploy\\WebsiteConfiguration.ps1", + "loc.input.label.SessionVariables": "Sitzungsvariablen", + "loc.input.help.SessionVariables": "Legen Sie allgemeine Sitzungsvariablen für beide Skripts im folgenden Format fest: $variable = Wert, $var1 = \"Wert, 123\"", + "loc.input.label.RunPowershellInParallel": "PowerShell parallel ausführen", + "loc.input.help.RunPowershellInParallel": "Wenn diese Option auf \"true\" festgelegt wird, werden PowerShell-Skripts parallel auf den Zielcomputern ausgeführt.", + "loc.input.label.ResourceFilteringMethod": "Computer auswählen nach", + "loc.input.help.ResourceFilteringMethod": "Wählen Sie optional eine Teilmenge der Computer durch Angeben von Computernamen oder Tags aus.", + "loc.input.label.MachineNames": "Filterkriterien", + "loc.input.help.MachineNames": "Diese Eingabe ist nur gültig für Computergruppen oder Ausgabevariablen und wird noch nicht für flache Listen von Computern unterstützt. Geben Sie eine Liste von Computern (z. B. \"dbserver.fabrikam.com\", \"webserver.fabrikam.com\", \"192.168.12.34\") oder Tags (z. B. \"Role:DB;OS:Win8.1\") an. Wenn mehrere Tags angegeben werden, wird der Task auf allen Computern mit den angegebenen Tags ausgeführt. Standardmäßig wird der Task auf allen Computern ausgeführt." +} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/en-US/resources.resjson b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/en-US/resources.resjson new file mode 100644 index 000000000000..64f129781fda --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/en-US/resources.resjson @@ -0,0 +1,32 @@ +{ + "loc.friendlyName": "PowerShell on Target Machines", + "loc.helpMarkDown": "[More Information](https://go.microsoft.com/fwlink/?linkid=627414)", + "loc.description": "Execute PowerShell scripts on remote machine(s)", + "loc.instanceNameFormat": "Run PowerShell on $(EnvironmentName)", + "loc.group.displayName.deployment": "Deployment", + "loc.group.displayName.advanced": "Advanced Options", + "loc.input.label.EnvironmentName": "Machines", + "loc.input.help.EnvironmentName": "Provide a comma separated list of machine IP addresses or FQDNs along with ports. Port is defaulted based on the selected protocol.
Eg: dbserver.fabrikam.com,dbserver_int.fabrikam.com:5986,192.168.12.34:5986
Or provide output variable of other tasks. Eg: $(variableName)
If you are using HTTPS, name/IP of machine should match the CN in the certificate.", + "loc.input.label.AdminUserName": "Admin Login", + "loc.input.help.AdminUserName": "Administrator login for the target machines.", + "loc.input.label.AdminPassword": "Password", + "loc.input.help.AdminPassword": "Administrator password for the target machines.
It can accept variable defined in Build/Release definitions as '$(passwordVariable)'.
You may mark variable type as 'secret' to secure it.", + "loc.input.label.Protocol": "Protocol", + "loc.input.help.Protocol": "Select the protocol to use for the WinRM connection with the machine(s). Default is HTTPS.", + "loc.input.label.TestCertificate": "Test Certificate", + "loc.input.help.TestCertificate": "Select the option to skip validating the authenticity of the machine's certificate by a trusted certification authority. The parameter is required for the WinRM HTTPS protocol.", + "loc.input.label.ScriptPath": "PowerShell Script", + "loc.input.help.ScriptPath": "Location of the PowerShell script on the target machines or on a UNC path like C:\\BudgetIT\\Web\\Deploy\\Website.ps1", + "loc.input.label.ScriptArguments": "Script Arguments", + "loc.input.help.ScriptArguments": "Arguments for the PowerShell script. Can be ordinal parameters or named parameters like -testParam test", + "loc.input.label.InitializationScriptPath": "Initialization Script", + "loc.input.help.InitializationScriptPath": "Location of the data script for DSC on the target machines or on a UNC path like C:\\BudgetIT\\Web\\Deploy\\WebsiteConfiguration.ps1", + "loc.input.label.SessionVariables": "Session Variables", + "loc.input.help.SessionVariables": "Set common session variables for both the scripts. For example, $variable = value, $var1 = \"value, 123\"", + "loc.input.label.RunPowershellInParallel": "Run PowerShell in Parallel", + "loc.input.help.RunPowershellInParallel": "Setting it to true will run the PowerShell scripts in parallel on the target machines.", + "loc.input.label.ResourceFilteringMethod": "Select Machines By", + "loc.input.help.ResourceFilteringMethod": "Optionally, select a subset of machines either by providing machine names or tags.", + "loc.input.label.MachineNames": "Filter Criteria", + "loc.input.help.MachineNames": "This input is valid only for machine groups or output variables and is not supported for flat list of machines yet. Provide a list of machines like, dbserver.fabrikam.com, webserver.fabrikam.com, 192.168.12.34, or tags like, Role:DB; OS:Win8.1. If multiple tags are provided, then the task will run in all the machines with the specified tags. The default is to run the task in all machines." +} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/es-es/resources.resjson b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/es-es/resources.resjson new file mode 100644 index 000000000000..5458570c30cd --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/es-es/resources.resjson @@ -0,0 +1,32 @@ +{ + "loc.friendlyName": "PowerShell en equipos de destino", + "loc.helpMarkDown": "[Más información](https://go.microsoft.com/fwlink/?linkid=627414)", + "loc.description": "Ejecutar scripts de PowerShell en equipo(s) remoto(s)", + "loc.instanceNameFormat": "Ejecutar PowerShell en $(EnvironmentName)", + "loc.group.displayName.deployment": "Implementación", + "loc.group.displayName.advanced": "Opciones avanzadas", + "loc.input.label.EnvironmentName": "Equipos", + "loc.input.help.EnvironmentName": "Proporcione una lista separada por comas de direcciones IP de equipos o nombres de dominio completos junto con puertos. El puerto se establece de manera predeterminada en función del protocolo seleccionado.
Ejemplo: dbserver.fabrikam.com,dbserver_int.fabrikam.com:5986,192.168.12.34:5986
O bien proporcione la variable de salida de otras tareas. Ejemplo: $(variableName)
Si usa HTTPS, el nombre o la dirección IP de la máquina deben coincidir con el CN del certificado.", + "loc.input.label.AdminUserName": "Inicio de sesión del administrador", + "loc.input.help.AdminUserName": "Inicio de sesión del administrador para los equipos de destino.", + "loc.input.label.AdminPassword": "Contraseña", + "loc.input.help.AdminPassword": "Contraseña del administrador para las máquinas de destino.
Admite la variable declarada en las definiciones de compilación o versión como \"$(passwordVariable)\".
Para proteger la variable, puede marcar el tipo como \"secret\".", + "loc.input.label.Protocol": "Protocolo", + "loc.input.help.Protocol": "Seleccione el protocolo que se usará para la conexión WinRM con los equipos. El valor predeterminado es HTTPS.", + "loc.input.label.TestCertificate": "Certificado de prueba", + "loc.input.help.TestCertificate": "Seleccione la opción de omitir la validación de la autenticidad del certificado del equipo por una autoridad de certificación de confianza. El parámetro es obligatorio para el protocolo HTTPS de WinRM.", + "loc.input.label.ScriptPath": "Script de PowerShell", + "loc.input.help.ScriptPath": "Ubicación del script de PowerShell en las máquinas de destino, o bien una ruta de acceso UNC como C:\\BudgetIT\\Web\\Deploy\\Website.ps1", + "loc.input.label.ScriptArguments": "Argumentos de script", + "loc.input.help.ScriptArguments": "Argumentos para el script de PowerShell. Pueden ser parámetros ordinales o parámetros con nombre como -testParam test", + "loc.input.label.InitializationScriptPath": "Script de inicialización", + "loc.input.help.InitializationScriptPath": "Ubicación del script de datos para DSC en los equipos de destino, o bien una ruta de acceso UNC como C:\\BudgetIT\\Web\\Deploy\\WebsiteConfiguration.ps1", + "loc.input.label.SessionVariables": "Variables de sesión", + "loc.input.help.SessionVariables": "Establecer variables de sesión comunes para los dos scripts. Por ejemplo, $variable = value, $var1 = \"value, 123\"", + "loc.input.label.RunPowershellInParallel": "Ejecutar PowerShell en paralelo", + "loc.input.help.RunPowershellInParallel": "Si se establece en true, se ejecutarán scripts de PowerShell en paralelo en los equipos de destino.", + "loc.input.label.ResourceFilteringMethod": "Seleccionar máquinas por", + "loc.input.help.ResourceFilteringMethod": "También puede seleccionar un subconjunto de máquinas especificando nombres de máquina o etiquetas.", + "loc.input.label.MachineNames": "Criterios de filtro", + "loc.input.help.MachineNames": "Esta entrada solo es válida para los grupos de máquinas o variables de salida, y no se admite aún para una lista plana de máquinas. Proporcione una lista de máquinas (como dbserver.fabrikam.com, webserver.fabrikam.com o 192.168.12.34) o etiquetas (como Role:DB; OS:Win8.1). Si se proporcionan varias etiquetas, la tarea se ejecutará en todas las máquinas que tengan las etiquetas especificadas. Para los grupos de recursos de Azure, proporcione el nombre de la máquina virtual, como ffweb o ffdb. La opción predeterminada es ejecutar la tarea en todas las máquinas." +} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/fr-fr/resources.resjson b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/fr-fr/resources.resjson new file mode 100644 index 000000000000..3a4a1d041733 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/fr-fr/resources.resjson @@ -0,0 +1,32 @@ +{ + "loc.friendlyName": "PowerShell sur des ordinateurs cibles", + "loc.helpMarkDown": "[Plus d'informations](https://go.microsoft.com/fwlink/?linkid=627414)", + "loc.description": "Exécuter des scripts PowerShell sur des ordinateurs distants", + "loc.instanceNameFormat": "Exécuter PowerShell sur $(EnvironmentName)", + "loc.group.displayName.deployment": "Déploiement", + "loc.group.displayName.advanced": "Options avancées", + "loc.input.label.EnvironmentName": "Ordinateurs", + "loc.input.help.EnvironmentName": "Indiquez une liste séparée par des virgules d'adresses IP ou de noms de domaine complets d'ordinateurs ainsi que les ports. Le port par défaut dépend du protocole sélectionné.
Exemple : dbserver.fabrikam.com,dbserver_int.fabrikam.com:5986,192.168.12.34:5986
Vous pouvez aussi indiquer une variable de sortie d'autres tâches. Exemple : $(variableName)
Si vous utilisez HTTPS, le nom ou l'adresse IP de la machine doit correspondre au CN dans le certificat.", + "loc.input.label.AdminUserName": "Informations de connexion d'administrateur", + "loc.input.help.AdminUserName": "Informations de connexion d'administrateur pour les ordinateurs cibles.", + "loc.input.label.AdminPassword": "Mot de passe", + "loc.input.help.AdminPassword": "Mot de passe d'administrateur pour les machines cibles.
Il peut accepter une variable définie dans les définitions Build/Release sous la forme '$(passwordVariable)'.
Vous pouvez marquer le type de variable en tant que 'secret' pour renforcer sa sécurité.", + "loc.input.label.Protocol": "Protocole", + "loc.input.help.Protocol": "Sélectionnez le protocole à utiliser pour la connexion WinRM avec les machines. La valeur par défaut est HTTPS.", + "loc.input.label.TestCertificate": "Certificat de test", + "loc.input.help.TestCertificate": "Sélectionnez l'option pour ignorer l'étape de validation de l'authenticité du certificat de l'ordinateur par une autorité de certification approuvée. Le paramètre est requis pour le protocole HTTPS WinRM.", + "loc.input.label.ScriptPath": "Script PowerShell", + "loc.input.help.ScriptPath": "Emplacement du script PowerShell sur les machines cibles ou dans un chemin d'accès UNC tel que C:\\BudgetIT\\Web\\Deploy\\Website.ps1", + "loc.input.label.ScriptArguments": "Arguments de script", + "loc.input.help.ScriptArguments": "Arguments pour le script PowerShell. Peuvent être des paramètres ordinaux ou nommés tels que -testParam test", + "loc.input.label.InitializationScriptPath": "Script d'initialisation", + "loc.input.help.InitializationScriptPath": "Emplacement du script de données pour DSC sur des machines cibles ou dans un chemin d'accès UNC tel que C:\\BudgetIT\\Web\\Deploy\\WebsiteConfiguration.ps1", + "loc.input.label.SessionVariables": "Variables de session", + "loc.input.help.SessionVariables": "Définissez des variables de session pour les deux scripts. Par exemple, $variable = value, $var1 = \"value, 123\"", + "loc.input.label.RunPowershellInParallel": "Exécuter PowerShell en parallèle", + "loc.input.help.RunPowershellInParallel": "Si cette valeur est true, les scripts PowerShell sont exécutés en parallèle sur les ordinateurs cibles.", + "loc.input.label.ResourceFilteringMethod": "Sélectionner les machines par", + "loc.input.help.ResourceFilteringMethod": "Vous pouvez également sélectionner un sous-ensemble de machines en spécifiant les noms des machines ou les balises associées.", + "loc.input.label.MachineNames": "Critères de filtre", + "loc.input.help.MachineNames": "Cette entrée est valide uniquement pour les groupes de machines ou les variables de sortie. Elle n'est pas encore prise en charge pour une liste plate de machines. Indiquez une liste de machines, par exemple dbserver.fabrikam.com, webserver.fabrikam.com, 192.168.12.34, ou utilisez des étiquettes telles que Role:DB; OS:Win8.1. Si plusieurs étiquettes sont indiquées, la tâche s'exécute sur toutes les machines correspondant aux étiquettes spécifiées. Par défaut, la tâche s'exécute sur toutes les machines." +} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/it-IT/resources.resjson b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/it-IT/resources.resjson new file mode 100644 index 000000000000..4264ecd4f0ac --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/it-IT/resources.resjson @@ -0,0 +1,32 @@ +{ + "loc.friendlyName": "PowerShell in computer di destinazione", + "loc.helpMarkDown": "[Altre informazioni](https://go.microsoft.com/fwlink/?linkid=627414)", + "loc.description": "Consente di eseguire script PowerShell nei computer remoti", + "loc.instanceNameFormat": "Esegui PowerShell in $(EnvironmentName)", + "loc.group.displayName.deployment": "Distribuzione", + "loc.group.displayName.advanced": "Opzioni avanzate", + "loc.input.label.EnvironmentName": "Computer", + "loc.input.help.EnvironmentName": "Consente di specificare un elenco di indirizzi IP o FQDN separati da virgola unitamente alle porte. Per impostazione predefinita, la porta è basata sul protocollo selezionato.
Esempio: dbserver.fabrikam.com,dbserver_int.fabrikam.com:5986,192.168.12.34:5986
In alternativa, consente di specificare la variabile di output di altre attività. Esempio: $(variableName)
Se si usa HTTPS, il nome e l'IP del computer devono essere uguali al nome comune (CN) del certificato.", + "loc.input.label.AdminUserName": "Account di accesso amministratore", + "loc.input.help.AdminUserName": "Account di accesso dell'amministratore per i computer di destinazione.", + "loc.input.label.AdminPassword": "Password", + "loc.input.help.AdminPassword": "Password dell'amministratore per i computer di destinazione.
Accetta la variabile definita nelle definizioni di compilazione/versione come '$(passwordVariable)'.
Per proteggerla, è possibile contrassegnare il tipo di variabile come 'secret'.", + "loc.input.label.Protocol": "Protocollo", + "loc.input.help.Protocol": "Consente di selezionare il protocollo da usare per la connessione WinRM con i computer. Il valore predefinito è HTTPS.", + "loc.input.label.TestCertificate": "Certificato di test", + "loc.input.help.TestCertificate": "Consente di selezionare l'opzione per ignorare la convalida dell'autenticità del certificato del computer da parte di un'Autorità di certificazione attendibile. Il parametro è obbligatorio per il protocollo HTTPS di WinRM.", + "loc.input.label.ScriptPath": "Script PowerShell", + "loc.input.help.ScriptPath": "Percorso dello script PowerShell nei computer di destinazione oppure in un percorso UNC come C:\\BudgetIT\\Web\\Distribuzione\\SitoWeb.ps1", + "loc.input.label.ScriptArguments": "Argomenti script", + "loc.input.help.ScriptArguments": "Argomenti per lo script PowerShell. Possono essere parametri ordinali o denominati come -testParam test", + "loc.input.label.InitializationScriptPath": "Script di inizializzazione", + "loc.input.help.InitializationScriptPath": "Percorso dello script di dati per DSC nei computer di destinazione oppure in un percorso UNC come C:\\BudgetIT\\Web\\Distribuzione\\ConfigurazioneSitoWeb.ps1", + "loc.input.label.SessionVariables": "Variabili di sessione", + "loc.input.help.SessionVariables": "Consente di impostare le variabili di sessione comuni per entrambi gli script. Ad esempio, $variable = value, $var1 = \"value, 123\"", + "loc.input.label.RunPowershellInParallel": "Esegui PowerShell in parallelo", + "loc.input.help.RunPowershellInParallel": "Se è impostato su true, gli script PowerShell verranno eseguiti in parallelo nei computer di destinazione.", + "loc.input.label.ResourceFilteringMethod": "Seleziona computer per", + "loc.input.help.ResourceFilteringMethod": "Selezionare, facoltativamente, un sottoinsieme di computer specificando nomi o tag dei computer.", + "loc.input.label.MachineNames": "Criteri di filtro", + "loc.input.help.MachineNames": "Questo input è valido solo per gruppi di computer o variabili di output e non è ancora supportato per l'elenco semplice di computer. Consente di specificare un elenco di computer come dbserver.fabrikam.com, webserver.fabrikam.com, 192.168.12.34 o tag come Role:DB; OS:Win8.1. Se vengono specificati più tag, l'attività verrà eseguita in tutti i computer con i tag specificati. Con l'impostazione predefinita l'attività viene eseguita in tutti i computer." +} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/ja-jp/resources.resjson b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/ja-jp/resources.resjson new file mode 100644 index 000000000000..e0ccd44d48bc --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/ja-jp/resources.resjson @@ -0,0 +1,32 @@ +{ + "loc.friendlyName": "ターゲット コンピューターでの PowerShell", + "loc.helpMarkDown": "[詳細](https://go.microsoft.com/fwlink/?linkid=627414)", + "loc.description": "リモート コンピューター (複数可) で PowerShell スクリプトを実行します", + "loc.instanceNameFormat": "$(EnvironmentName) での PowerShell の実行", + "loc.group.displayName.deployment": "配置", + "loc.group.displayName.advanced": "詳細設定のオプション", + "loc.input.label.EnvironmentName": "コンピューター", + "loc.input.help.EnvironmentName": "コンピューターの IP アドレスまたは FQDN とポートのコンマ区切り一覧を指定します。ポートは選んだプロトコルに基づいて既定値に設定されます。
例: dbserver.fabrikam.com,dbserver_int.fabrikam.com:5986,192.168.12.34:5986
または他のタスクの出力変数を指定します。例: $(variableName)
HTTPS を使用している場合、マシンの名前/IP は証明書の CN と一致する必要があります。", + "loc.input.label.AdminUserName": "管理者ログイン", + "loc.input.help.AdminUserName": "ターゲット コンピューターの管理者ログイン。", + "loc.input.label.AdminPassword": "パスワード", + "loc.input.help.AdminPassword": "対象のコンピューターの管理者パスワード。
ビルド/リリース定義で '$(passwordVariable)' として定義された変数を受け入れることができます。
変数タイプを 'シークレット' とマークして保護することもできます。", + "loc.input.label.Protocol": "プロトコル", + "loc.input.help.Protocol": "コンピューターとの WinRM 接続に使用するプロトコルを選びます。既定は HTTPS です。", + "loc.input.label.TestCertificate": "テスト証明書", + "loc.input.help.TestCertificate": "信頼された証明機関によるマシンの証明書の信頼性検証をスキップするオプションを選択します。このパラメーターは、WinRM の HTTPS プロトコルでは必須です。", + "loc.input.label.ScriptPath": "PowerShell スクリプト", + "loc.input.help.ScriptPath": "ターゲット コンピューター上、または UNC パス (C:\\BudgetIT\\Web\\Deploy\\Website.ps1 など) 上の、PowerShell スクリプトの場所", + "loc.input.label.ScriptArguments": "スクリプトの引数", + "loc.input.help.ScriptArguments": "PowerShell スクリプトの引数。順序によるパラメーターまたは \"-testParam test\" のような名前付きのパラメーターとすることができます。", + "loc.input.label.InitializationScriptPath": "初期化スクリプト", + "loc.input.help.InitializationScriptPath": "ターゲット コンピューター上、または UNC パス (C:\\BudgetIT\\Web\\Deploy\\WebsiteConfiguration.ps1 など) 上の、DSC のデータ スクリプトの場所", + "loc.input.label.SessionVariables": "セッション変数", + "loc.input.help.SessionVariables": "両方のスクリプトの共通セッション変数を設定します。例、$variable = value、$var1 = \"value, 123\"", + "loc.input.label.RunPowershellInParallel": "PowerShell の並列実行", + "loc.input.help.RunPowershellInParallel": "「true」に設定すると、複数の PowerShell スクリプトをターゲット コンピューター上で並列に実行します。", + "loc.input.label.ResourceFilteringMethod": "以下の条件でコンピューターを選択", + "loc.input.help.ResourceFilteringMethod": "必要に応じて、コンピューター名またはタグを指定してコンピューターのサブセットを選びます。", + "loc.input.label.MachineNames": "フィルター条件", + "loc.input.help.MachineNames": "この入力はマシン グループまたは出力変数にのみ使用でき、コンピューターのフラット リストではまだサポートされていません。マシンのリスト (dbserver.fabrikam.com, webserver.fabrikam.com, 192.168.12.34 など)、またはタグのリスト (Role:DB; OS:Win8.1 など) を指定します。複数のタグを指定した場合、指定されたタグを持つすべてのマシンでタスクが実行されます。既定では、タスクがすべてのマシンで実行されます。" +} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/ko-KR/resources.resjson b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/ko-KR/resources.resjson new file mode 100644 index 000000000000..c860b7e2db89 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/ko-KR/resources.resjson @@ -0,0 +1,32 @@ +{ + "loc.friendlyName": "대상 컴퓨터의 PowerShell", + "loc.helpMarkDown": "[자세한 정보](https://go.microsoft.com/fwlink/?linkid=627414)", + "loc.description": "원격 컴퓨터에서 PowerShell 스크립트 실행", + "loc.instanceNameFormat": "$(EnvironmentName)에서 PowerShell 실행", + "loc.group.displayName.deployment": "배포", + "loc.group.displayName.advanced": "고급 옵션", + "loc.input.label.EnvironmentName": "컴퓨터", + "loc.input.help.EnvironmentName": "포트와 함께 쉼표로 구분된 컴퓨터 IP 주소 또는 FQDN 목록을 입력합니다. 포트의 기본값은 선택된 프로토콜을 기준으로 설정됩니다.
예: dbserver.fabrikam.com,dbserver_int.fabrikam.com:5986,192.168.12.34:5986
또는 다른 작업의 출력 변수를 입력합니다. 예: $(variableName)
HTTPS를 사용하는 경우 컴퓨터의 이름/IP가 인증서의 CN과 일치해야 합니다.", + "loc.input.label.AdminUserName": "관리자 로그인", + "loc.input.help.AdminUserName": "대상 컴퓨터의 관리자 암호입니다.", + "loc.input.label.AdminPassword": "암호", + "loc.input.help.AdminPassword": "대상 컴퓨터에 대한 관리자 암호입니다.
빌드/릴리스 정의에 '$(passwordVariable)'(으)로 정의된 변수를 사용할 수 있습니다.
보호하기 위해 변수 형식을 'secret'으로 표시할 수 있습니다.", + "loc.input.label.Protocol": "프로토콜", + "loc.input.help.Protocol": "컴퓨터와의 WinRM 연결에 사용할 프로토콜을 선택하세요. 기본값은 HTTPS입니다.", + "loc.input.label.TestCertificate": "테스트 인증서", + "loc.input.help.TestCertificate": "신뢰할 수 있는 인증 기관의 컴퓨터 인증서 신뢰성 확인을 건너뛰려면 이 옵션을 선택하세요. WinRM HTTPS 프로토콜에는 매개 변수가 필요합니다.", + "loc.input.label.ScriptPath": "PowerShell 스크립트", + "loc.input.help.ScriptPath": "대상 컴퓨터나 UNC 경로(예: C:\\BudgetIT\\Web\\Deploy\\Website.ps1) 상의 PowerShell 스크립트 위치", + "loc.input.label.ScriptArguments": "스크립트 인수", + "loc.input.help.ScriptArguments": "PowerShell 스크립트에 대한 인수입니다. -testParam 테스트 같은 서수 매개 변수나 명명된 매개 변수일 수 있습니다.", + "loc.input.label.InitializationScriptPath": "초기화 스크립트", + "loc.input.help.InitializationScriptPath": "대상 컴퓨터나 UNC 경로(예: C:\\BudgetIT\\Web\\Deploy\\WebsiteConfiguration.ps1) 상의 DSC용 데이터 스크립트 위치", + "loc.input.label.SessionVariables": "세션 변수", + "loc.input.help.SessionVariables": "두 스크립트에 대한 일반 세션 변수(예: $variable = value, $var1 = \"value, 123\")를 설정합니다.", + "loc.input.label.RunPowershellInParallel": "PowerShell을 병렬로 실행", + "loc.input.help.RunPowershellInParallel": "true로 설정하면 PowerShell 스크립트가 대상 컴퓨터에서 동시에 실행됩니다.", + "loc.input.label.ResourceFilteringMethod": "컴퓨터 선택 기준", + "loc.input.help.ResourceFilteringMethod": "필요한 경우 컴퓨터 이름 또는 태그를 제공하여 컴퓨터의 하위 집합을 선택합니다.", + "loc.input.label.MachineNames": "필터 조건", + "loc.input.help.MachineNames": "이 입력은 컴퓨터 그룹 또는 출력 변수에만 유효하며 컴퓨터의 단순 목록에는 아직 지원되지 않습니다. dbserver.fabrikam.com, webserver.fabrikam.com, 192.168.12.34 등과 같은 컴퓨터나 Role:DB; OS:Win8.1 등과 같은 태그 목록을 지정하세요 여러 태그를 지정하는 경우 지정된 태그가 포함된 모든 컴퓨터에서 작업이 실행됩니다. 기본값은 모든 컴퓨터에서 실행하는 것입니다." +} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/ru-RU/resources.resjson b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/ru-RU/resources.resjson new file mode 100644 index 000000000000..b4e3dede3743 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/ru-RU/resources.resjson @@ -0,0 +1,32 @@ +{ + "loc.friendlyName": "PowerShell на целевых компьютерах", + "loc.helpMarkDown": "[Подробнее...](https://go.microsoft.com/fwlink/?linkid=627414)", + "loc.description": "Выполнять сценарии PowerShell на удаленных компьютерах", + "loc.instanceNameFormat": "Выполнить PowerShell в $(EnvironmentName)", + "loc.group.displayName.deployment": "Развертывание", + "loc.group.displayName.advanced": "Дополнительные параметры", + "loc.input.label.EnvironmentName": "Компьютеры", + "loc.input.help.EnvironmentName": "Укажите разделенный запятыми список IP-адресов или полных доменных имен компьютеров вместе с портами. Порт по умолчанию выбирается на основе используемого протокола.
Например: dbserver.fabrikam.com,dbserver_int.fabrikam.com:5986,192.168.12.34:5986.
Также можно указать выходную переменную других задач, например $(variableName).
Если используется протокол HTTPS, имя или IP-адрес компьютера должны совпадать с общим именем (CN) в сертификате.", + "loc.input.label.AdminUserName": "Имя для входа администратора", + "loc.input.help.AdminUserName": "Имя для входа администратора для целевых компьютеров.", + "loc.input.label.AdminPassword": "Пароль", + "loc.input.help.AdminPassword": "Пароль администратора для целевых компьютеров.
Он может принять переменную, заданную в определениях сборки или выпуска в качестве \"$(passwordVariable)\".
Вы можете отметить тип переменной как \"secret\", чтобы защитить ее.", + "loc.input.label.Protocol": "Протокол", + "loc.input.help.Protocol": "Выберите протокол, используемый в WinRM-подключениях к компьютерам. Значение по умолчанию — HTTPS.", + "loc.input.label.TestCertificate": "Тестовый сертификат", + "loc.input.help.TestCertificate": "Выберите этот параметр, чтобы пропустить проверку достоверности сертификата компьютера доверенным центром сертификации. Параметр обязателен для протокола WinRM HTTPS.", + "loc.input.label.ScriptPath": "Сценарий PowerShell", + "loc.input.help.ScriptPath": "Расположение скрипта PowerShell на целевых машинах или по UNC-пути, например C:\\BudgetIT\\Web\\Deploy\\Website.ps1", + "loc.input.label.ScriptArguments": "Аргументы скрипта", + "loc.input.help.ScriptArguments": "Аргументы для сценария PowerShell. Это могут быть порядковые или именованные параметры, например -testParam test", + "loc.input.label.InitializationScriptPath": "Сценарий инициализации", + "loc.input.help.InitializationScriptPath": "Расположение скрипта данных для DSC на целевых компьютерах или по UNC-пути, например C:\\BudgetIT\\Web\\Deploy\\WebsiteConfiguration.ps1", + "loc.input.label.SessionVariables": "Переменные сеанса", + "loc.input.help.SessionVariables": "Задайте стандартные переменные сеансов для обоих скриптов. Например, $variable = value, $var1 = \"value, 123\"", + "loc.input.label.RunPowershellInParallel": "Выполнять PowerShell параллельно", + "loc.input.help.RunPowershellInParallel": "Если задать значение true, скрипты PowerShell будут выполняться на целевых компьютерах параллельно.", + "loc.input.label.ResourceFilteringMethod": "Выбор компьютеров по", + "loc.input.help.ResourceFilteringMethod": "Как вариант, выберите подмножество компьютеров, указав их имена или теги.", + "loc.input.label.MachineNames": "Условия фильтра", + "loc.input.help.MachineNames": "Входные данные допустимы только для групп компьютеров или выходных переменных и пока не поддерживаются для плоского списка компьютеров. Укажите список компьютеров, например dbserver.fabrikam.com, webserver.fabrikam.com, 192.168.12.34, или теги, такие как Role:DB; OS:Win8.1. Если указано несколько тегов, задача будет выполняться на всех компьютерах с заданными тегами. По умолчанию задача выполняется на всех компьютерах." +} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/zh-CN/resources.resjson b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/zh-CN/resources.resjson new file mode 100644 index 000000000000..d96dd9bf9586 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/zh-CN/resources.resjson @@ -0,0 +1,32 @@ +{ + "loc.friendlyName": "目标计算机上的 PowerShell", + "loc.helpMarkDown": "[详细信息](https://go.microsoft.com/fwlink/?linkid=627414)", + "loc.description": "在远程计算机上执行 PowerShell 脚本", + "loc.instanceNameFormat": "在 $(EnvironmentName) 上运行 PowerShell", + "loc.group.displayName.deployment": "部署", + "loc.group.displayName.advanced": "高级选项", + "loc.input.label.EnvironmentName": "计算机", + "loc.input.help.EnvironmentName": "提供以逗号分隔的计算机 IP 地址或 FQDN 以及端口列表。端口默认基于选定的协议。
例如: dbserver.fabrikam.com,dbserver_int.fabrikam.com:5986,192.168.12.34:5986
或者提供其他任务的输出变量。例如: $(variableName)
如果使用的是 HTTPS,则计算机的名称/IP 应与证书中的 CN 匹配。", + "loc.input.label.AdminUserName": "管理员登录名", + "loc.input.help.AdminUserName": "目标计算机的管理员登录名。", + "loc.input.label.AdminPassword": "密码", + "loc.input.help.AdminPassword": "目标计算机的管理员密码。
可接受“生成/发布”定义中定义为 \"$(passwordVariable)\" 的变量。
你可将变量类型标记为“机密”来进行保护。", + "loc.input.label.Protocol": "协议", + "loc.input.help.Protocol": "选择与计算机进行 WinRM 连接时使用的协议。默认为 HTTPS.", + "loc.input.label.TestCertificate": "测试证书", + "loc.input.help.TestCertificate": "选择跳过验证计算机的证书是否真正由受信任的证书颁发机构签署的选项。WinRM HTTPS 协议需要该参数。", + "loc.input.label.ScriptPath": "PowerShell 脚本", + "loc.input.help.ScriptPath": "目标计算机上或 UNC 路径上 PowerShell 脚本的位置,如 C:\\BudgetIT\\Web\\Deploy\\Website.ps1", + "loc.input.label.ScriptArguments": "脚本参数", + "loc.input.help.ScriptArguments": "用于 PowerShell 脚本的参数。可以是序号或命名参数(如 -testParam test)", + "loc.input.label.InitializationScriptPath": "初始化脚本", + "loc.input.help.InitializationScriptPath": "目标计算机上或 UNC 路径上用于 DSC 的数据脚本的位置,如 C:\\BudgetIT\\Web\\Deploy\\WebsiteConfiguration.ps1", + "loc.input.label.SessionVariables": "会话变量", + "loc.input.help.SessionVariables": "为这两个脚本设置通用会话变量。例如,$variable = value,$var1 = \"value, 123\"", + "loc.input.label.RunPowershellInParallel": "并行运行 PowerShell", + "loc.input.help.RunPowershellInParallel": "将它设置为 true 会在目标计算机上并行运行 PowerShell 脚本。", + "loc.input.label.ResourceFilteringMethod": "计算机选择依据", + "loc.input.help.ResourceFilteringMethod": "(可选)通过提供计算机名或标记来选择计算机的子集。", + "loc.input.label.MachineNames": "筛选条件", + "loc.input.help.MachineNames": "此输入仅对计算机组或输出变量有效,且尚不受计算机的简单列表支持。提供计算机列表(如 dbserver.fabrikam.com、 webserver.fabrikam.com、 192.168.12.34)或标记列表(如 Role:DB; OS:Win8.1)。如果提供了多个标记,则任务将在具有指定标记的所有计算机中运行。默认为在所有计算机中运行任务。" +} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/zh-TW/resources.resjson b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/zh-TW/resources.resjson new file mode 100644 index 000000000000..611cb4447c2c --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Strings/resources.resjson/zh-TW/resources.resjson @@ -0,0 +1,32 @@ +{ + "loc.friendlyName": "目標電腦上的 PowerShell", + "loc.helpMarkDown": "[詳細資訊](https://go.microsoft.com/fwlink/?linkid=627414)", + "loc.description": "在遠端電腦上執行 PowerShell 指令碼。", + "loc.instanceNameFormat": "在 $(EnvironmentName) 執行 PowerShell", + "loc.group.displayName.deployment": "部署", + "loc.group.displayName.advanced": "進階選項", + "loc.input.label.EnvironmentName": "電腦", + "loc.input.help.EnvironmentName": "提供以逗點分隔的清單,內含電腦 IP 位址或 FQDN 以及連接埠。連接埠預設為依據所選取的通訊協定。
例如: dbserver.fabrikam.com,dbserver_int.fabrikam.com:5986,192.168.12.34:5986
或提供其他工作的輸出變數。例如: $(variableName)
若使用 HTTPS,電腦的名稱/IP 應符合憑證中的 CN。", + "loc.input.label.AdminUserName": "系統管理員登入", + "loc.input.help.AdminUserName": "目標電腦的系統管理員登入。", + "loc.input.label.AdminPassword": "密碼", + "loc.input.help.AdminPassword": "目標電腦的系統管理員密碼。
其可接受組建/發行定義中 '$(passwordVariable)' 這類形式的變數。
您可以將變數類型標示為 'secret' 加以保護。", + "loc.input.label.Protocol": "通訊協定", + "loc.input.help.Protocol": "選取 WinRM 與電腦連線時所囡使用的通訊協定。預設值為 HTTPS。", + "loc.input.label.TestCertificate": "測試憑證", + "loc.input.help.TestCertificate": "選取選項,跳過由受信任的憑證授權單位驗證電腦憑證真確性。WinRM HTTPS 通訊協定需要參數。", + "loc.input.label.ScriptPath": "PowerShell 指令碼", + "loc.input.help.ScriptPath": "PowerShell 指令碼在目標電腦或 UNC 路徑上的位置,例如 C:\\BudgetIT\\Web\\Deploy\\Website.ps1", + "loc.input.label.ScriptArguments": "指令碼引數", + "loc.input.help.ScriptArguments": "PowerShell 指令碼的引數。其可以是序號參數或具名參數,例如: testParam test", + "loc.input.label.InitializationScriptPath": "初始化指令碼", + "loc.input.help.InitializationScriptPath": "目標電腦或 UNC 路徑上之 DSC 資料指令碼的位置,例如 C:\\BudgetIT\\Web\\Deploy\\WebsiteConfiguration.ps1", + "loc.input.label.SessionVariables": "工作階段變數", + "loc.input.help.SessionVariables": "為兩種指令碼設定通用工作階段變數。例如 $variable = value、$var1 = \"value, 123\"", + "loc.input.label.RunPowershellInParallel": "平行執行 PowerShell", + "loc.input.help.RunPowershellInParallel": "若將其設為 Ture,將會在目標電腦中平行執行 PowerShell 指令碼。", + "loc.input.label.ResourceFilteringMethod": "選取電腦依據 ", + "loc.input.help.ResourceFilteringMethod": "選擇性地提供電腦名稱或標記來選取電腦的子集。", + "loc.input.label.MachineNames": "篩選準則", + "loc.input.help.MachineNames": "此輸入只對電腦群組或輸出變數有效,電腦的簡單列表尚無法支援。請以 dbserver.fabrikam.com、webserver.fabrikam.com、192.168.12.34 等形式提供電腦清單,或以 Role:DB; OS:Win8.1 等形式提供標記清單。若提供多個標記,工作會在所有具有指定標記的電腦上執行。預設會在所有電腦上執行此工作。" +} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/L0.ts b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0.ts new file mode 100644 index 000000000000..37a8c30706a2 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0.ts @@ -0,0 +1,95 @@ +/// +/// +/// + +import Q = require('q'); +import assert = require('assert'); +import path = require('path'); + +var psm = require('../../../Tests/lib/psRunner'); +var psr = null; + +describe('PowerShellOnTargetMachine Suite', function () { + this.timeout(20000); + + before((done) => { + if (psm.testSupported()) { + psr = new psm.PSRunner(); + psr.start(); + } + done(); + }); + + after(function () { + if (psr) { + psr.kill(); + } + }); + + if (psm.testSupported()) { + it('Validate Get-EnvironmentResources Command', (done) => { + psr.run(path.join(__dirname, 'L0ValidateEnvResources.ps1'), done); + }); + it('Validate Get-EnvironmentProperty Command', (done) => { + psr.run(path.join(__dirname, 'L0ValidateEnvProperty.ps1'), done); + }); + it('Throws if Invoke-PsOnRemote fails for a resource', (done) => { + psr.run(path.join(__dirname, 'L0InvalidEnvFail.ps1'), done); + }); + it('Performs deployment on all machines and works correctly for valid input for sequential run', (done) => { + psr.run(path.join(__dirname, 'L0ValidSequentialRun.ps1'), done); + }); + + it('Performs deployment on all machines and works correctly for valid input for Parallel run', (done) => { + psr.run(path.join(__dirname, 'L0ValidParallelRun.ps1'), done); + }); + it('Throws if job fails for resources in parallel run', (done) => { + psr.run(path.join(__dirname, 'L0ParallelRunFail.ps1'), done); + }); + it('Performs deployment on all machines with same resource name', (done) => { + psr.run(path.join(__dirname, 'L0ParallelRunDuplicate.ps1'), done); + }); + } +}); + +describe('PowerShellOnTargetMachine - (Get-SkipCACheckOption and Get-ResourceWinRmConfig) Suite', function() { + this.timeout(20000); + + before((done) => { + if (psm.testSupported()) { + psr = new psm.PSRunner(); + psr.start(); + } + done(); + }); + + after(function () { + if (psr) { + psr.kill(); + } + }); + + if (psm.testSupported()) { + it('Test for an environment with Https/SkipCA property set', (done) => { + psr.run(path.join(__dirname, 'L0SkipCAPropertyOnly.ps1'), done); + }); + it('Test for an environment with Http property set and not Https/skiCA', (done) => { + psr.run(path.join(__dirname, 'L0HttpProperty.ps1'), done); + }); + it('Should throw exception saying both the protocols were not set', (done) => { + psr.run(path.join(__dirname, 'L0NoHttp(s)AndskipCAProperty.ps1'), done); + }); + it('Should try to get Http Port and not Https Port', (done) => { + psr.run(path.join(__dirname, 'L0HttpAndNoSkipCA.ps1'), done); + }); + it('Should try to get Http Port and not Https Port. Should throw when Http port not found', (done) => { + psr.run(path.join(__dirname, 'L0SkipCAandNoHttpPort.ps1'), done); + }); + it('Should try to get Https Port and not Http Port', (done) => { + psr.run(path.join(__dirname, 'L0SkipCAandHttpsPort.ps1'), done); + }); + it('Should try to get Https Port and not Http Port. Should throw when Https port not found', (done) => { + psr.run(path.join(__dirname, 'L0SkipCAandNoHttpsPort.ps1'), done); + }); + } +}); diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/L0HttpAndNoSkipCA.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0HttpAndNoSkipCA.ps1 new file mode 100644 index 000000000000..22cd5f8cfc5f --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0HttpAndNoSkipCA.ps1 @@ -0,0 +1,28 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 +. $PSScriptRoot\MockModule.ps1 + +$remotePowershellRunnerPath = "$PSScriptRoot\..\PowerShellOnTargetMachines.ps1" + +Register-Mock Get-ParsedSessionVariables { } + +Register-Mock Invoke-PsOnRemote { } +Register-Mock Invoke-Command { + $deploymentResponse = @{} + $deploymentResponse.Status = "Passed" + return $deploymentResponse + } + +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $environmentWithSkipCANotSet } -ParametersEvaluator {$EnvironmentName -eq $environmentWithSkipCANotSet} +Register-Mock Get-EnvironmentResources { return $validResources } -ParametersEvaluator {$EnvironmentName -eq $environmentWithSkipCANotSet} +Register-Mock Get-EnvironmentProperty { return $environmentWinRMHttpPort } -ParametersEvaluator {$Environment.Name -eq $environmentWithSkipCANotSet -and $Key -eq $resourceWinRMHttpPortKeyName} + +& "$remotePowershellRunnerPath" -environmentName $environmentWithSkipCANotSet -machineNames $validMachineName1 -scriptPath $validScriptPath -runPowershellInParallel $false -protocol "HTTP" + +Assert-WasCalled Get-EnvironmentProperty -Times 0 -ParametersEvaluator {$Environment.Name -eq $environmentWithSkipCANotSet -and $Key -eq $resourceWinRMHttpsPortKeyName} + +# Function called twice +Assert-WasCalled Get-EnvironmentProperty -Times 2 -ParametersEvaluator {$Environment.Name -eq $environmentWithSkipCANotSet -and $Key -eq $resourceWinRMHttpPortKeyName} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/L0HttpProperty.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0HttpProperty.ps1 new file mode 100644 index 000000000000..8d992ee9b711 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0HttpProperty.ps1 @@ -0,0 +1,27 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 +. $PSScriptRoot\MockModule.ps1 + +$remotePowershellRunnerPath = "$PSScriptRoot\..\PowerShellOnTargetMachines.ps1" + +Register-Mock Get-ParsedSessionVariables { } + +Register-Mock Invoke-PsOnRemote { } +Register-Mock Invoke-Command { + $deploymentResponse = @{} + $deploymentResponse.Status = "Passed" + return $deploymentResponse + } + +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $environmentWithSkipCANotSet } -ParametersEvaluator {$EnvironmentName -eq $environmentWithSkipCANotSet} +Register-Mock Get-EnvironmentResources { return $validResources } -ParametersEvaluator {$EnvironmentName -eq $environmentWithSkipCANotSet} +Register-Mock Get-EnvironmentProperty { return $environmentWinRMHttpPort } -ParametersEvaluator {$Environment.Name -eq $environmentWithSkipCANotSet -and $Key -eq $resourceWinRMHttpPortKeyName} + +& "$remotePowershellRunnerPath" -environmentName $environmentWithSkipCANotSet -machineNames $validMachineName1 -scriptPath $validScriptPath -runPowershellInParallel $false + +Assert-WasCalled Get-EnvironmentProperty -Times 1 -ParametersEvaluator {$Environment.Name -eq $environmentWithSkipCANotSet -and $Key -eq $skipCACheckKeyName} +Assert-WasCalled Get-EnvironmentProperty -Times 0 -ParametersEvaluator {$Environment.Name -eq $environmentWithSkipCANotSet -and $Key -eq $resourceWinRMHttpsPortKeyName} +Assert-WasCalled Get-EnvironmentProperty -Times 2 -ParametersEvaluator {$Environment.Name -eq $environmentWithSkipCANotSet -and $Key -eq $resourceWinRMHttpPortKeyName} diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/L0InvalidEnvFail.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0InvalidEnvFail.ps1 new file mode 100644 index 000000000000..bfa60d6a7577 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0InvalidEnvFail.ps1 @@ -0,0 +1,27 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 +. $PSScriptRoot\MockModule.ps1 + +$remotePowershellRunnerPath = "$PSScriptRoot\..\PowerShellOnTargetMachines.ps1" + +Register-Mock Get-ParsedSessionVariables { } + +Register-Mock Register-Environment { + return GetEnvironmentWithStandardProvider $invalidEnvironmentNameForFailDeploy +} -ParametersEvaluator {$EnvironmentName -eq $invalidEnvironmentNameForFailDeploy} + +Register-Mock Get-EnvironmentResources { return $resourceFailForDeploy } -ParametersEvaluator {$EnvironmentName -eq $invalidEnvironmentNameForFailDeploy} +Register-Mock Get-Environment { return $environmentWithStandardProvider } -ParametersEvaluator {$EnvironmentName -eq $invalidEnvironmentNameForFailDeploy} + +Register-Mock Get-EnvironmentProperty { + $machineNamesForFailDeploy +} + +Register-Mock Invoke-PsOnRemote { throw "$FailedDeployError" } -ParametersEvaluator { $MachineDnsName -eq $machineNamesForFailDeploy} + +Assert-Throws { + & "$remotePowershellRunnerPath" -environmentName $invalidEnvironmentNameForFailDeploy -machineNames $validMachineNames -scriptPath $validScriptPath -runPowershellInParallel $false +} -MessagePattern "$FailedDeployError" \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/L0NoHttp(s)AndskipCAProperty.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0NoHttp(s)AndskipCAProperty.ps1 new file mode 100644 index 000000000000..dc9362489f0b --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0NoHttp(s)AndskipCAProperty.ps1 @@ -0,0 +1,30 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 +. $PSScriptRoot\MockModule.ps1 + +$remotePowershellRunnerPath = "$PSScriptRoot\..\PowerShellOnTargetMachines.ps1" + +Register-Mock Get-ParsedSessionVariables { } + +Register-Mock Invoke-PsOnRemote { } +Register-Mock Invoke-Command { + $deploymentResponse = @{} + $deploymentResponse.Status = "Passed" + return $deploymentResponse + } + +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $envWithBothProtocalsNotSet } -ParametersEvaluator {$EnvironmentName -eq $envWithBothProtocalsNotSet} +Register-Mock Get-EnvironmentResources { return $validResources } -ParametersEvaluator {$EnvironmentName -eq ($envWithBothProtocalsNotSet)} +Register-Mock Get-EnvironmentProperty { return '' } -ParametersEvaluator {$Environment.Name -eq $envWithBothProtocalsNotSet -and $Key -eq $resourceWinRMHttpsPortKeyName} +Register-Mock Get-EnvironmentProperty { return '' } -ParametersEvaluator {$Environment.Name -eq $envWithBothProtocalsNotSet -and $Key -eq $resourceWinRMHttpPortKeyName} + +Assert-Throws { + & "$remotePowershellRunnerPath" -environmentName $envWithBothProtocalsNotSet -machineNames $validMachineName1 -scriptPath $validScriptPath -runPowershellInParallel $false +} -MessagePattern "*" + +Assert-WasCalled Get-EnvironmentProperty -Times 1 -ParametersEvaluator {$Environment.Name -eq $envWithBothProtocalsNotSet -and $Key -eq $skipCACheckKeyName} +Assert-WasCalled Get-EnvironmentProperty -Times 1 -ParametersEvaluator {$Environment.Name -eq $envWithBothProtocalsNotSet -and $Key -eq $resourceWinRMHttpsPortKeyName} +Assert-WasCalled Get-EnvironmentProperty -Times 1 -ParametersEvaluator {$Environment.Name -eq $envWithBothProtocalsNotSet -and $Key -eq $resourceWinRMHttpPortKeyName} diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ParallelRunDuplicate.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ParallelRunDuplicate.ps1 new file mode 100644 index 000000000000..c43dcb9a4cd2 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ParallelRunDuplicate.ps1 @@ -0,0 +1,34 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 +. $PSScriptRoot\MockModule.ps1 + +$remotePowershellRunnerPath = "$PSScriptRoot\..\PowerShellOnTargetMachines.ps1" + +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $validEnvironmentNameWithDuplicateResourceName } -ParametersEvaluator {$EnvironmentName -eq $validEnvironmentNameWithDuplicateResourceName} +Register-Mock Get-EnvironmentResources { return $validResourcesWithDuplicateResourceName } -ParametersEvaluator {$Environment.Name -eq $validEnvironmentNameWithDuplicateResourceName} +Register-Mock Get-EnvironmentProperty{ return $environmentWinRMHttpPortForDuplicateResource } -ParametersEvaluator{$Key -eq $resourceWinRMHttpPortKeyName -and $ResourceId -eq $validMachineId1Duplicate} +Register-Mock Get-EnvironmentProperty { return $environmentWinRMHttpsPort } -ParametersEvaluator {$Key -eq $resourceWinRMHttpsPortKeyName} +Register-Mock Get-EnvironmentProperty { return $validMachineName1 } -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId1Duplicate} +Register-Mock Get-EnvironmentProperty { return $validMachineName1 } -ParametersEvaluator{$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId1} +Register-Mock Start-Job { $testJobs.Add($Job1); return $job1} -ParametersEvaluator {$ArgumentList -contains $validResource1.Name -and $ArgumentList -contains $environmentWinRMHttpsPort} +Register-Mock Start-Job { $testJobs.Add($Job2); return $job2} -ParametersEvaluator {$ArgumentList -contains $validResource1Duplicate.Name -and $ArgumentList -contains $environmentWinRMHttpPortForDuplicateResource } +#Get-Job Mocks +Register-Mock Get-Job { return $testJobs } + +#Start-Sleep Mocks +Register-Mock Start-Sleep { } + +#Receive-Job Mocks +Register-Mock Receive-Job { return $JobPassResponse} + +#Remove-Job Mocks +Register-Mock Remove-Job { $testJobs.RemoveAt(0) } + +& "$remotePowershellRunnerPath" -environmentName $validEnvironmentNameWithDuplicateResourceName -machineNames "" -scriptPath $validScriptPath -runPowershellInParallel $true + +Assert-WasCalled Start-Job -Times 2 +Assert-WasCalled Get-EnvironmentProperty -Times 1 -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId1Duplicate} +Assert-WasCalled Get-EnvironmentProperty -Times 1 -ParametersEvaluator {$Key -eq $resourceWinRMHttpPortKeyName -and $ResourceId -eq $validMachineId1Duplicate} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ParallelRunFail.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ParallelRunFail.ps1 new file mode 100644 index 000000000000..869f9de0bbf9 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ParallelRunFail.ps1 @@ -0,0 +1,37 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 +. $PSScriptRoot\MockModule.ps1 + +$remotePowershellRunnerPath = "$PSScriptRoot\..\PowerShellOnTargetMachines.ps1" + + +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $EnvironmentNameForFailedJob } -ParametersEvaluator {$EnvironmentName -eq $EnvironmentNameForFailedJob} +Register-Mock Get-EnvironmentResources { return $validResources } -ParametersEvaluator {$EnvironmentName -eq $EnvironmentNameForFailedJob} + +Register-Mock Get-EnvironmentProperty { return $environmentWinRMHttpsPort } -ParametersEvaluator {$Key -eq $resourceWinRMHttpsPortKeyName} +Register-Mock Get-EnvironmentProperty { return $validMachineName1 } -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId1} +Register-Mock Get-EnvironmentProperty { return $validMachineName2 } -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId2} + +Register-Mock Start-Job { $testJobs.Add($Job1); return $job1} -ParametersEvaluator {$ArgumentList -contains $validResource1.Name } +Register-Mock Start-Job { $testJobs.Add($Job2); return $job2} -ParametersEvaluator {$ArgumentList -contains $validResource2.Name } + +#Get-Job Mocks +Register-Mock Get-Job { return $testJobs } + +#Start-Sleep Mocks +Register-Mock Start-Sleep { } + +#Receive-Job Mocks +Register-Mock Receive-Job { return $JobFailResponseForDeploy } -ParametersEvaluator { $Id -eq 2 } + +#Remove-Job Mocks +Register-Mock Remove-Job { $testJobs.RemoveAt(0) } + +Assert-Throws { + & "$remotePowershellRunnerPath" -environmentName $EnvironmentNameForFailedJob -machineNames $validMachineNames -scriptPath $validScriptPath -runPowershellInParallel $true +} -MessagePattern "Deployment on one or more machines failed." + +Assert-WasCalled Start-Job -Times 2 \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/L0SkipCAPropertyOnly.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0SkipCAPropertyOnly.ps1 new file mode 100644 index 000000000000..6aa5ee58428a --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0SkipCAPropertyOnly.ps1 @@ -0,0 +1,16 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 +. $PSScriptRoot\MockModule.ps1 +. $PSScriptRoot\..\Utility.ps1 +$doNotSkipCACheckOption = '' + +Register-Mock Register-Environment { return GetEnvironmentWithAzureProvider $environmentWithSkipCASet } -ParametersEvaluator { $EnvironmentName -eq $environmentWithSkipCASet } +$environment = Register-Environment -EnvironmentName $environmentWithSkipCASet +Register-Mock Get-EnvironmentProperty { $environmentWinRMHttpsPort} -ParametersEvaluator { $Environment.Name -eq $environmentWithSkipCASet } + +Get-SkipCACheckOption -environmentName $environmentWithSkipCASet + +Assert-WasCalled Get-EnvironmentProperty -Times 1 -ParametersEvaluator { $Environment.Name -eq $environmentWithSkipCASet } \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/L0SkipCAandHttpsPort.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0SkipCAandHttpsPort.ps1 new file mode 100644 index 000000000000..f5973a5797a2 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0SkipCAandHttpsPort.ps1 @@ -0,0 +1,27 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 +. $PSScriptRoot\MockModule.ps1 + +$remotePowershellRunnerPath = "$PSScriptRoot\..\PowerShellOnTargetMachines.ps1" + +Register-Mock Get-ParsedSessionVariables { } + +Register-Mock Invoke-PsOnRemote { } +Register-Mock Invoke-Command { + $deploymentResponse = @{} + $deploymentResponse.Status = "Passed" + return $deploymentResponse + } + +Register-Mock Register-Environment { return GetEnvironmentWithAzureProvider $EnvironmentName } -ParametersEvaluator {$EnvironmentName -eq $environmentWithSkipCASet} +Register-Mock Get-EnvironmentResources { return $validResources } -ParametersEvaluator {$Environment.Name -eq ($environmentWithSkipCASet)} +Register-Mock Get-EnvironmentProperty { return $environmentWinRMHttpsPort } -ParametersEvaluator {$Key -eq $resourceWinRMHttpsPortKeyName} + +& "$remotePowershellRunnerPath" -environmentName $environmentWithSkipCASet -machineNames $validMachineName1 -scriptPath $validScriptPath -runPowershellInParallel $false -protocol "HTTPS" + +#Function invoked twice. (once for each valid resource) +Assert-WasCalled Get-EnvironmentProperty -Times 2 -ParametersEvaluator {$Environment.Name -eq $environmentWithSkipCASet -and $Key -eq $resourceWinRMHttpsPortKeyName} +Assert-WasCalled Get-EnvironmentProperty -Times 0 -ParametersEvaluator {$Environment.Name -eq $environmentWithSkipCASet -and $Key -eq $resourceWinRMHttpPortKeyName} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/L0SkipCAandNoHttpPort.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0SkipCAandNoHttpPort.ps1 new file mode 100644 index 000000000000..7b6d782b99a6 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0SkipCAandNoHttpPort.ps1 @@ -0,0 +1,28 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 +. $PSScriptRoot\MockModule.ps1 + +$remotePowershellRunnerPath = "$PSScriptRoot\..\PowerShellOnTargetMachines.ps1" + +Register-Mock Get-ParsedSessionVariables { } + +Register-Mock Invoke-PsOnRemote { } +Register-Mock Invoke-Command { + $deploymentResponse = @{} + $deploymentResponse.Status = "Passed" + return $deploymentResponse + } + +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $environmentWithSkipCASet } -ParametersEvaluator {$EnvironmentName -eq $environmentWithSkipCASet} +Register-Mock Get-EnvironmentResources { return $validResources } -ParametersEvaluator {$EnvironmentName -eq $environmentWithSkipCASet} +Register-Mock Get-EnvironmentProperty { return '' } -ParametersEvaluator {$Environment.Name -eq $environmentWithSkipCASet -and $Key -eq $resourceWinRMHttpPortKeyName} + +Assert-Throws { + & "$remotePowershellRunnerPath" -environmentName $environmentWithSkipCASet -machineNames $validMachineName1 -scriptPath $validScriptPath -runPowershellInParallel $false -protocol "HTTP" +} + +Assert-WasCalled Get-EnvironmentProperty -Times 0 -ParametersEvaluator {$Environment.Name -eq $environmentWithSkipCASet -and $Key -eq $resourceWinRMHttpsPortKeyName} +Assert-WasCalled Get-EnvironmentProperty -Times 1 -ParametersEvaluator {$Environment.Name -eq $environmentWithSkipCASet -and $Key -eq $resourceWinRMHttpPortKeyName} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/L0SkipCAandNoHttpsPort.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0SkipCAandNoHttpsPort.ps1 new file mode 100644 index 000000000000..c9f1b24647f6 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0SkipCAandNoHttpsPort.ps1 @@ -0,0 +1,28 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 +. $PSScriptRoot\MockModule.ps1 + +$remotePowershellRunnerPath = "$PSScriptRoot\..\PowerShellOnTargetMachines.ps1" + +Register-Mock Get-ParsedSessionVariables { } + +Register-Mock Invoke-PsOnRemote { } +Register-Mock Invoke-Command { + $deploymentResponse = @{} + $deploymentResponse.Status = "Passed" + return $deploymentResponse + } + +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $envWithBothProtocalsNotSet } -ParametersEvaluator {$EnvironmentName -eq $envWithBothProtocalsNotSet} +Register-Mock Get-EnvironmentResources { return $validResources } -ParametersEvaluator {$EnvironmentName -eq $envWithBothProtocalsNotSet} +Register-Mock Get-EnvironmentProperty { return '' } -ParametersEvaluator {$Environment.Name -eq $envWithBothProtocalsNotSet -and $Key -eq $resourceWinRMHttpsPortKeyName} + +Assert-Throws { + & "$remotePowershellRunnerPath" -environmentName $envWithBothProtocalsNotSet -machineNames $validMachineName1 -scriptPath $validScriptPath -runPowershellInParallel $false -protocol "HTTPS" +} +Assert-WasCalled Get-EnvironmentProperty -Times 1 -ParametersEvaluator {$Environment.Name -eq $envWithBothProtocalsNotSet -and $Key -eq $resourceWinRMHttpsPortKeyName} + +Assert-WasCalled Get-EnvironmentProperty -Times 0 -ParametersEvaluator {$Environment.Name -eq $envWithBothProtocalsNotSet -and $Key -eq $resourceWinRMHttpPortKeyName} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ValidParallelRun.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ValidParallelRun.ps1 new file mode 100644 index 000000000000..30245047aea5 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ValidParallelRun.ps1 @@ -0,0 +1,35 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 +. $PSScriptRoot\MockModule.ps1 + +$remotePowershellRunnerPath = "$PSScriptRoot\..\PowerShellOnTargetMachines.ps1" + + +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $validEnvironmentName } -ParametersEvaluator {$EnvironmentName -eq $validEnvironmentName} +Register-Mock Get-EnvironmentResources { return $validResources } -ParametersEvaluator {$EnvironmentName -eq $validEnvironmentName} + +Register-Mock Get-EnvironmentProperty { return $environmentWinRMHttpsPort } -ParametersEvaluator {$Key -eq $resourceWinRMHttpsPortKeyName} +Register-Mock Get-EnvironmentProperty { return $validMachineName1 } -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId1} +Register-Mock Get-EnvironmentProperty { return $validMachineName2 } -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId2} + +Register-Mock Start-Job { $testJobs.Add($Job1); return $job1} -ParametersEvaluator {$ArgumentList -contains $validResource1.Name } +Register-Mock Start-Job { $testJobs.Add($Job2); return $job2} -ParametersEvaluator {$ArgumentList -contains $validResource2.Name } + +#Get-Job Mocks +Register-Mock Get-Job { return $testJobs } + +#Start-Sleep Mocks +Register-Mock Start-Sleep { } + +#Receive-Job Mocks +Register-Mock Receive-Job { return $JobPassResponse} + +#Remove-Job Mocks +Register-Mock Remove-Job { $testJobs.RemoveAt(0) } + +& "$remotePowershellRunnerPath" -environmentName $validEnvironmentName -machineNames $validMachineNames -scriptPath $validScriptPath -runPowershellInParallel $true + +Assert-WasCalled Start-Job -Times 2 \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ValidSequentialRun.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ValidSequentialRun.ps1 new file mode 100644 index 000000000000..c5549a75a0a1 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ValidSequentialRun.ps1 @@ -0,0 +1,25 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 +. $PSScriptRoot\MockModule.ps1 + +$remotePowershellRunnerPath = "$PSScriptRoot\..\PowerShellOnTargetMachines.ps1" + +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $validEnvironmentName } -ParametersEvaluator {$EnvironmentName -eq $validEnvironmentName} +Register-Mock Get-EnvironmentResources { return $validResources } -ParametersEvaluator {$EnvironmentName -eq $validEnvironmentName} +Register-Mock Get-EnvironmentProperty { return $environmentWinRMHttpsPort } -ParametersEvaluator {$Key -eq $resourceWinRMHttpsPortKeyName} +Register-Mock Get-ParsedSessionVariables { } + +Register-Mock Get-EnvironmentProperty { return $validMachineName1 } -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId1} +Register-Mock Get-EnvironmentProperty { return $validMachineName2 } -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId2} + +Register-Mock Invoke-PsOnRemote { $passResponseForResource1 } -ParametersEvaluator {$MachineDnsName -eq $validResource1.Name -and $scriptPath -eq $validScriptPath } +Register-Mock Invoke-PsOnRemote { $passResponseForResource2 } -ParametersEvaluator {$MachineDnsName -eq $validResource2.Name -and $scriptPath -eq $validScriptPath } + +& "$remotePowershellRunnerPath" -environmentName $validEnvironmentName -machineNames $validMachineNames -scriptPath $validScriptPath -runPowershellInParallel $false ` + -adminUserName $userName -adminPassword $password -protocol $winRmProtocol -testCertificate "False" + +Assert-WasCalled Register-Environment -Times 1 -ParametersEvaluator {$EnvironmentName -eq $validEnvironmentName -and $EnvironmentSpecification -eq $validEnvironmentName ` + -and $UserName -eq $userName -and $Password -eq $password -and $WinRmProtocol -eq $winRmProtocol -and $TestCertificate -eq $false} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ValidateEnvProperty.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ValidateEnvProperty.ps1 new file mode 100644 index 000000000000..0be96f9e6a8f --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ValidateEnvProperty.ps1 @@ -0,0 +1,20 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 +. $PSScriptRoot\MockModule.ps1 + +$remotePowershellRunnerPath = "$PSScriptRoot\..\PowerShellOnTargetMachines.ps1" + +Register-Mock Register-Environment { + return GetEnvironmentWithStandardProvider $invalidEnvironmentNameForNoResourceProperty +} -ParametersEvaluator { $EnvironmentName -eq $invalidEnvironmentNameForNoResourceProperty } + +Register-Mock Get-EnvironmentResources { return $resourceFailForNoProperty } -ParametersEvaluator {$EnvironmentName -eq $invalidEnvironmentNameForNoResourceProperty} +Register-Mock Get-EnvironmentProperty { throw "No property invalidResourcePropertyKeyName found." } -ParametersEvaluator { $ResourceId -eq $machineIdForNoResouceProperty } + +Assert-Throws { + & "$remotePowershellRunnerPath" -environmentName $invalidEnvironmentNameForNoResourceProperty -machineNames $validMachineNames -scriptPath $validScriptPath -runPowershellInParallel $false +} -MessagePattern "No property invalidResourcePropertyKeyName found." + diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ValidateEnvResources.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ValidateEnvResources.ps1 new file mode 100644 index 000000000000..0b068ec9b40d --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/L0ValidateEnvResources.ps1 @@ -0,0 +1,54 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 +. $PSScriptRoot\MockModule.ps1 + +$remotePowershellRunnerPath = "$PSScriptRoot\..\PowerShellOnTargetMachines.ps1" + +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $validEnvironmentNameWithNoVm } -ParametersEvaluator { $EnvironmentName -eq $validEnvironmentNameWithNoVm } +Register-Mock Get-EnvironmentResources { return $emptyResourceList } -ParametersEvaluator { $EnvironmentName -eq $validEnvironmentNameWithNoVm } + +# "Should throw for environment name with no vm" +Assert-Throws { + & "$remotePowershellRunnerPath" -environmentName $validEnvironmentNameWithNoVm -machineNames $emptyInputMachineName -scriptPath $validScriptPath -runPowershellInParallel $false +} -MessagePattern "No machine exists under environment: '$validEnvironmentNameWithNoVm' for deployment" + +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $invalidInputEnvironmentName } -ParametersEvaluator { $EnvironmentName -eq $invalidInputEnvironmentName } +Register-Mock Get-EnvironmentResources { throw "No environment found" } -ParametersEvaluator {$EnvironmentName -eq $invalidInputEnvironmentName} +Register-Mock Get-EnvironmentProperty { } + +# "Should throw for invalid environment name" +Assert-Throws { + & "$remotePowershellRunnerPath" -environmentName $invalidInputEnvironmentName -machineNames $validMachineNames -scriptPath $validScriptPath -runPowershellInParallel $false +} -MessagePattern "No environment found" + +Assert-WasCalled Get-EnvironmentProperty -Times 0 + +$invalidEnvironmentWithNoResource = "invalidEnvironmentWithNoResource" +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $invalidEnvironmentWithNoResource } -ParametersEvaluator { $EnvironmentName -eq $invalidEnvironmentWithNoResource } +Register-Mock Get-EnvironmentResources { throw "No resources found" } -ParametersEvaluator {$EnvironmentName -eq $invalidEnvironmentWithNoResource} + +# "Should throw for invalid machine names" +Assert-Throws { + & "$remotePowershellRunnerPath" -environmentName $invalidEnvironmentWithNoResource -machineNames $invalidInputMachineNames -scriptPath $validScriptPath -runPowershellInParallel $false +} -MessagePattern "No resources found" + +Assert-WasCalled Get-EnvironmentProperty -Times 0 + + +Unregister-Mock Get-EnvironmentProperty +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $validEnvironmentName } -ParametersEvaluator { $EnvironmentName -eq $validEnvironmentName } +Register-Mock Get-EnvironmentResources { return $validResources } -ParametersEvaluator { $EnvironmentName -eq $validEnvironmentName } +Register-Mock Get-EnvironmentProperty { return $environmentWinRMHttpPort } -ParametersEvaluator { $Key -eq $resourceWinRMHttpPortKeyName } + +Register-Mock Invoke-Command { + $deploymentResponse = @{} + $deploymentResponse.Status = "Passed" + return $deploymentResponse +} +#should not throw error for valid input +& "$remotePowershellRunnerPath" -environmentName $validEnvironmentName -resourceFilteringMethod "tags" -machineNames $validTagString -scriptPath $validScriptPath -runPowershellInParallel $false + +Assert-WasCalled Get-EnvironmentResources -Times 1 -ParametersEvaluator { $Environment.Name -eq $validEnvironmentName } \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/MockModule.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Tests/MockModule.ps1 new file mode 100644 index 000000000000..73ee04a3c0ea --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/MockModule.ps1 @@ -0,0 +1,29 @@ +function GetEnvironmentWithStandardProvider( + [string]$environmentName) +{ + return @{ "Name" = $environmentName } +} + +function GetEnvironmentWithAzureProvider( + [string]$environmentName) +{ + return @{ + "Name" = $environmentName ; + "provider" = @{ + "id"="2"; + "name"="AzureResourceGroupManagerV2"; + "url"="https=//builddemos82.dtldev.tfsallin.net/DefaultCollection/demo/_apis/vslabs/providers/ValidProvider" + }; + } +} + +Register-Mock Import-Module { } +Register-Mock Get-ResourceHttpTagKey { return $validResourceWinRMHttpPortKeyName } +Register-Mock Get-ResourceHttpsTagKey { return $validResourceWinRMHttpsPortKeyName } +Register-Mock Write-Telemetry { } +Register-Mock Get-ResourceFQDNTagKey { return $validResourceFQDNKeyName } +Register-Mock Get-VssConnection { return $null } +Register-Mock Get-ResourceCredentials { } +Register-Mock Get-SkipCACheckTagKey { return $validSkipCACheckKeyName} +Register-Mock Write-ResponseLogs { } +Register-Mock Get-ChildItem { return $assembly } \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Tests/MockVariable.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Tests/MockVariable.ps1 new file mode 100644 index 000000000000..39efd8a1d1b3 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Tests/MockVariable.ps1 @@ -0,0 +1,159 @@ +# Input Parameters + +$validResourceFQDNKeyName = 'Microsoft-Vslabs-MG-Resource-FQDN' +$validResourceWinRMHttpPortKeyName = 'WinRM_Http' +$validResourceWinRMHttpsPortKeyName = 'WinRM_Https' +$validSkipCACheckKeyName = 'Microsoft-Vslabs-MG-SkipCACheck' +$validTagKey = "tag1" +$validTagValue = "value1" +$validTagKey2 = "tag2" +$validTagValue2 = "value2" +$validTagString = " $validTagKey : $validTagValue; $validTagKey2 : $validTagValue2" +$validTagStringWithSemicolon = " $validTagKey : $validTagValue; ;;;; $validTagKey2 : $validTagValue2; " +$validTagList = New-Object 'System.Collections.Generic.List[Tuple[string,string]]' +$validTagList.Add((New-Object "System.Tuple[string,string]" ($validTagKey, $validTagValue))) +$validTagList.Add((New-Object "System.Tuple[string,string]" ($validTagKey2, $validTagValue2))) +$validTagList = [System.Collections.Generic.IEnumerable[Tuple[string,string]]]$validTagList +$validEnvironmentName = "Test" +$validEnvironmentNameWithNoVm = "validEnvironmentNameWithNoVm" +$validEnvironmentNameWithDuplicateResourceName = "validEnvironmentNameWithDuplicateVmName" + $environmentWithStandardProvider = @{ + } + $environmentWithAzureProvider = @{ + "provider"=@{ + "id"="2"; + "name"="AzureResourceGroupManagerV2"; + "url"="https=//builddemos82.dtldev.tfsallin.net/DefaultCollection/demo/_apis/vslabs/providers/ValidProvider" + }; + } + + $azureProvider = @{ + "id"="2"; + "name"="AzureResourceGroupManagerV2"; + "url"="https=//builddemos82.dtldev.tfsallin.net/DefaultCollection/demo/_apis/vslabs/providers/ValidProvider" + } + +$validMachineName1 = "Test1" +$validMachineName2 = "Test2" +$validMachineId1 = "18" +$validMachineId2 = "19" +$validMachineId1Duplicate = "23" +$emptyInputMachineName = "" +$validMachineNames = $validMachineName1 + ", " + $validMachineName2 +$testPath = Join-Path $env:windir "Test" +$powershellScriptPath = Join-Path $testPath 'powershell.ps1' +$validScriptPath = $powershellScriptPath +$validInitializationScriptPath = $powershellScriptPath +$assembly = New-Object System.Collections.Generic.List``1[System.Object] +$testJobs = New-Object System.Collections.Generic.List``1[System.Object] +$userName = "UserName" +$password = "Password" +$winRmProtocol = "HTTPS" + +#Invalid Input Parameters +$invalidInputEnvironmentName = "Invalid" +$invalidEnvironmentNameForFailCopy = "CopyFail" +$invalidEnvironmentNameForFailDeploy = "DeployFail" +$invalidEnvironmentNameWithNoUsername = "UsernameFail" +$invalidEnvironmentNameForNoResourceProperty = "CopyFailWithNoResourceProperty" +$environmentWithSkipCASet = "envWithSkipCAEnabled" +$environmentWithSkipCANotSet = "envWithSkipCADisabled" +$envWithBothProtocalsNotSet = "envWithBothProtocalsNotSet" +$EnvironmentNameForFailedJob = "JobFail" + +$machineNamesForFailCopy = "Test3" +$machineNamesForFailDeploy = "Test4" +$machineNamesForNoResouceProperty = "Test5" +$invalidInputMachineNames = "Invalid1" + +$machineIdForFailCopy = "20" +$machineIdForFailDeploy = "21" +$machineIdForNoResouceProperty = "22" + +# Environment Properties + +$environmentWinRMHttpPort = '5985' +$environmentWinRMHttpPortForDuplicateResource = '5987' +$environmentWinRMHttpsPort = '5986' +$environmentUsername = "fareast\test" +$environmentPassword = "Password~1" + +# Environment / Resource operations + +$environmentOperationId = [guid]::NewGuid().ToString() +$operationIdForResource1 = [guid]::NewGuid().ToString() +$operationIdForResource2 = [guid]::NewGuid().ToString() +$operationIdForResource3 = [guid]::NewGuid().ToString() +$operationIdForResource4 = [guid]::NewGuid().ToString() + +# Response Status + +$FailedStatus = "Failed" +$PassedStatus = "Passed" + + +# Response Logs + +$SuccessLog = "Success Logs" +$FailedLog = "Failed Logs" +$FailedCopyLog = "Failed Copy Operation." +$FailedDeployLog = "Failed Deployment Operation." + + +# Response Error + +$FailedError = "Operation Failed" +$FailedCopyError = $FailedCopyLog +$FailedDeployError = $FailedDeployLog + +# Resources + +$emptyResourceList = @{} +$validResource1 = @{"Id" = $validMachineId1; "Name" = $validMachineName1; "Type" = $null; "Username" = $environmentUsername; "Password" = $environmentPassword; "PropertyBag" = @{"Bag" = @{ "Microsoft-Vslabs-MG-Resource-FQDN" = @{"IsSecure" = $false; "Data" = $validMachineName1}; "Username" = @{"IsSecure" = $false; "Data" = $environmentUsername}; "Password" = @{"IsSecure" = $false; "Data" = $environmentPassword}}}} +$validResource2 = @{"Id" = $validMachineId2; "Name" = $validMachineName2; "Type" = $null; "Username" = $environmentUsername; "Password" = $environmentPassword; "PropertyBag" = @{"Bag" = @{ "Microsoft-Vslabs-MG-Resource-FQDN" = @{"IsSecure" = $false; "Data" = $validMachineName1}; "Username" = @{"IsSecure" = $false; "Data" = $environmentUsername}; "Password" = @{"IsSecure" = $false; "Data" = $environmentPassword}}}} +$resourceFailForCopy = @{"Id" = $machineIdForFailCopy; "Name" = $machineNamesForFailCopy; "Type" = $null; "Username" = $environmentUsername; "Password" = $environmentPassword; "PropertyBag" = @{"Bag" = @{ "Microsoft-Vslabs-MG-Resource-FQDN" = @{"IsSecure" = $false; "Data" = $validMachineName1}; "Username" = @{"IsSecure" = $false; "Data" = $environmentUsername}; "Password" = @{"IsSecure" = $false; "Data" = $environmentPassword}}}} +$resourceFailForDeploy = @{"Id" = $machineIdForFailDeploy; "Name" = $machineNamesForFailDeploy; "Type" = $null; "Username" = $environmentUsername; "Password" = $environmentPassword; "PropertyBag" = @{"Bag" = @{ "Microsoft-Vslabs-MG-Resource-FQDN" = @{"IsSecure" = $false; "Data" = $validMachineName1}; "Username" = @{"IsSecure" = $false; "Data" = $environmentUsername}; "Password" = @{"IsSecure" = $false; "Data" = $environmentPassword}}}} +$resourceFailForNoProperty = @{"Id" = $machineIdForNoResouceProperty; "Name" = $machineNamesForNoResouceProperty; "Type" = $null; "Username" = $environmentUsername; "Password" = $environmentPassword; "PropertyBag" = @{"Bag" = @{ "Microsoft-Vslabs-MG-Resource-FQDN" = @{"IsSecure" = $false; "Data" = $validMachineName1}; "Username" = @{"IsSecure" = $false; "Data" = $environmentUsername}; "Password" = @{"IsSecure" = $false; "Data" = $environmentPassword}}}} +$validResource1Duplicate = @{"Id" = $validMachineId1Duplicate; "Name" = $validMachineName1; "Type" = $null; "Username" = $environmentUsername; "Password" = $environmentPassword; "PropertyBag" = @{"Bag" = @{ "Microsoft-Vslabs-MG-Resource-FQDN" = @{"IsSecure" = $false; "Data" = $validMachineName1}; "Username" = @{"IsSecure" = $false; "Data" = $environmentUsername}; "Password" = @{"IsSecure" = $false; "Data" = $environmentPassword}}}} + +$validResources = New-Object 'System.Collections.Generic.List[System.Object]' +$validResources.Add($validResource1) +$validResources.Add($validResource2) + +$invalidResources = New-Object 'System.Collections.Generic.List[System.Object]' +$invalidResources.Add($resourceFailForDeploy) + +$validResourcesWithDuplicateResourceName = New-Object 'System.Collections.Generic.List[System.Object]' +$validResourcesWithDuplicateResourceName.Add($validResource1) +$validResourcesWithDuplicateResourceName.Add($validResource1Duplicate) + +# Resource Property Key Names +$resourceFQDNKeyName = 'Microsoft-Vslabs-MG-Resource-FQDN' +$resourceWinRMHttpPortKeyName = 'WinRM_Http' +$resourceWinRMHttpsPortKeyName = 'WinRM_Https' +$skipCACheckKeyName = 'Microsoft-Vslabs-MG-SkipCACheck' + +#Deployment Responses + +$passResponseForResource1 = @{"MachineName" = $validMachineName1; "Status" = $PassedStatus; "DeploymentLog" = $SuccessLog; "ServiceLog" = $null; "Error" = $null} +$passResponseForResource2 = @{"MachineName" = $validMachineName2; "Status" = $PassedStatus; "DeploymentLog" = $SuccessLog; "ServiceLog" = $null; "Error" = $null} +$failedResponseForCopy = @{"MachineName" = $machineNamesForFailCopy; "Status" = $FailedStatus; "DeploymentLog" = $FailedCopyLog; "ServiceLog" = $null; "Error" = @{"Message" = $FailedCopyError}} +$failedResponseForDeploy = @{"MachineName" = $machineNamesForFailDeploy; "Status" = $FailedStatus; "DeploymentLog" = $FailedDeployLog; "ServiceLog" = $null; "Error" = @{"Message" = $FailedDeployError}} +$JobPassResponse = @{"Status" = $PassedStatus; "DeploymentLog" = $SuccessLog; "ServiceLog" = $null; "Error" = $null} +$JobFailResponseForDeploy = @{"Status" = $FailedStatus; "DeploymentLog" = $FailedDeployLog; "ServiceLog" = $null; "Error" = $null} +$JobFailResponseForCopy = @{"Status" = $FailedStatus; "DeploymentLog" = $FailedCopyLog; "ServiceLog" = $null; "Error" = $null} + +#Jobs + +$Job1 = @{"Id" = "1"; "Status" = "Completed"} +$Job2 = @{"Id" = "2"; "Status" = "Completed"} +$Job3 = @{"Id" = "3"; "Status" = "Completed"} + +#SkipCA Key and value +$doSkipCACheckOption = '-SkipCACheck' + +#WindowsFileCopy Constants +$validSourcePackage = $testPath +$validApplicationPath = $testPath +$invalidSourcePath = "Invalid" +$invalidTargetPath = "`$env:abc\123" \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/Utility.ps1 b/Tasks/PowerShellOnTargetMachinesV1/Utility.ps1 new file mode 100644 index 000000000000..890e739554bd --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/Utility.ps1 @@ -0,0 +1,184 @@ +function Get-ResourceWinRmConfig +{ + param + ( + [string]$resourceName, + [int]$resourceId + ) + + $resourceProperties = @{} + + $winrmPortToUse = '' + $protocolToUse = '' + + if($protocol -eq "HTTPS") + { + $protocolToUse = $useHttpsProtocolOption + + Write-Verbose "Starting Get-EnvironmentProperty cmdlet call on environment name: $environmentName with resource id: $resourceId(Name : $resourceName) and key: $resourceWinRMHttpsPortKeyName" + $winrmPortToUse = Get-EnvironmentProperty -Environment $environment -Key $resourceWinRMHttpsPortKeyName -ResourceId $resourceId + Write-Verbose "Completed Get-EnvironmentProperty cmdlet call on environment name: $environmentName with resource id: $resourceId (Name : $resourceName) and key: $resourceWinRMHttpsPortKeyName" + + if([string]::IsNullOrWhiteSpace($winrmPortToUse)) + { + Write-Telemetry "Input_Validation" "WinRM HTTPS port not provided" + throw(Get-LocalizedString -Key "{0} port was not provided for resource '{1}'" -ArgumentList "WinRM HTTPS", $resourceName) + } + } + elseif($protocol -eq "HTTP") + { + $protocolToUse = $useHttpProtocolOption + + Write-Verbose "Starting Get-EnvironmentProperty cmdlet call on environment name: $environmentName with resource id: $resourceId(Name : $resourceName) and key: $resourceWinRMHttpPortKeyName" + $winrmPortToUse = Get-EnvironmentProperty -Environment $environment -Key $resourceWinRMHttpPortKeyName -ResourceId $resourceId + Write-Verbose "Completed Get-EnvironmentProperty cmdlet call on environment name: $environmentName with resource id: $resourceId(Name : $resourceName) and key: $resourceWinRMHttpPortKeyName" + + if([string]::IsNullOrWhiteSpace($winrmPortToUse)) + { + Write-Telemetry "Input_Validation" "WinRM HTTP port not provided" + throw(Get-LocalizedString -Key "{0} port was not provided for resource '{1}'" -ArgumentList "WinRM HTTP", $resourceName) + } + } + + elseif($environment.Provider -ne $null) # For standard environment provider will be null + { + Write-Verbose "`t Environment is not standard environment. Https port has higher precedence" + + Write-Verbose "Starting Get-EnvironmentProperty cmdlet call on environment name: $environmentName with resource id: $resourceId(Name : $resourceName) and key: $resourceWinRMHttpsPortKeyName" + $winrmHttpsPort = Get-EnvironmentProperty -Environment $environment -Key $resourceWinRMHttpsPortKeyName -ResourceId $resourceId + Write-Verbose "Completed Get-EnvironmentProperty cmdlet call on environment name: $environmentName with resource id: $resourceId (Name : $resourceName) and key: $resourceWinRMHttpsPortKeyName" + + if ([string]::IsNullOrEmpty($winrmHttpsPort)) + { + Write-Verbose "`t Resource: $resourceName does not have any winrm https port defined, checking for winrm http port" + + Write-Verbose "Starting Get-EnvironmentProperty cmdlet call on environment name: $environmentName with resource id: $resourceId(Name : $resourceName) and key: $resourceWinRMHttpPortKeyName" + $winrmHttpPort = Get-EnvironmentProperty -Environment $environment -Key $resourceWinRMHttpPortKeyName -ResourceId $resourceId + Write-Verbose "Completed Get-EnvironmentProperty cmdlet call on environment name: $environmentName with resource id: $resourceId(Name : $resourceName) and key: $resourceWinRMHttpPortKeyName" + + if ([string]::IsNullOrEmpty($winrmHttpPort)) + { + Write-Telemetry "Input_Validation" "WinRM port not available" + throw(Get-LocalizedString -Key "Resource: '{0}' does not have WinRM service configured. Configure WinRM service on the Azure VM Resources. Refer for more details '{1}'" -ArgumentList $resourceName, "https://aka.ms/azuresetup" ) + } + else + { + # if resource has winrm http port defined + $winrmPortToUse = $winrmHttpPort + $protocolToUse = $useHttpProtocolOption + } + } + else + { + # if resource has winrm https port opened + $winrmPortToUse = $winrmHttpsPort + $protocolToUse = $useHttpsProtocolOption + } + } + else + { + Write-Verbose "`t Environment is standard environment. Http port has higher precedence" + Write-Verbose "Starting Get-EnvironmentProperty cmdlet call on environment name: $environmentName with resource id: $resourceId(Name : $resourceName) and key: $resourceWinRMHttpPortKeyName" + $winrmHttpPort = Get-EnvironmentProperty -Environment $environment -Key $resourceWinRMHttpPortKeyName -ResourceId $resourceId + Write-Verbose "Completed Get-EnvironmentProperty cmdlet call on environment name: $environmentName with resource id: $resourceId(Name : $resourceName) and key: $resourceWinRMHttpPortKeyName" + + if ([string]::IsNullOrEmpty($winrmHttpPort)) + { + Write-Verbose "`t Resource: $resourceName does not have any winrm http port defined, checking for winrm https port" + + Write-Verbose "Starting Get-EnvironmentProperty cmdlet call on environment name: $environmentName with resource id: $resourceId(Name : $resourceName) and key: $resourceWinRMHttpsPortKeyName" + $winrmHttpsPort = Get-EnvironmentProperty -Environment $environment -Key $resourceWinRMHttpsPortKeyName -ResourceId $resourceId + Write-Verbose "Completed Get-EnvironmentProperty cmdlet call on environment name: $environmentName with resource id: $resourceId(Name : $resourceName) and key: $resourceWinRMHttpsPortKeyName" + + if ([string]::IsNullOrEmpty($winrmHttpsPort)) + { + Write-Telemetry "Input_Validation" "WinRM port not available" + throw(Get-LocalizedString -Key "Resource: '{0}' does not have WinRM service configured. Configure WinRM service on the Azure VM Resources. Refer for more details '{1}'" -ArgumentList $resourceName, "https://aka.ms/azuresetup" ) + } + else + { + # if resource has winrm https port defined + $winrmPortToUse = $winrmHttpsPort + $protocolToUse = $useHttpsProtocolOption + } + } + else + { + # if resource has winrm http port opened + $winrmPortToUse = $winrmHttpPort + $protocolToUse = $useHttpProtocolOption + } + } + + $resourceProperties.protocolOption = $protocolToUse + $resourceProperties.winrmPort = $winrmPortToUse + + return $resourceProperties; + +} + +function Get-SkipCACheckOption +{ + [CmdletBinding()] + Param + ( + [string]$environmentName + ) + + $skipCACheckOption = $doNotSkipCACheckOption + $skipCACheckKeyName = Get-SkipCACheckTagKey + + # get skipCACheck option from environment + Write-Verbose "Starting Get-EnvironmentProperty cmdlet call on environment name: $environmentName with key: $skipCACheckKeyName" + $skipCACheckBool = Get-EnvironmentProperty -Environment $environment -Key $skipCACheckKeyName + Write-Verbose "Completed Get-EnvironmentProperty cmdlet call on environment name: $environmentName with key: $skipCACheckKeyName" + + if ($skipCACheckBool -eq "true") + { + $skipCACheckOption = $doSkipCACheckOption + } + + return $skipCACheckOption +} + +function Get-ResourceConnectionDetails +{ + param([object]$resource) + + $resourceProperties = @{} + $resourceName = $resource.Name + $resourceId = $resource.Id + + Write-Verbose "Starting Get-EnvironmentProperty cmdlet call on environment name: $environmentName with resource id: $resourceId(Name : $resourceName) and key: $resourceFQDNKeyName" + $fqdn = Get-EnvironmentProperty -Environment $environment -Key $resourceFQDNKeyName -ResourceId $resourceId + Write-Verbose "Completed Get-EnvironmentProperty cmdlet call on environment name: $environmentName with resource id: $resourceId(Name : $resourceName) and key: $resourceFQDNKeyName" + + $winrmconfig = Get-ResourceWinRmConfig -resourceName $resourceName -resourceId $resourceId + $resourceProperties.fqdn = $fqdn + $resourceProperties.winrmPort = $winrmconfig.winrmPort + $resourceProperties.protocolOption = $winrmconfig.protocolOption + $resourceProperties.credential = Get-ResourceCredentials -resource $resource + $resourceProperties.displayName = $fqdn + ":" + $winrmconfig.winrmPort + + return $resourceProperties +} + +function Get-ResourcesProperties +{ + param([object]$resources) + + $skipCACheckOption = Get-SkipCACheckOption -environmentName $environmentName + [hashtable]$resourcesPropertyBag = @{} + + foreach ($resource in $resources) + { + $resourceName = $resource.Name + $resourceId = $resource.Id + Write-Verbose "Get Resource properties for $resourceName (ResourceId = $resourceId)" + $resourceProperties = Get-ResourceConnectionDetails -resource $resource + $resourceProperties.skipCACheckOption = $skipCACheckOption + $resourcesPropertyBag.add($resourceId, $resourceProperties) + } + + return $resourcesPropertyBag +} diff --git a/Tasks/PowerShellOnTargetMachinesV1/icon.png b/Tasks/PowerShellOnTargetMachinesV1/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f2b41e93d99fe7722bb12939f7a44b8a1e99b8cf GIT binary patch literal 783 zcmV+q1MvKbP)8Mr(=6=+r>iAo2wD(>pl%`x z%DRvOZ>%J!yol&V3%XGdyDAWakR&3yD7*|5ktpaQaJUc3v^52tn!22Gp0|q%R77{S z;evj5hx7jb&vV|7a}NCP5yiK9P75g>0|lE(mbj&=)ha200|@LwQ=TY#;i@7?S4FZk z?26usR}JO#G*+@FH!~>)x&>54kZU&3ait8aDSh(=u6DSXn2k_qHPT*JMplNJ2w&K!HG(5w6B(~+)P{6UvL`#hw0N>b88afB@1VWsx$fLQYbj_NV@5i|N+#3sj<0bG< zhI!|m#xokCwR?#0;_oMO2j=jOhR8Im{1`21k`JGM!~zFE*lzafJGjY=B%JVzDA6du zNXqC!4l7cMx>QIA$(RuGx~@M7N0ZACnVwi!XUM#-&6%~KCeQXI?ac{vltR)1+S@aq#NQ`P!W3B=$>QegTP&+8tK(WS0N{ N002ovPDHLkV1lAzX&e9m literal 0 HcmV?d00001 diff --git a/Tasks/PowerShellOnTargetMachinesV1/icon.svg b/Tasks/PowerShellOnTargetMachinesV1/icon.svg new file mode 100644 index 000000000000..0616374e1f25 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/icon.svg @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/Tasks/PowerShellOnTargetMachinesV1/make.json b/Tasks/PowerShellOnTargetMachinesV1/make.json new file mode 100644 index 000000000000..965dc675ec60 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/make.json @@ -0,0 +1,8 @@ +{ + "common": [ + { + "module": "../Common/Deployment/TelemetryHelper", + "type": "ps" + } + ] +} diff --git a/Tasks/PowerShellOnTargetMachinesV1/task.json b/Tasks/PowerShellOnTargetMachinesV1/task.json new file mode 100644 index 000000000000..eb1a372582ae --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/task.json @@ -0,0 +1,156 @@ +{ + "id": "3B5693D4-5777-4FEE-862A-BD2B7A374C68", + "name": "PowerShellOnTargetMachines", + "friendlyName": "PowerShell on Target Machines", + "description": "Execute PowerShell scripts on remote machine(s)", + "helpMarkDown": "[More Information](https://go.microsoft.com/fwlink/?linkid=627414)", + "category": "Deploy", + "visibility": [ + "Build", + "Release" + ], + "author": "Microsoft Corporation", + "version": { + "Major": 1, + "Minor": 0, + "Patch": 51 + }, + "minimumAgentVersion": "1.104.0", + "groups": [ + { + "name": "deployment", + "displayName": "Deployment", + "isExpanded": true + }, + { + "name": "advanced", + "displayName": "Advanced Options", + "isExpanded": false + } + ], + "inputs": [ + { + "name": "EnvironmentName", + "type": "multiLine", + "label": "Machines", + "defaultValue": "", + "required": true, + "helpMarkDown": "Provide a comma separated list of machine IP addresses or FQDNs along with ports. Port is defaulted based on the selected protocol.
Eg: dbserver.fabrikam.com,dbserver_int.fabrikam.com:5986,192.168.12.34:5986
Or provide output variable of other tasks. Eg: $(variableName)
If you are using HTTPS, name/IP of machine should match the CN in the certificate." + }, + { + "name": "AdminUserName", + "type": "string", + "label": "Admin Login", + "defaultValue": "", + "required": false, + "helpMarkDown": "Administrator login for the target machines." + }, + { + "name": "AdminPassword", + "type": "string", + "label": "Password", + "defaultValue": "", + "required": false, + "helpMarkDown": "Administrator password for the target machines.
It can accept variable defined in Build/Release definitions as '$(passwordVariable)'.
You may mark variable type as 'secret' to secure it." + }, + { + "name": "Protocol", + "type": "radio", + "label": "Protocol", + "required": false, + "defaultValue": "", + "options": { + "Http": "HTTP", + "Https": "HTTPS" + }, + "helpMarkDown": "Select the protocol to use for the WinRM connection with the machine(s). Default is HTTPS." + }, + { + "name": "TestCertificate", + "type": "boolean", + "label": "Test Certificate", + "defaultValue": "true", + "visibleRule": "Protocol = Https", + "required": false, + "helpMarkDown": "Select the option to skip validating the authenticity of the machine's certificate by a trusted certification authority. The parameter is required for the WinRM HTTPS protocol." + }, + { + "name": "ScriptPath", + "type": "string", + "label": "PowerShell Script", + "defaultValue": "", + "groupName": "deployment", + "required": true, + "helpMarkDown": "Location of the PowerShell script on the target machines or on a UNC path like C:\\BudgetIT\\Web\\Deploy\\Website.ps1" + }, + { + "name": "ScriptArguments", + "type": "string", + "label": "Script Arguments", + "defaultValue": "", + "groupName": "deployment", + "required": false, + "properties": { + "editorExtension": "ms.vss-services-azure.parameters-grid" + }, + "helpMarkDown": "Arguments for the PowerShell script. Can be ordinal parameters or named parameters like -testParam test" + }, + { + "name": "InitializationScriptPath", + "type": "string", + "label": "Initialization Script", + "defaultValue": "", + "groupName": "deployment", + "required": false, + "helpMarkDown": "Location of the data script for DSC on the target machines or on a UNC path like C:\\BudgetIT\\Web\\Deploy\\WebsiteConfiguration.ps1" + }, + { + "name": "SessionVariables", + "type": "multiLine", + "label": "Session Variables", + "defaultValue": "", + "required": false, + "groupName": "deployment", + "helpMarkDown": "Set common session variables for both the scripts. For example, $variable = value, $var1 = \"value, 123\"" + }, + { + "name": "RunPowershellInParallel", + "type": "boolean", + "label": "Run PowerShell in Parallel", + "defaultValue": "true", + "required": false, + "groupName": "advanced", + "helpMarkDown": "Setting it to true will run the PowerShell scripts in parallel on the target machines." + }, + { + "name": "ResourceFilteringMethod", + "type": "radio", + "label": "Select Machines By", + "required": false, + "defaultValue": "machineNames", + "options": { + "machineNames": "Machine Names", + "tags": "Tags" + }, + "groupName": "advanced", + "helpMarkDown": "Optionally, select a subset of machines either by providing machine names or tags." + }, + { + "name": "MachineNames", + "type": "string", + "label": "Filter Criteria", + "defaultValue": "", + "required": false, + "groupName": "advanced", + "helpMarkDown": "This input is valid only for machine groups or output variables and is not supported for flat list of machines yet. Provide a list of machines like, dbserver.fabrikam.com, webserver.fabrikam.com, 192.168.12.34, or tags like, Role:DB; OS:Win8.1. If multiple tags are provided, then the task will run in all the machines with the specified tags. The default is to run the task in all machines." + } + ], + "instanceNameFormat": "Run PowerShell on $(EnvironmentName)", + "execution": { + "PowerShell": { + "target": "$(currentDirectory)\\PowerShellOnTargetMachines.ps1", + "argumentFormat": "", + "workingDirectory": "$(currentDirectory)" + } + } +} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/task.loc.json b/Tasks/PowerShellOnTargetMachinesV1/task.loc.json new file mode 100644 index 000000000000..3cb94b414295 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/task.loc.json @@ -0,0 +1,156 @@ +{ + "id": "3B5693D4-5777-4FEE-862A-BD2B7A374C68", + "name": "PowerShellOnTargetMachines", + "friendlyName": "ms-resource:loc.friendlyName", + "description": "ms-resource:loc.description", + "helpMarkDown": "ms-resource:loc.helpMarkDown", + "category": "Deploy", + "visibility": [ + "Build", + "Release" + ], + "author": "Microsoft Corporation", + "version": { + "Major": 1, + "Minor": 0, + "Patch": 51 + }, + "minimumAgentVersion": "1.104.0", + "groups": [ + { + "name": "deployment", + "displayName": "ms-resource:loc.group.displayName.deployment", + "isExpanded": true + }, + { + "name": "advanced", + "displayName": "ms-resource:loc.group.displayName.advanced", + "isExpanded": false + } + ], + "inputs": [ + { + "name": "EnvironmentName", + "type": "multiLine", + "label": "ms-resource:loc.input.label.EnvironmentName", + "defaultValue": "", + "required": true, + "helpMarkDown": "ms-resource:loc.input.help.EnvironmentName" + }, + { + "name": "AdminUserName", + "type": "string", + "label": "ms-resource:loc.input.label.AdminUserName", + "defaultValue": "", + "required": false, + "helpMarkDown": "ms-resource:loc.input.help.AdminUserName" + }, + { + "name": "AdminPassword", + "type": "string", + "label": "ms-resource:loc.input.label.AdminPassword", + "defaultValue": "", + "required": false, + "helpMarkDown": "ms-resource:loc.input.help.AdminPassword" + }, + { + "name": "Protocol", + "type": "radio", + "label": "ms-resource:loc.input.label.Protocol", + "required": false, + "defaultValue": "", + "options": { + "Http": "HTTP", + "Https": "HTTPS" + }, + "helpMarkDown": "ms-resource:loc.input.help.Protocol" + }, + { + "name": "TestCertificate", + "type": "boolean", + "label": "ms-resource:loc.input.label.TestCertificate", + "defaultValue": "true", + "visibleRule": "Protocol = Https", + "required": false, + "helpMarkDown": "ms-resource:loc.input.help.TestCertificate" + }, + { + "name": "ScriptPath", + "type": "string", + "label": "ms-resource:loc.input.label.ScriptPath", + "defaultValue": "", + "groupName": "deployment", + "required": true, + "helpMarkDown": "ms-resource:loc.input.help.ScriptPath" + }, + { + "name": "ScriptArguments", + "type": "string", + "label": "ms-resource:loc.input.label.ScriptArguments", + "defaultValue": "", + "groupName": "deployment", + "required": false, + "properties": { + "editorExtension": "ms.vss-services-azure.parameters-grid" + }, + "helpMarkDown": "ms-resource:loc.input.help.ScriptArguments" + }, + { + "name": "InitializationScriptPath", + "type": "string", + "label": "ms-resource:loc.input.label.InitializationScriptPath", + "defaultValue": "", + "groupName": "deployment", + "required": false, + "helpMarkDown": "ms-resource:loc.input.help.InitializationScriptPath" + }, + { + "name": "SessionVariables", + "type": "multiLine", + "label": "ms-resource:loc.input.label.SessionVariables", + "defaultValue": "", + "required": false, + "groupName": "deployment", + "helpMarkDown": "ms-resource:loc.input.help.SessionVariables" + }, + { + "name": "RunPowershellInParallel", + "type": "boolean", + "label": "ms-resource:loc.input.label.RunPowershellInParallel", + "defaultValue": "true", + "required": false, + "groupName": "advanced", + "helpMarkDown": "ms-resource:loc.input.help.RunPowershellInParallel" + }, + { + "name": "ResourceFilteringMethod", + "type": "radio", + "label": "ms-resource:loc.input.label.ResourceFilteringMethod", + "required": false, + "defaultValue": "machineNames", + "options": { + "machineNames": "Machine Names", + "tags": "Tags" + }, + "groupName": "advanced", + "helpMarkDown": "ms-resource:loc.input.help.ResourceFilteringMethod" + }, + { + "name": "MachineNames", + "type": "string", + "label": "ms-resource:loc.input.label.MachineNames", + "defaultValue": "", + "required": false, + "groupName": "advanced", + "helpMarkDown": "ms-resource:loc.input.help.MachineNames" + } + ], + "instanceNameFormat": "ms-resource:loc.instanceNameFormat", + "execution": { + "PowerShell": { + "target": "$(currentDirectory)\\PowerShellOnTargetMachines.ps1", + "argumentFormat": "", + "workingDirectory": "$(currentDirectory)" + } + } +} \ No newline at end of file diff --git a/Tasks/PowerShellOnTargetMachinesV1/tsconfig.json b/Tasks/PowerShellOnTargetMachinesV1/tsconfig.json new file mode 100644 index 000000000000..79a868c8d1e3 --- /dev/null +++ b/Tasks/PowerShellOnTargetMachinesV1/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES6", + "module": "commonjs" + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/README.md b/Tasks/WindowsMachineFileCopyV1/README.md new file mode 100644 index 000000000000..05dfdc3a6e32 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/README.md @@ -0,0 +1,13 @@ +# Windows Machine File Copy Task +### Overview +The task is used to copy application files and other artifacts that are required to install the application on Windows Machines like PowerShell scripts, PowerShell-DSC modules etc. The task provides the ability to copy files to Windows Machines. The tasks uses RoboCopy, the command-line utility built for fast copying of data. + +### The different parameters of the task are explained below: + +* **Source**: The source of the files. As described above using pre-defined system variables like $(Build.Repository.LocalPath) make it easy to specify the location of the build on the Build Automation Agent machine. The variables resolve to the working folder on the agent machine, when the task is run on it. Wild cards like **\*.zip are not supported. +* **Machines**: Specify comma separated list of machine FQDNs/ip addresses along with port(optional). For example dbserver.fabrikam.com, dbserver_int.fabrikam.com:5986,192.168.34:5986. +* **Admin Login**: Domain/Local administrator of the target host. Format: <Domain or hostname>\ < Admin User>. +* **Password**: Password for the admin login. It can accept variable defined in Build/Release definitions as '$(passwordVariable)'. You may mark variable type as 'secret' to secure it. +* **Destination Folder**: The folder in the Windows machines where the files will be copied to. An example of the destination folder is c:\FabrikamFibre\Web. +* **Clean Target**: Checking this option will clean the destination folder prior to copying the files to it. +* **Copy Files in Parallel**: Checking this option will copy files to all the target machines in parallel, which can speed up the copying process. diff --git a/Tasks/WindowsMachineFileCopyV1/RoboCopyJob.ps1 b/Tasks/WindowsMachineFileCopyV1/RoboCopyJob.ps1 new file mode 100644 index 000000000000..a4ab6415f738 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/RoboCopyJob.ps1 @@ -0,0 +1,239 @@ +$CopyJob = { +param ( + [string]$fqdn, + [string]$sourcePath, + [string]$targetPath, + [object]$credential, + [string]$cleanTargetBeforeCopy, + [string]$additionalArguments + ) + + $sourcePath = $sourcePath.Trim().TrimEnd('\', '/') + $targetPath = $targetPath.Trim().TrimEnd('\', '/') + + $isFileCopy = Test-Path -Path $sourcePath -PathType Leaf + $doCleanUp = $cleanTargetBeforeCopy -eq "true" + + $sourceDirectory = $sourcePath + $filesToCopy = "" + if($isFileCopy) + { + $sourceDirectory = Split-Path $sourcePath + $filesToCopy = Split-Path $sourcePath -Leaf + } + + if(Test-Path "$env:AGENT_HOMEDIRECTORY\Agent\Worker") + { + Get-ChildItem $env:AGENT_HOMEDIRECTORY\Agent\Worker\*.dll | % { + [void][reflection.assembly]::LoadFrom( $_.FullName ) + Write-Verbose "Loading .NET assembly:`t$($_.name)" -Verbose + } + } + else + { + if(Test-Path "$env:AGENT_HOMEDIRECTORY\externals\vstshost") + { + [void][reflection.assembly]::LoadFrom("$env:AGENT_HOMEDIRECTORY\externals\vstshost\Microsoft.TeamFoundation.DistributedTask.Task.LegacySDK.dll") + } + } + + import-module "Microsoft.TeamFoundation.DistributedTask.Task.Common" + + function ThrowError + { + param( + [string]$errorMessage, + [string]$fqdn + ) + + $failMessage = "Copying failed for resource : $fqdn" + throw "$failMessage`n$errorMessage" + } + + function Validate-Null( + [string]$value, + [string]$variableName + ) + { + $value = $value.Trim() + if(-not $value) + { + ThrowError -errorMessage (Get-LocalizedString -Key "Parameter '{0}' cannot be null or empty." -ArgumentList $variableName) + } + } + + function Validate-Credential( + [object]$credential) + { + if($credential) + { + Validate-Null $credential.UserName "Username" + Validate-Null $credential.Password "Password" + } + else + { + ThrowError -errorMessage (Get-LocalizedString -Key "Parameter '{0}' cannot be null or empty." -ArgumentList "credential") + } + } + + function Get-DownLevelLogonName( + [string]$fqdn, + [string]$userName + ) + { + if($userName -like '.\*') { + $userName = $userName.replace(".\","\") + $userName = $fqdn+$userName + } + return $userName + } + + function Replace-First( + [string]$text, + [string]$search, + [string]$replace + ) + { + $pos = $text.IndexOf($search); + if ($pos -le 0) + { + return $text; + } + + return $text.Substring(0, $pos) + $replace + $text.Substring($pos + $search.Length); + } + + function Get-DestinationNetworkPath( + [string]$targetPath, + [string]$machineShare + ) + { + if(-not $machineShare) + { + return $targetPath + } + + $targetSpecificPath = Replace-First $targetPath ":" '$' + return [io.path]::Combine($machineShare, $targetSpecificPath) + } + + function Get-RoboCopyParameters( + [string]$additionalArguments, + [switch]$fileCopy, + [switch]$clean) + { + $robocopyParameters = "/COPY:DAT" + + if(-not $fileCopy.IsPresent) + { + if($clean.IsPresent) + { + $robocopyParameters += " /MIR" + } + else + { + $robocopyParameters += " /E" + } + } + + if (-not [string]::IsNullOrWhiteSpace($additionalArguments)) + { + $robocopyParameters += " $additionalArguments" + } + + return $robocopyParameters.Trim() + } + + function Get-MachineShare( + [string]$fqdn, + [string]$targetPath + ) + { + if([bool]([uri]$targetPath).IsUnc) + { + return $targetPath + } + if($fqdn) + { + return [IO.Path]::DirectorySeparatorChar + [IO.Path]::DirectorySeparatorChar + $fqdn + } + + return "" + } + + function Get-NetExeCommand + { + $netExePath = Join-Path -path (get-item env:\windir).value -ChildPath system32\net.exe + if(Test-Path $netExePath) + { + Write-Verbose "Found the net exe path $netExePath. Net command will be $netExePath" + return $netExePath + } + + Write-Verbose "Unable to get the path for net.exe. Net command will be 'net'" + return 'net' + } + + $machineShare = Get-MachineShare -fqdn $fqdn -targetPath $targetPath + $destinationNetworkPath = Get-DestinationNetworkPath -targetPath $targetPath -machineShare $machineShare + + Validate-Credential $credential + $userName = Get-DownLevelLogonName -fqdn $fqdn -userName $($credential.UserName) + $password = $($credential.Password) + + $netExeCommand = Get-NetExeCommand + + if($machineShare) + { + $command = "$netExeCommand use `"$machineShare`"" + if($userName) + { + $command += " /user:`"$userName`" `'$($password -replace "['`]", '$&$&')`'" + } + $command += " 2>&1" + + $dtl_mapOut = iex $command + if ($LASTEXITCODE -ne 0) + { + $errorMessage = (Get-LocalizedString -Key "Failed to connect to the path {0} with the user {1} for copying.`n" -ArgumentList $machineShare, $($credential.UserName)) + $dtl_mapOut + ThrowError -errorMessage $errorMessage -fqdn $fqdn + } + } + + try + { + if($isFileCopy -and $doCleanUp -and (Test-Path -path $destinationNetworkPath -pathtype container)) + { + Get-ChildItem -Path $destinationNetworkPath -Recurse -force | Remove-Item -force -recurse; + $output = Remove-Item -path $destinationNetworkPath -force -recurse 2>&1 + $err = $output | ?{$_.gettype().Name -eq "ErrorRecord"} + if($err) + { + Write-Verbose -Verbose "Error occurred while deleting the destination folder: $err" + } + } + + $robocopyParameters = Get-RoboCopyParameters -additionalArguments $additionalArguments -fileCopy:$isFileCopy -clean:$doCleanUp + + $command = "robocopy `"$sourceDirectory`" `"$destinationNetworkPath`" `"$filesToCopy`" $robocopyParameters" + Invoke-Expression $command + + if ($LASTEXITCODE -ge 8) + { + $errorMessage = Get-LocalizedString -Key "Copying failed. Consult the robocopy logs for more details." + ThrowError -errorMessage $errorMessage -fqdn $fqdn + } + else + { + $message = (Get-LocalizedString -Key "Copying recursively from {0} to {1} on machine {2} succeeded" -ArgumentList $sourcePath, $targetPath, $fqdn) + Write-Output $message + } + } + finally + { + if($machineShare) + { + $dtl_deleteMap = iex "$netExeCommand use `"$machineShare`" /D /Y"; + } + } +} diff --git a/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/de-de/resources.resjson b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/de-de/resources.resjson new file mode 100644 index 000000000000..797480ec4266 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/de-de/resources.resjson @@ -0,0 +1,26 @@ +{ + "loc.friendlyName": "Dateikopiervorgang auf Windows-Computer", + "loc.helpMarkDown": "[Weitere Informationen](https://go.microsoft.com/fwlink/?linkid=627415)", + "loc.description": "Dateien auf Remotecomputer kopieren", + "loc.instanceNameFormat": "Dateien kopieren aus $(SourcePath)", + "loc.group.displayName.advanced": "Erweiterte Optionen", + "loc.input.label.SourcePath": "Quelle", + "loc.input.help.SourcePath": "Der absolute Pfad des Quellordners oder der Quelldatei auf dem lokalen Computer oder eine UNC-Freigabe, z. B. \"c:\\fabrikamfiber\" oder \"\\\\\\\\fabrikamshare\\fabrikamfiber\".", + "loc.input.label.EnvironmentName": "Computer", + "loc.input.help.EnvironmentName": "Stellen Sie eine durch Kommas getrennte Liste der IP-Computeradressen oder FQDNs bereit.
Beispiel: \"dbserver.fabrikam.com,192.168.12.34\"
Sie können auch die Ausgabevariable anderer Tasks angeben. Beispiel: \"$(variableName)\"", + "loc.input.label.AdminUserName": "Administratoranmeldung", + "loc.input.help.AdminUserName": "Die Administratoranmeldung für die Zielcomputer.", + "loc.input.label.AdminPassword": "Kennwort", + "loc.input.help.AdminPassword": "Das Kennwort für die Administratoranmeldung für die Zielcomputer.
Es kann die Variable annehmen, die in Build-/Releasedefinitionen als\"$(passwordVariable)\" definiert wird.
Sie können den Variablentyp als \"secret\" markieren, um die Variable zu sichern. ", + "loc.input.label.TargetPath": "Zielordner", + "loc.input.help.TargetPath": "Der lokale Pfad auf den Zielcomputern oder ein UNC-Pfad, auf den zum Kopieren der Dateien aus der Quelle zugegriffen werden kann. Beispiel: \"d:\\fabrikam\" oder \"\\\\\\\\fabrikam\\Web\".", + "loc.input.label.CleanTargetBeforeCopy": "Ziel bereinigen", + "loc.input.help.CleanTargetBeforeCopy": "Wenn diese Option ausgewählt wird, wird der Zielordner vor dem Kopieren der Dateien bereinigt.", + "loc.input.label.CopyFilesInParallel": "Dateien parallel kopieren", + "loc.input.help.CopyFilesInParallel": "Wenn diese Option ausgewählt wird, werden Dateien parallel auf die Computer kopiert.", + "loc.input.label.AdditionalArguments": "Zusätzliche Argumente", + "loc.input.help.AdditionalArguments": "Zusätzliche Robocopy-Argumente, die beim Kopieren von Dateien angewendet werden. Beispiel: \"/min:33553332 /l\".", + "loc.input.label.ResourceFilteringMethod": "Computer auswählen nach", + "loc.input.label.MachineNames": "Filterkriterien", + "loc.input.help.MachineNames": "Diese Eingabe ist nur gültig für Computergruppen und wird noch nicht für flache Listen von Computern oder Ausgabevariablen unterstützt. Geben Sie eine Liste von Computern (z. B. \"dbserver.fabrikam.com\", \"webserver.fabrikam.com\", \"192.168.12.34\") oder Tags (z. B. \"Role:DB;OS:Win8.1\") an. Wenn mehrere Tags angegeben werden, wird der Task auf allen Computern mit den angegebenen Tags ausgeführt. Standardmäßig wird der Task auf allen Computern ausgeführt." +} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/en-US/resources.resjson b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/en-US/resources.resjson new file mode 100644 index 000000000000..04dbf771ba11 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/en-US/resources.resjson @@ -0,0 +1,26 @@ +{ + "loc.friendlyName": "Windows Machine File Copy", + "loc.helpMarkDown": "[More Information](https://go.microsoft.com/fwlink/?linkid=627415)", + "loc.description": "Copy files to remote machine(s)", + "loc.instanceNameFormat": "Copy files from $(SourcePath)", + "loc.group.displayName.advanced": "Advanced Options", + "loc.input.label.SourcePath": "Source", + "loc.input.help.SourcePath": "Absolute path of the source folder or file on the local machine, or a UNC Share like c:\\fabrikamfiber or \\\\\\\\fabrikamshare\\fabrikamfiber.", + "loc.input.label.EnvironmentName": "Machines", + "loc.input.help.EnvironmentName": "Provide a comma separated list of machine IP addresses or FQDNs.
Eg: dbserver.fabrikam.com,192.168.12.34
Or provide output variable of other tasks. Eg: $(variableName)", + "loc.input.label.AdminUserName": "Admin Login", + "loc.input.help.AdminUserName": "Administrator login for the target machines.", + "loc.input.label.AdminPassword": "Password", + "loc.input.help.AdminPassword": "Password for administrator login for the target machines.
It can accept variable defined in Build/Release definitions as '$(passwordVariable)'.
You may mark variable type as 'secret' to secure it. ", + "loc.input.label.TargetPath": "Destination Folder", + "loc.input.help.TargetPath": "Local Path on the target machines or an accessible UNC path for copying the files from the source like d:\\fabrikam or \\\\\\\\fabrikam\\Web.", + "loc.input.label.CleanTargetBeforeCopy": "Clean Target", + "loc.input.help.CleanTargetBeforeCopy": "Selecting it will clean the destination folder before copying the files.", + "loc.input.label.CopyFilesInParallel": "Copy Files in Parallel", + "loc.input.help.CopyFilesInParallel": "Selecting it will copy files in parallel to the machines.", + "loc.input.label.AdditionalArguments": "Additional Arguments", + "loc.input.help.AdditionalArguments": "Additional robocopy arguments that will be applied when copying files like, /min:33553332 /l.", + "loc.input.label.ResourceFilteringMethod": "Select Machines By", + "loc.input.label.MachineNames": "Filter Criteria", + "loc.input.help.MachineNames": "This input is valid only for machine groups and is not supported for flat list of machines or output variables yet. Provide a list of machines like, dbserver.fabrikam.com, webserver.fabrikam.com, 192.168.12.34, or tags like, Role:DB; OS:Win8.1. If multiple tags are provided, then the task will run in all the machines with the specified tags. The default is to run the task in all machines." +} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/es-es/resources.resjson b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/es-es/resources.resjson new file mode 100644 index 000000000000..4442b171323e --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/es-es/resources.resjson @@ -0,0 +1,26 @@ +{ + "loc.friendlyName": "Copia de archivo en equipo Windows", + "loc.helpMarkDown": "[Más información](https://go.microsoft.com/fwlink/?linkid=627415)", + "loc.description": "Copiar archivos en máquinas remotas", + "loc.instanceNameFormat": "Copiar archivos desde $(SourcePath)", + "loc.group.displayName.advanced": "Opciones avanzadas", + "loc.input.label.SourcePath": "Origen", + "loc.input.help.SourcePath": "Ruta de acceso absoluta del archivo o carpeta de origen en el equipo local, o un recurso compartido UNC, como c:\\fabrikamfiber o \\\\\\\\fabrikamshare\\fabrikamfiber.", + "loc.input.label.EnvironmentName": "Equipos", + "loc.input.help.EnvironmentName": "Proporcione una lista separada por comas de direcciones IP de equipos o nombres de dominio completos.
Ejemplo: dbserver.fabrikam.com,192.168.12.34
O bien proporcione la variable de salida de otras tareas. Ejemplo: $(nombreDeVariable)", + "loc.input.label.AdminUserName": "Inicio de sesión del administrador", + "loc.input.help.AdminUserName": "Inicio de sesión del administrador para los equipos de destino.", + "loc.input.label.AdminPassword": "Contraseña", + "loc.input.help.AdminPassword": "Contraseña de inicio de sesión del administrador para las máquinas de destino.
Admite la variable declarada en las definiciones de compilación o versión como \"$(passwordVariable)\".
Para proteger la variable, puede marcar el tipo como \"secret\". ", + "loc.input.label.TargetPath": "Carpeta de destino", + "loc.input.help.TargetPath": "Ruta de acceso local en los equipos de destino o una ruta de acceso UNC accesible donde copiar los archivos desde el origen, como d:\\fabrikam o \\\\\\\\fabrikam\\Web.", + "loc.input.label.CleanTargetBeforeCopy": "Limpiar destino", + "loc.input.help.CleanTargetBeforeCopy": "Si se selecciona, se borrará el contenido la carpeta de destino antes de copiar los archivos.", + "loc.input.label.CopyFilesInParallel": "Copiar archivos en paralelo", + "loc.input.help.CopyFilesInParallel": "Si se selecciona, los archivos se copiarán en paralelo en los equipos.", + "loc.input.label.AdditionalArguments": "Argumentos adicionales", + "loc.input.help.AdditionalArguments": "Argumentos adicionales de robocopy que se aplicarán al copiar archivos, como /min:33553332 /l.", + "loc.input.label.ResourceFilteringMethod": "Seleccionar máquinas por", + "loc.input.label.MachineNames": "Criterios de filtro", + "loc.input.help.MachineNames": "Esta entrada solo es válida para los grupos de máquinas y no se admite aún para una lista plana de máquinas o de variables de salida. Proporcione una lista de máquinas (como dbserver.fabrikam.com, webserver.fabrikam.com o 192.168.12.34) o etiquetas (como Role:DB; OS:Win8.1). Si se proporcionan varias etiquetas, la tarea se ejecutará en todas las máquinas que tengan las etiquetas especificadas. Para los grupos de recursos de Azure, proporcione el nombre de la máquina virtual, como ffweb o ffdb. La opción predeterminada es ejecutar la tarea en todas las máquinas." +} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/fr-fr/resources.resjson b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/fr-fr/resources.resjson new file mode 100644 index 000000000000..cae72cabe030 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/fr-fr/resources.resjson @@ -0,0 +1,26 @@ +{ + "loc.friendlyName": "Copie des fichiers de l'ordinateur Windows", + "loc.helpMarkDown": "[Plus d'informations](https://go.microsoft.com/fwlink/?linkid=627415)", + "loc.description": "Copier les fichiers sur les ordinateurs distants", + "loc.instanceNameFormat": "Copier les fichiers à partir de $(Source Path)", + "loc.group.displayName.advanced": "Options avancées", + "loc.input.label.SourcePath": "Source", + "loc.input.help.SourcePath": "Chemin d'accès absolu au dossier ou fichier source sur l'ordinateur local, ou partage UNC, par ex. c:\\fabrikamfiber ou \\\\\\\\fabrikamshare\\fabrikamfiber.", + "loc.input.label.EnvironmentName": "Ordinateurs", + "loc.input.help.EnvironmentName": "Indiquez une liste séparée par des virgules d'adresses IP ou de noms de domaine complets d'ordinateurs.
Exemple : dbserver.fabrikam.com,192.168.12.34
Ou indiquez une variable de sortie d'autres tâches. Exemple : $(variableName)", + "loc.input.label.AdminUserName": "Informations de connexion d'administrateur", + "loc.input.help.AdminUserName": "Informations de connexion d'administrateur pour les ordinateurs cibles.", + "loc.input.label.AdminPassword": "Mot de passe", + "loc.input.help.AdminPassword": "Mot de passe de la connexion de l'administrateur pour les machines cibles.
Il peut accepter une variable définie dans les définitions Build/Release sous la forme '$(passwordVariable)'.
Vous pouvez marquer le type de variable en tant que 'secret' pour renforcer sa sécurité. ", + "loc.input.label.TargetPath": "Dossier de destination", + "loc.input.help.TargetPath": "Chemin d'accès local sur les ordinateurs cibles ou chemin d'accès UNC accessible pour la copie des fichiers à partir de la source, par ex., d:\\fabrikam ou \\\\\\\\fabrikam\\Web.", + "loc.input.label.CleanTargetBeforeCopy": "Nettoyer la cible", + "loc.input.help.CleanTargetBeforeCopy": "Si la valeur est sélectionnée, le dossier de destination est nettoyé avant la copie des fichiers.", + "loc.input.label.CopyFilesInParallel": "Copier les fichiers en parallèle", + "loc.input.help.CopyFilesInParallel": "Si la valeur est sélectionnée, les fichiers sont copiés en parallèle sur les ordinateurs.", + "loc.input.label.AdditionalArguments": "Arguments supplémentaires", + "loc.input.help.AdditionalArguments": "Arguments robocopy supplémentaires qui seront appliqués lors de la copie des fichiers, par exemple /min:33553332 /l.", + "loc.input.label.ResourceFilteringMethod": "Sélectionner les machines par", + "loc.input.label.MachineNames": "Critères de filtre", + "loc.input.help.MachineNames": "Cette entrée est valide uniquement pour les groupes de machines. Elle n'est pas encore prise en charge pour une liste plate de machines ou de variables de sortie. Indiquez une liste de machines, par exemple dbserver.fabrikam.com, webserver.fabrikam.com, 192.168.12.34, ou utilisez des étiquettes telles que Role:DB; OS:Win8.1. Si plusieurs étiquettes sont indiquées, la tâche s'exécute sur toutes les machines correspondant aux étiquettes spécifiées. Par défaut, la tâche s'exécute sur toutes les machines." +} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/it-IT/resources.resjson b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/it-IT/resources.resjson new file mode 100644 index 000000000000..cfb722eecc25 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/it-IT/resources.resjson @@ -0,0 +1,26 @@ +{ + "loc.friendlyName": "Copia dei file del computer Windows", + "loc.helpMarkDown": "[Altre informazioni](https://go.microsoft.com/fwlink/?linkid=627415)", + "loc.description": "Consente di copiare file nei computer remoti", + "loc.instanceNameFormat": "Copia file da $(SourcePath)", + "loc.group.displayName.advanced": "Opzioni avanzate", + "loc.input.label.SourcePath": "Origine", + "loc.input.help.SourcePath": "Percorso assoluto della cartella o del file di origine nel computer locale oppure condivisione UNC, ad esempio c:\\fabrikamfiber o \\\\\\\\fabrikamshare\\fabrikamfiber.", + "loc.input.label.EnvironmentName": "Computer", + "loc.input.help.EnvironmentName": "Consente di specificare un elenco di indirizzi IP o FQDN separati da virgola.
Esempio: dbserver.fabrikam.com,192.168.12.34
In alternativa, consente di specificare la variabile di output di altre attività. Esempio: $(variableName)", + "loc.input.label.AdminUserName": "Account di accesso amministratore", + "loc.input.help.AdminUserName": "Account di accesso dell'amministratore per i computer di destinazione.", + "loc.input.label.AdminPassword": "Password", + "loc.input.help.AdminPassword": "Password dell'account di accesso dell'amministratore per i computer di destinazione.
Accetta la variabile definita nelle definizioni di compilazione/versione come '$(passwordVariable)'.
Per proteggerla, è possibile contrassegnare il tipo di variabile come 'secret'. ", + "loc.input.label.TargetPath": "Cartella di destinazione", + "loc.input.help.TargetPath": "Percorso locale nei computer di destinazione oppure percorso UNC accessibile per la copia dei file dall'origine, ad esempio u:\\fabrikam o \\\\\\\\fabrikam\\Web.", + "loc.input.label.CleanTargetBeforeCopy": "Pulisci destinazione", + "loc.input.help.CleanTargetBeforeCopy": "Se è selezionato, la cartella di destinazione verrà pulita prima della copia dei file.", + "loc.input.label.CopyFilesInParallel": "Copia file in parallelo", + "loc.input.help.CopyFilesInParallel": "Se è selezionato, i file verranno copiati in parallelo nei computer.", + "loc.input.label.AdditionalArguments": "Argomenti aggiuntivi", + "loc.input.help.AdditionalArguments": "Argomenti aggiuntivi di robocopy che verranno applicati durante la copia dei file, ad esempio /min:33553332 /l.", + "loc.input.label.ResourceFilteringMethod": "Seleziona computer per", + "loc.input.label.MachineNames": "Criteri di filtro", + "loc.input.help.MachineNames": "Questo input è valido solo per gruppi di computer e non è ancora supportato per elenchi semplici di computer o variabili di output. Consente di specificare un elenco di computer come dbserver.fabrikam.com, webserver.fabrikam.com, 192.168.12.34 o tag come Role:DB; OS:Win8.1. Se vengono specificati più tag, l'attività verrà eseguita in tutti i computer con i tag specificati. Con l'impostazione predefinita l'attività viene eseguita in tutti i computer." +} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/ja-jp/resources.resjson b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/ja-jp/resources.resjson new file mode 100644 index 000000000000..50740e3c0a67 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/ja-jp/resources.resjson @@ -0,0 +1,26 @@ +{ + "loc.friendlyName": "Windows コンピューターのファイル コピー", + "loc.helpMarkDown": "[詳細](https://go.microsoft.com/fwlink/?linkid=627415)", + "loc.description": "ファイルをリモート コンピューター (複数可) にコピーします", + "loc.instanceNameFormat": "ファイルを $(SourcePath) からコピーする", + "loc.group.displayName.advanced": "詳細設定のオプション", + "loc.input.label.SourcePath": "ソース", + "loc.input.help.SourcePath": "ローカル コンピューターまたは UNC 共有にあるソース フォルダーまたはファイルの絶対パス。たとえば、c:\\fabrikamfiber または \\\\\\\\fabrikamshare\\fabrikamfiber など。", + "loc.input.label.EnvironmentName": "コンピューター", + "loc.input.help.EnvironmentName": "コンピューターの IP アドレスまたは FQDN のコンマ区切り一覧を指定します。
例: dbserver.fabrikam.com,192.168.12.34
または他のタスクの出力変数を指定します。例: $(variableName)", + "loc.input.label.AdminUserName": "管理者ログイン", + "loc.input.help.AdminUserName": "ターゲット コンピューターの管理者ログイン。", + "loc.input.label.AdminPassword": "パスワード", + "loc.input.help.AdminPassword": "対象のコンピューターでの管理者ログインのパスワード。
ビルド/リリース定義で '$(passwordVariable)' として定義された変数を受け入れることができます。
変数タイプを 'シークレット' とマークして保護することもできます。", + "loc.input.label.TargetPath": "宛先フォルダー ", + "loc.input.help.TargetPath": "ソースからのファイルのコピー先となるターゲット コンピューター上のローカル パスまたはアクセス可能な UNC パス。たとえば、d:\\fabrikam または \\\\\\\\fabrikam\\Web など。", + "loc.input.label.CleanTargetBeforeCopy": "ターゲットの内容消去 ", + "loc.input.help.CleanTargetBeforeCopy": "これを選ぶと、ファイルをコピーする前に宛先フォルダーの内容を消去します。", + "loc.input.label.CopyFilesInParallel": "並列でのファイルのコピー", + "loc.input.help.CopyFilesInParallel": "これを選ぶと、複数のファイルを並列でコンピューターにコピーします。", + "loc.input.label.AdditionalArguments": "追加引数", + "loc.input.help.AdditionalArguments": "ファイルをコピーする場合に適用される追加の robocopy 引数 (/min:33553332 /l など)。", + "loc.input.label.ResourceFilteringMethod": "以下の条件でコンピューターを選択", + "loc.input.label.MachineNames": "フィルター条件", + "loc.input.help.MachineNames": "この入力はマシン グループにのみ使用でき、マシンのフラット リストまたは出力変数ではまだサポートされていません。マシンのリスト (dbserver.fabrikam.com, webserver.fabrikam.com, 192.168.12.34 など)、またはタグのリスト (Role:DB; OS:Win8.1 など) を指定します。複数のタグを指定した場合、指定されたタグを持つすべてのマシンでタスクが実行されます。既定では、タスクがすべてのマシンで実行されます。" +} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/ko-KR/resources.resjson b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/ko-KR/resources.resjson new file mode 100644 index 000000000000..2a7e94742546 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/ko-KR/resources.resjson @@ -0,0 +1,26 @@ +{ + "loc.friendlyName": "Windows 컴퓨터 파일 복사", + "loc.helpMarkDown": "[자세한 정보](https://go.microsoft.com/fwlink/?linkid=627415)", + "loc.description": "파일을 원격 컴퓨터에 복사", + "loc.instanceNameFormat": "$(SourcePath)에서 파일 복사", + "loc.group.displayName.advanced": "고급 옵션", + "loc.input.label.SourcePath": "소스", + "loc.input.help.SourcePath": "로컬 컴퓨터나 UNC 공유에 있는 소스 폴더 또는 파일의 절대 경로입니다(예: c:\\fabrikamfiber 또는 \\\\\\\\fabrikamshare\\fabrikamfiber).", + "loc.input.label.EnvironmentName": "컴퓨터", + "loc.input.help.EnvironmentName": "쉼표로 구분된 컴퓨터 IP 주소 또는 FQDN 목록을 제공하세요.
예: dbserver.fabrikam.com,192.168.12.34
또는 기타 작업의 출력 변수를 제공하세요. 예: $(variableName)", + "loc.input.label.AdminUserName": "관리자 로그인", + "loc.input.help.AdminUserName": "대상 컴퓨터의 관리자 암호입니다.", + "loc.input.label.AdminPassword": "암호", + "loc.input.help.AdminPassword": "대상 컴퓨터에 대한 관리자 로그인의 암호입니다.
빌드/릴리스 정의에 '$(passwordVariable)'(으)로 정의된 변수를 사용할 수 있습니다.
보호하기 위해 변수 형식을 'secret'으로 표시할 수 있습니다.", + "loc.input.label.TargetPath": "대상 폴더", + "loc.input.help.TargetPath": "소스에서 파일을 복사하기 위한 대상 컴퓨터 상의 로컬 경로 또는 액세스 가능한 UNC 경로입니다(예: d:\\fabrikam 또는 \\\\\\\\fabrikam\\Web).", + "loc.input.label.CleanTargetBeforeCopy": "클린 대상", + "loc.input.help.CleanTargetBeforeCopy": "선택하면 파일을 복사하기 전에 대상 폴더가 정리됩니다.", + "loc.input.label.CopyFilesInParallel": "파일을 동시에 복사", + "loc.input.help.CopyFilesInParallel": "선택하면 파일이 컴퓨터에 동시에 복사됩니다.", + "loc.input.label.AdditionalArguments": "추가 인수", + "loc.input.help.AdditionalArguments": "파일을 복사할 때 적용될 추가 robocopy 인수입니다(예: /min:33553332 /l).", + "loc.input.label.ResourceFilteringMethod": "컴퓨터 선택 기준", + "loc.input.label.MachineNames": "필터 조건", + "loc.input.help.MachineNames": "이 입력은 컴퓨터 그룹에만 유효하며 컴퓨터 또는 출력 변수의 단순 목록에는 아직 지원되지 않습니다. dbserver.fabrikam.com, webserver.fabrikam.com, 192.168.12.34 등과 같은 컴퓨터나 Role:DB; OS:Win8.1 등과 같은 태그 목록을 지정하세요 여러 태그를 지정하는 경우 지정된 태그가 포함된 모든 컴퓨터에서 작업이 실행됩니다. 기본값은 모든 컴퓨터에서 실행하는 것입니다." +} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/ru-RU/resources.resjson b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/ru-RU/resources.resjson new file mode 100644 index 000000000000..e5915773c48d --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/ru-RU/resources.resjson @@ -0,0 +1,26 @@ +{ + "loc.friendlyName": "Копирование файлов на компьютер Windows", + "loc.helpMarkDown": "[Подробнее...](https://go.microsoft.com/fwlink/?linkid=627415)", + "loc.description": "Копировать файлы на удаленные компьютеры", + "loc.instanceNameFormat": "Копировать файлы из $(SourcePath)", + "loc.group.displayName.advanced": "Дополнительные параметры", + "loc.input.label.SourcePath": "Источник", + "loc.input.help.SourcePath": "Абсолютный путь к исходной папке или файлу на локальном компьютере либо общий ресурс UNC, например c:\\fabrikamfiber или \\\\\\\\fabrikamshare\\fabrikamfiber.", + "loc.input.label.EnvironmentName": "Компьютеры", + "loc.input.help.EnvironmentName": "Укажите разделенный запятыми список IP-адресов компьютеров или полных доменных имен.
Например: dbserver.fabrikam.com,192.168.12.34
Или укажите выходную переменную из других задач. Например: $(variableName)", + "loc.input.label.AdminUserName": "Имя для входа администратора", + "loc.input.help.AdminUserName": "Имя для входа администратора для целевых компьютеров.", + "loc.input.label.AdminPassword": "Пароль", + "loc.input.help.AdminPassword": "Пароль учетной записи администратора для целевых компьютеров.
Он может принять переменную, заданную в определениях сборки или выпуска, такую как \"$(passwordVariable)\".
Вы можете пометить тип переменной как \"secret\", чтобы защитить ее. ", + "loc.input.label.TargetPath": "Папка назначения", + "loc.input.help.TargetPath": "Локальный путь на целевых компьютерах или доступный UNC-путь, куда копируются файлы из источника, например d:\\fabrikam или \\\\\\\\fabrikam\\Web.", + "loc.input.label.CleanTargetBeforeCopy": "Очистить целевую папку", + "loc.input.help.CleanTargetBeforeCopy": "Если задать это значение, перед копированием файлов целевая папка будет очищена.", + "loc.input.label.CopyFilesInParallel": "Копировать файлы параллельно", + "loc.input.help.CopyFilesInParallel": "Если задать это значение, файлы будут копироваться на компьютеры параллельно.", + "loc.input.label.AdditionalArguments": "Дополнительные аргументы", + "loc.input.help.AdditionalArguments": "Дополнительные аргументы robocopy, которые будут применены при копировании файлов, например /min:33553332 /l.", + "loc.input.label.ResourceFilteringMethod": "Выбор компьютеров по", + "loc.input.label.MachineNames": "Условия фильтра", + "loc.input.help.MachineNames": "Входные данные допустимы только для групп компьютеров и пока не поддерживаются для плоского списка компьютеров или выходных переменных. Укажите список компьютеров, например dbserver.fabrikam.com, webserver.fabrikam.com, 192.168.12.34, или теги, такие как Role:DB; OS:Win8.1. Если указано несколько тегов, задача будет выполняться на всех компьютерах с заданными тегами. По умолчанию задача выполняется на всех компьютерах." +} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/zh-CN/resources.resjson b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/zh-CN/resources.resjson new file mode 100644 index 000000000000..e243f5123ea4 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/zh-CN/resources.resjson @@ -0,0 +1,26 @@ +{ + "loc.friendlyName": "Windows 计算机文件复制", + "loc.helpMarkDown": "[详细信息](https://go.microsoft.com/fwlink/?linkid=627415)", + "loc.description": "将文件复制到远程计算机", + "loc.instanceNameFormat": "从 $(SourcePath)复制文件", + "loc.group.displayName.advanced": "高级选项", + "loc.input.label.SourcePath": "源", + "loc.input.help.SourcePath": "本地计算机上的源文件夹或文件的绝对路径或 UNC 共享,如 c:\\fabrikamfiber 或 \\\\\\\\fabrikamshare\\fabrikamfiber。", + "loc.input.label.EnvironmentName": "计算机", + "loc.input.help.EnvironmentName": "提供以逗号分隔的计算机 IP 地址或 FQDN 列表。
例如: dbserver.fabrikam.com,192.168.12.34
或者提供其他任务的输出变量。例如: $(variableName)", + "loc.input.label.AdminUserName": "管理员登录名", + "loc.input.help.AdminUserName": "目标计算机的管理员登录名。", + "loc.input.label.AdminPassword": "密码", + "loc.input.help.AdminPassword": "目标计算机的管理员登录密码。
可接受”生成/发布\"定义中定义为 \"$(passwordVariable)\" 的变量。
你可将变量类型标记为“机密”来进行保护。", + "loc.input.label.TargetPath": "目标文件夹", + "loc.input.help.TargetPath": "用于从源复制文件的目标计算机上的本地路径或可访问的 UNC 路径,如 d:\\fabrikam 或 \\\\\\\\fabrikam\\Web。", + "loc.input.label.CleanTargetBeforeCopy": "清理目标", + "loc.input.help.CleanTargetBeforeCopy": "选择它会在复制文件之前清理目标文件夹。", + "loc.input.label.CopyFilesInParallel": "并行复制文件", + "loc.input.help.CopyFilesInParallel": "选择它会将文件并行复制到计算机。", + "loc.input.label.AdditionalArguments": "其他参数", + "loc.input.help.AdditionalArguments": "复制文件时将应用的其他 robocopy 参数,如 /min:33553332 /l。", + "loc.input.label.ResourceFilteringMethod": "计算机选择依据", + "loc.input.label.MachineNames": "筛选条件", + "loc.input.help.MachineNames": "此输入仅对计算机组有效,且尚不受计算机或输出变量的简单列表支持。提供计算机列表(如 dbserver.fabrikam.com、webserver.fabrikam.com、 192.168.12.34)或标记列表(如 Role:DB; OS:Win8.1)。如果提供了多个标记,则任务将在具有指定标记的所有计算机中运行。默认为在所有计算机中运行任务。" +} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/zh-TW/resources.resjson b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/zh-TW/resources.resjson new file mode 100644 index 000000000000..0d919e159e84 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Strings/resources.resjson/zh-TW/resources.resjson @@ -0,0 +1,26 @@ +{ + "loc.friendlyName": "Windows 電腦檔案複製", + "loc.helpMarkDown": "[詳細資訊](https://go.microsoft.com/fwlink/?linkid=627415)", + "loc.description": "將檔案複製到遠端電腦", + "loc.instanceNameFormat": "複製 $(SourcePath) 中的檔案", + "loc.group.displayName.advanced": "進階選項", + "loc.input.label.SourcePath": "來源", + "loc.input.help.SourcePath": "本機電腦上之來源資料夾或檔案的絕對路徑或通用命名慣例 (UNC) 共用,例如 c:\\fabrikamfiber 或 \\\\\\\\fabrikamshare\\fabrikamfiber。", + "loc.input.label.EnvironmentName": "電腦", + "loc.input.help.EnvironmentName": "提供以逗號分隔,包含電腦 IP 位址或 FQDN 的清單。
例如: dbserver.fabrikam.com,192.168.12.34
或提供其他作業的輸出變數。例如: $(variableName)", + "loc.input.label.AdminUserName": "系統管理員登入", + "loc.input.help.AdminUserName": "目標電腦的系統管理員登入。", + "loc.input.label.AdminPassword": "密碼", + "loc.input.help.AdminPassword": "目標電腦的系統管理員登入密碼。
其可接受組建/發行定義中 '$(passwordVariable)' 這類形式的變數。
您可以將變數類型標示為 'secret' 加以保護。", + "loc.input.label.TargetPath": "目的資料夾", + "loc.input.help.TargetPath": "可用於從來源複製檔案之目標電腦上的本機路徑或可存取的 UNC 路徑,例如: d:\\fabrikam 或 \\\\\\\\fabrikam\\Web。", + "loc.input.label.CleanTargetBeforeCopy": "清除目標", + "loc.input.help.CleanTargetBeforeCopy": "選取此選項將會在複製檔案前先清除目的資料夾。", + "loc.input.label.CopyFilesInParallel": "平行複製檔案", + "loc.input.help.CopyFilesInParallel": "選取此選項會將檔案平行複製到電腦。", + "loc.input.label.AdditionalArguments": "其他引數", + "loc.input.help.AdditionalArguments": "複製檔案時所要套用的其他 robocopy 引數,例如 /min:33553332 /l。", + "loc.input.label.ResourceFilteringMethod": "選取電腦依據 ", + "loc.input.label.MachineNames": "篩選準則", + "loc.input.help.MachineNames": "此輸入只對電腦群組有效,電腦的簡單列表或輸出變數目前尚無法支援。請以 dbserver.fabrikam.com、webserver.fabrikam.com、192.168.12.34 等形式提供電腦清單,或以 Role:DB; OS:Win8.1 等形式提供標記清單。若提供多個標記,工作會在所有具有指定標記的電腦上執行。預設會在所有電腦上執行此工作。" +} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Tests/L0.ts b/Tasks/WindowsMachineFileCopyV1/Tests/L0.ts new file mode 100644 index 000000000000..eb7a4de9792e --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Tests/L0.ts @@ -0,0 +1,66 @@ +/// +/// +/// + + +import Q = require('q'); +import assert = require('assert'); +import path = require('path'); + +var psm = require('../../../Tests/lib/psRunner'); +var psr = null; + +describe('WindowsMachineFileCopy Suite', function () { + this.timeout(20000); + + before((done) => { + if (psm.testSupported()) { + psr = new psm.PSRunner(); + psr.start(); + } + + done(); + }); + + after(function () { + if (psr) { + psr.kill(); + } + }); + + if (psm.testSupported()) { + it('Throw error if Source Path is invalid or empty', (done) => { + psr.run(path.join(__dirname, 'L0ValidateSourcePath.ps1'), done); + }); + it('Throw error if Destination Path is invalid or empty', (done) => { + psr.run(path.join(__dirname, 'L0ValidateDestinationPath.ps1'), done); + }); + it('Should copy on local machine when no machine name is given', (done) => { + psr.run(path.join(__dirname, 'L0ShouldCopyOnLocalMachine.ps1'), done); + }); + it('Validate Get-ResourcesProperties Utility', (done) => { + psr.run(path.join(__dirname, 'L0GetResourcesProperties.ps1'), done); + }); + it('Validate Get-ResourceConnectionDetails Utility', (done) => { + psr.run(path.join(__dirname, 'L0GetResourceConnectionDetails.ps1'), done); + }); + it('Throw Error for invalid machine names', (done) => { + psr.run(path.join(__dirname, 'L0InvalidEnvironmentResource.ps1'), done); + }); + it('Throw Error if Copy Files To Target Machine fails', (done) => { + psr.run(path.join(__dirname, 'L0SequentialCopyFail.ps1'), done); + }); + it('Performs Sequential copy on all machines and works correctly for valid input', (done) => { + psr.run(path.join(__dirname, 'L0ValidInputSequentialCopy.ps1'), done); + }); + it('Performs Parallel copy on all machines and works correctly for valid input', (done) => { + psr.run(path.join(__dirname, 'L0ValidInputParallelCopy.ps1'), done); + }); + it('Throw error if job fails for resource in Parallel Copy', (done) => { + psr.run(path.join(__dirname, 'L0ParallelCopyFail.ps1'), done); + }); + } + else { + console.warn('Cannot run tests for WindowsMachineFileCopy on Non-Windows Platform'); + } +}); \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Tests/L0GetResourceConnectionDetails.ps1 b/Tasks/WindowsMachineFileCopyV1/Tests/L0GetResourceConnectionDetails.ps1 new file mode 100644 index 000000000000..8ea85d718211 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Tests/L0GetResourceConnectionDetails.ps1 @@ -0,0 +1,26 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 -Force + +. $PSScriptRoot\..\Utility.ps1 + +Register-Mock Get-ResourceCredentials { } + +Register-Mock Get-EnvironmentProperty { throw "No property invalidResourcePropertyKeyName found." } -ParametersEvaluator{$ResourceId -eq $machineIdForNoResouceProperty} + +Assert-Throws { + Get-ResourceConnectionDetails -envName "envName" -resource $resourceFailForNoProperty +} -MessagePattern "No property invalidResourcePropertyKeyName found." +Assert-WasCalled Get-EnvironmentProperty -Times 1 + +Register-Mock Get-EnvironmentProperty { return $validMachineName1 } -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId1} + +Get-ResourceConnectionDetails -envName "envName" -resource $validResource1 +Assert-WasCalled Get-EnvironmentProperty -Times 1 -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId1} + +Register-Mock Get-EnvironmentProperty { return $validMachineName2 } -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId2} + +Get-ResourceConnectionDetails -envName "envName" -resource $validResource2 +Assert-WasCalled Get-EnvironmentProperty -Times 1 -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId2} diff --git a/Tasks/WindowsMachineFileCopyV1/Tests/L0GetResourcesProperties.ps1 b/Tasks/WindowsMachineFileCopyV1/Tests/L0GetResourcesProperties.ps1 new file mode 100644 index 000000000000..5363781bbb9b --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Tests/L0GetResourcesProperties.ps1 @@ -0,0 +1,18 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 -Force + +. $PSScriptRoot\..\Utility.ps1 + +Register-Mock Get-ResourceCredentials { } + +Register-Mock Get-EnvironmentProperty { return $validMachineName2 } -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId2} +Register-Mock Get-EnvironmentProperty { return $validMachineName1 } -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId1} + +Get-ResourcesProperties -envName "envName" -resources $validResources + +Assert-WasCalled Get-EnvironmentProperty -Times 2 +Assert-WasCalled Get-EnvironmentProperty -Times 1 -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId1} +Assert-WasCalled Get-EnvironmentProperty -Times 1 -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId2} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Tests/L0InvalidEnvironmentResource.ps1 b/Tasks/WindowsMachineFileCopyV1/Tests/L0InvalidEnvironmentResource.ps1 new file mode 100644 index 000000000000..fe54db697967 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Tests/L0InvalidEnvironmentResource.ps1 @@ -0,0 +1,29 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 -Force +. $PSScriptRoot\MockHelper.ps1 -Force + +$invalidEnvironmentWithNoResource = "invalidEnvironmentWithNoResource" + +#TODO : Mocked Modules - Move to Common Test Folder + +Unregister-Mock Get-EnvironmentProperty +Register-Mock Get-EnvironmentProperty { } + +Register-Mock Get-EnvironmentResources { throw "No resources found" } -ParametersEvaluator{$EnvironmentName -eq $invalidEnvironmentWithNoResource} +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $invalidEnvironmentWithNoResource } -ParametersEvaluator{$EnvironmentName -eq $invalidEnvironmentWithNoResource} + +Assert-Throws { + & "$copyFilesToMachinesPath" -environmentName $invalidEnvironmentWithNoResource -machineNames $invalidInputMachineNames -sourcePath $validSourcePackage -targetPath $validApplicationPath -cleanTargetBeforeCopy $true -copyFilesInParallel $false +} -MessagePattern "No resources found" + +Assert-WasCalled Get-EnvironmentProperty -Times 0 + +Unregister-Mock Get-EnvironmentResources +Register-Mock Get-EnvironmentResources { return $validResources } -ParametersEvaluator {$EnvironmentName -eq $validEnvironmentName} + +& "$copyFilesToMachinesPath" -environmentName $validEnvironmentName -resourceFilteringMethod "tags" -machineNames "" -sourcePath $validSourcePackage -targetPath $validApplicationPath -cleanTargetBeforeCopy $true -copyFilesInParallel $false + +Assert-WasCalled Get-EnvironmentResources -Times 1 -ParametersEvaluator {$validEnvironmentName -eq $validEnvironmentName} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Tests/L0ParallelCopyFail.ps1 b/Tasks/WindowsMachineFileCopyV1/Tests/L0ParallelCopyFail.ps1 new file mode 100644 index 000000000000..82682cd4b024 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Tests/L0ParallelCopyFail.ps1 @@ -0,0 +1,37 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 -Force +. $PSScriptRoot\MockHelper.ps1 -Force + +Register-Mock Get-EnvironmentResources { return $validResources } -ParametersEvaluator{$EnvironmentName -eq $EnvironmentNameForFailedJob} + +Register-Mock Get-EnvironmentProperty { return $validMachineName1 } -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId1} +Register-Mock Get-EnvironmentProperty { return $validMachineName2 } -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId2} + +Register-Mock Receive-Job { } -ParametersEvaluator { $Id -eq 1 } +Register-Mock Receive-Job { throw "Copy to one or more machines failed." } -ParametersEvaluator { $Id -eq 2 } + +#Start-Job Register-Mocks +Register-Mock Start-Job { $testJobs.Add($Job1); return $job1} -ParametersEvaluator{$ArgumentList -contains $validResource1.Name } +Register-Mock Start-Job { $testJobs.Add($Job2); return $job2} -ParametersEvaluator{$ArgumentList -contains $validResource1Duplicate.Name -and $ArgumentList -contains $environmentWinRMHttpPortForDuplicateResource } +Register-Mock Start-Job { $testJobs.Add($Job2); return $job2} -ParametersEvaluator{$ArgumentList -contains $validResource2.Name } + +#Get-Job Register-Mocks +Register-Mock Get-Job { return $testJobs } + +#Start-Sleep Register-Mocks +Register-Mock Start-Sleep { } + +#Remove-Job Register-Mocks +Register-Mock Remove-Job { $testJobs.RemoveAt(0) } + +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $EnvironmentNameForFailedJob } -ParametersEvaluator{$EnvironmentName -eq $EnvironmentNameForFailedJob} + +#Import-Module Register-Mocks +Register-Mock Import-Module { } + +Assert-Throws { + & "$copyFilesToMachinesPath" -environmentName $EnvironmentNameForFailedJob -machineNames $validMachineNames -sourcePath $validSourcePackage -targetPath $validApplicationPath -cleanTargetBeforeCopy $false -copyFilesInParallel $true +} -MessagePattern "Copy to one or more machines failed." diff --git a/Tasks/WindowsMachineFileCopyV1/Tests/L0SequentialCopyFail.ps1 b/Tasks/WindowsMachineFileCopyV1/Tests/L0SequentialCopyFail.ps1 new file mode 100644 index 000000000000..58c512278f6d --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Tests/L0SequentialCopyFail.ps1 @@ -0,0 +1,21 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 -Force +. $PSScriptRoot\MockHelper.ps1 -Force + + +$invalidEnvironmentWithNoResource = "invalidEnvironmentWithNoResource" + +Register-Mock Get-EnvironmentProperty { return $validResources } -ParametersEvaluator {$EnvironmentName -eq $invalidEnvironmentNameForFailCopy} +Register-Mock Get-EnvironmentResources { return $resourceFailForCopy } -ParametersEvaluator {$EnvironmentName -eq $invalidEnvironmentNameForFailCopy} + +Unregister-Mock Invoke-Command +Register-Mock Invoke-Command { throw "$FailedCopyError" } + +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $invalidEnvironmentNameForFailCopy } -ParametersEvaluator{$EnvironmentName -eq $invalidEnvironmentNameForFailCopy} + +Assert-Throws { + & "$copyFilesToMachinesPath" -environmentName $invalidEnvironmentNameForFailCopy -machineNames $validMachineNames -sourcePath $validSourcePackage -targetPath $validApplicationPath -cleanTargetBeforeCopy $true -copyFilesInParallel $false +} -MessagePattern "$FailedCopyError" diff --git a/Tasks/WindowsMachineFileCopyV1/Tests/L0ShouldCopyOnLocalMachine.ps1 b/Tasks/WindowsMachineFileCopyV1/Tests/L0ShouldCopyOnLocalMachine.ps1 new file mode 100644 index 000000000000..ac6a298c7aff --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Tests/L0ShouldCopyOnLocalMachine.ps1 @@ -0,0 +1,18 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 -Force + +. $PSScriptRoot\..\Utility.ps1 +. $PSScriptRoot\..\WindowsMachineFileCopyJob.ps1 + +Register-Mock Invoke-Command { } + +Copy-OnLocalMachine -sourcePath $validSourcePackage -targetPath $validApplicationPath -adminUserName $userName -adminPassword $password ` + -cleanTargetBeforeCopy $true -additionalArguments "" + +Assert-WasCalled Invoke-Command -Times 1 -ParametersEvaluator { + $ScriptBlock -eq $CopyJob -and $ArgumentList -contains $validSourcePackage -and $ArgumentList -contains $validApplicationPath -and ` + $ArgumentList -contains $true +} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Tests/L0ValidInputParallelCopy.ps1 b/Tasks/WindowsMachineFileCopyV1/Tests/L0ValidInputParallelCopy.ps1 new file mode 100644 index 000000000000..2fb6dc1e188a --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Tests/L0ValidInputParallelCopy.ps1 @@ -0,0 +1,36 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 -Force +. $PSScriptRoot\MockHelper.ps1 -Force + +Register-Mock Get-EnvironmentResources { return $validResources } -ParametersEvaluator {$EnvironmentName -eq $validEnvironmentName} + +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $validEnvironmentName } -ParametersEvaluator{$EnvironmentName -eq $validEnvironmentName} + + +Register-Mock Get-EnvironmentProperty { return $validMachineName1 } -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId1} +Register-Mock Get-EnvironmentProperty { return $validMachineName2 } -ParametersEvaluator {$Key -eq $resourceFQDNKeyName -and $ResourceId -eq $validMachineId2} + +#Start-Job Register-Mocks +Register-Mock Start-Job { $testJobs.Add($Job1); return $job1} -ParametersEvaluator{$ArgumentList -contains $validResource1.Name } +Register-Mock Start-Job { $testJobs.Add($Job2); return $job2} -ParametersEvaluator{$ArgumentList -contains $validResource2.Name } + +#Get-Job Register-Mocks +Register-Mock Get-Job { return $testJobs } + +#Start-Sleep Register-Mocks +Register-Mock Start-Sleep { } + +#Receive-Job Register-Mocks +Register-Mock Receive-Job { return $JobPassResponse} + +#Remove-Job Register-Mocks +Register-Mock Remove-Job { $testJobs.RemoveAt(0) } + +#Import-Module Register-Mocks +Register-Mock Import-Module { } + +#should not throw error +& "$copyFilesToMachinesPath" -environmentName $validEnvironmentName -machineNames $validMachineNames -sourcePath $validSourcePackage -targetPath $validApplicationPath -cleanTargetBeforeCopy $true -copyFilesInParallel $true \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Tests/L0ValidInputSequentialCopy.ps1 b/Tasks/WindowsMachineFileCopyV1/Tests/L0ValidInputSequentialCopy.ps1 new file mode 100644 index 000000000000..5c0ef0f9b0a3 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Tests/L0ValidInputSequentialCopy.ps1 @@ -0,0 +1,14 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 -Force +. $PSScriptRoot\MockHelper.ps1 -Force + +Register-Mock Register-Environment { return GetEnvironmentWithStandardProvider $validEnvironmentName } -ParametersEvaluator {$EnvironmentName -eq $validEnvironmentName} +Register-Mock Get-EnvironmentResources { return $validResources } -ParametersEvaluator {$EnvironmentName -eq $validEnvironmentName} +Register-Mock Get-EnvironmentProperty { return $validResources } -ParametersEvaluator {$EnvironmentName -eq $validEnvironmentName} + +& "$copyFilesToMachinesPath" -environmentName $validEnvironmentName -machineNames $validMachineNames -sourcePath $validSourcePackage -targetPath $validApplicationPath -cleanTargetBeforeCopy $true -copyFilesInParallel $false + +Assert-WasCalled Invoke-Command -Times 2 \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Tests/L0ValidateDestinationPath.ps1 b/Tasks/WindowsMachineFileCopyV1/Tests/L0ValidateDestinationPath.ps1 new file mode 100644 index 000000000000..8923c3146e73 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Tests/L0ValidateDestinationPath.ps1 @@ -0,0 +1,17 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\MockVariable.ps1 -Force + +. $PSScriptRoot\..\Utility.ps1 + +Register-Mock Get-ResourceFQDNTagKey { return $validResourceFQDNKeyName } + +Assert-Throws { + Validate-DestinationPath -value "" -environmentName $validEnvironmentName +} -Message "Parameter 'targetPath' cannot be null or empty." + +Assert-Throws { + Validate-DestinationPath -value $invalidTargetPath -environmentName $validEnvironmentName +} -Message "Remote destination path '$invalidTargetPath' cannot contain environment variables." diff --git a/Tasks/WindowsMachineFileCopyV1/Tests/L0ValidateSourcePath.ps1 b/Tasks/WindowsMachineFileCopyV1/Tests/L0ValidateSourcePath.ps1 new file mode 100644 index 000000000000..cc9b11981aa1 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Tests/L0ValidateSourcePath.ps1 @@ -0,0 +1,16 @@ +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\..\Utility.ps1 + +$invalidSourcePath = "Invalid" +Register-Mock Test-Path { return $false } -ParametersEvaluator { $LiteralPath -eq $invalidSourcePath } + +Assert-Throws { + Validate-SourcePath -value "" +} -MessagePattern "Parameter 'sourcePath' cannot be null or empty." + +Assert-Throws { + Validate-SourcePath -value "$invalidSourcePath" +} -MessagePattern "Source path '$invalidSourcePath' does not exist." \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Tests/MockHelper.ps1 b/Tasks/WindowsMachineFileCopyV1/Tests/MockHelper.ps1 new file mode 100644 index 000000000000..06185773b3ce --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Tests/MockHelper.ps1 @@ -0,0 +1,13 @@ +function GetEnvironmentWithStandardProvider( + [string]$environmentName) +{ + return @{ "Name" = $environmentName } +} + +Register-Mock Test-Path { return $true } -ParametersEvaluator{ $LiteralPath -eq $validSourcePackage } +Register-Mock Test-Path { return $false } -ParametersEvaluator { $LiteralPath -eq $invalidSourcePath } +Register-Mock Get-ResourceFQDNTagKey { return $validResourceFQDNKeyName } +Register-Mock Get-VssConnection { return $null } + +Register-Mock Invoke-Command { } +Register-Mock Get-ResourceCredentials { } diff --git a/Tasks/WindowsMachineFileCopyV1/Tests/MockVariable.ps1 b/Tasks/WindowsMachineFileCopyV1/Tests/MockVariable.ps1 new file mode 100644 index 000000000000..3d3c27eaa2ca --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Tests/MockVariable.ps1 @@ -0,0 +1,126 @@ +# Input Parameters +$validResourceFQDNKeyName = 'Microsoft-Vslabs-MG-Resource-FQDN' +$validResourceWinRMHttpPortKeyName = 'WinRM_Http' +$validResourceWinRMHttpsPortKeyName = 'WinRM_Https' +$validSkipCACheckKeyName = 'Microsoft-Vslabs-MG-SkipCACheck' + +$validEnvironmentName = "Test" +$validEnvironmentNameWithNoVm = "validEnvironmentNameWithNoVm" +$validEnvironmentNameWithDuplicateResourceName = "validEnvironmentNameWithDuplicateVmName" +$invalidEnvironmentWithNoResource = "invalidEnvironmentWithNoResource" + +$validMachineName1 = "Test1" +$validMachineName2 = "Test2" +$validMachineId1 = "18" +$validMachineId2 = "19" +$validMachineId1Duplicate = "23" +$emptyInputMachineName = "" +$validMachineNames = $validMachineName1 + ", " + $validMachineName2 +$testPath = Join-Path $env:windir "Test" +$powershellScriptPath = Join-Path $testPath 'powershell.ps1' +$validScriptPath = $powershellScriptPath +$validInitializationScriptPath = $powershellScriptPath +$assembly = New-Object System.Collections.Generic.List``1[System.Object] +$testJobs = New-Object System.Collections.Generic.List``1[System.Object] +$userName = "UserName" +$password = "Password" +$winRmProtocol = "HTTPS" + +#Invalid Input Parameters +$invalidInputEnvironmentName = "Invalid" +$invalidEnvironmentNameForFailCopy = "CopyFail" +$invalidEnvironmentNameForFailDeploy = "DeployFail" +$invalidEnvironmentNameWithNoUsername = "UsernameFail" +$invalidEnvironmentNameForNoResourceProperty = "CopyFailWithNoResourceProperty" +$environmentWithSkipCASet = "envWithSkipCAEnabled" +$environmentWithSkipCANotSet = "envWithSkipCADisabled" +$envWithBothProtocalsNotSet = "envWithBothProtocalsNotSet" +$EnvironmentNameForFailedJob = "JobFail" + +$machineNamesForFailCopy = "Test3" +$machineNamesForFailDeploy = "Test4" +$machineNamesForNoResouceProperty = "Test5" +$invalidInputMachineNames = "Invalid1" + +$machineIdForFailCopy = "20" +$machineIdForFailDeploy = "21" +$machineIdForNoResouceProperty = "22" + +# Environment Properties +$environmentWinRMHttpPort = '5985' +$environmentWinRMHttpPortForDuplicateResource = '5987' +$environmentWinRMHttpsPort = '5986' +$environmentUsername = "fareast\test" +$environmentPassword = "Password~1" + +# Environment / Resource operations +$environmentOperationId = [guid]::NewGuid().ToString() +$operationIdForResource1 = [guid]::NewGuid().ToString() +$operationIdForResource2 = [guid]::NewGuid().ToString() +$operationIdForResource3 = [guid]::NewGuid().ToString() +$operationIdForResource4 = [guid]::NewGuid().ToString() + +# Response Status +$FailedStatus = "Failed" +$PassedStatus = "Passed" + +# Response Logs +$SuccessLog = "Success Logs" +$FailedLog = "Failed Logs" +$FailedCopyLog = "Failed Copy Operation." +$FailedDeployLog = "Failed Deployment Operation." + + +# Response Error +$FailedError = "Operation Failed" +$FailedCopyError = $FailedCopyLog +$FailedDeployError = $FailedDeployLog + +# Resources +$emptyResourceList = @{} +$validResource1 = @{"Id" = $validMachineId1; "Name" = $validMachineName1; "Type" = $null; "Username" = $environmentUsername; "Password" = $environmentPassword; "PropertyBag" = @{"Bag" = @{ "Microsoft-Vslabs-MG-Resource-FQDN" = @{"IsSecure" = $false; "Data" = $validMachineName1}; "Username" = @{"IsSecure" = $false; "Data" = $environmentUsername}; "Password" = @{"IsSecure" = $false; "Data" = $environmentPassword}}}} +$validResource2 = @{"Id" = $validMachineId2; "Name" = $validMachineName2; "Type" = $null; "Username" = $environmentUsername; "Password" = $environmentPassword; "PropertyBag" = @{"Bag" = @{ "Microsoft-Vslabs-MG-Resource-FQDN" = @{"IsSecure" = $false; "Data" = $validMachineName1}; "Username" = @{"IsSecure" = $false; "Data" = $environmentUsername}; "Password" = @{"IsSecure" = $false; "Data" = $environmentPassword}}}} +$resourceFailForCopy = @{"Id" = $machineIdForFailCopy; "Name" = $machineNamesForFailCopy; "Type" = $null; "Username" = $environmentUsername; "Password" = $environmentPassword; "PropertyBag" = @{"Bag" = @{ "Microsoft-Vslabs-MG-Resource-FQDN" = @{"IsSecure" = $false; "Data" = $validMachineName1}; "Username" = @{"IsSecure" = $false; "Data" = $environmentUsername}; "Password" = @{"IsSecure" = $false; "Data" = $environmentPassword}}}} +$resourceFailForDeploy = @{"Id" = $machineIdForFailDeploy; "Name" = $machineNamesForFailDeploy; "Type" = $null; "Username" = $environmentUsername; "Password" = $environmentPassword; "PropertyBag" = @{"Bag" = @{ "Microsoft-Vslabs-MG-Resource-FQDN" = @{"IsSecure" = $false; "Data" = $validMachineName1}; "Username" = @{"IsSecure" = $false; "Data" = $environmentUsername}; "Password" = @{"IsSecure" = $false; "Data" = $environmentPassword}}}} +$resourceFailForNoProperty = @{"Id" = $machineIdForNoResouceProperty; "Name" = $machineNamesForNoResouceProperty; "Type" = $null; "Username" = $environmentUsername; "Password" = $environmentPassword; "PropertyBag" = @{"Bag" = @{ "Microsoft-Vslabs-MG-Resource-FQDN" = @{"IsSecure" = $false; "Data" = $validMachineName1}; "Username" = @{"IsSecure" = $false; "Data" = $environmentUsername}; "Password" = @{"IsSecure" = $false; "Data" = $environmentPassword}}}} +$validResource1Duplicate = @{"Id" = $validMachineId1Duplicate; "Name" = $validMachineName1; "Type" = $null; "Username" = $environmentUsername; "Password" = $environmentPassword; "PropertyBag" = @{"Bag" = @{ "Microsoft-Vslabs-MG-Resource-FQDN" = @{"IsSecure" = $false; "Data" = $validMachineName1}; "Username" = @{"IsSecure" = $false; "Data" = $environmentUsername}; "Password" = @{"IsSecure" = $false; "Data" = $environmentPassword}}}} + +$validResources = New-Object 'System.Collections.Generic.List[System.Object]' +$validResources.Add($validResource1) +$validResources.Add($validResource2) + +$validResourcesWithDuplicateResourceName = New-Object 'System.Collections.Generic.List[System.Object]' +$validResourcesWithDuplicateResourceName.Add($validResource1) +$validResourcesWithDuplicateResourceName.Add($validResource1Duplicate) + +# Resource Property Key Names +$resourceFQDNKeyName = 'Microsoft-Vslabs-MG-Resource-FQDN' +$resourceWinRMHttpPortKeyName = 'WinRM_Http' +$resourceWinRMHttpsPortKeyName = 'WinRM_Https' +$skipCACheckKeyName = 'Microsoft-Vslabs-MG-SkipCACheck' + +#Deployment Responses +$passResponseForResource1 = @{"MachineName" = $validMachineName1; "Status" = $PassedStatus; "DeploymentLog" = $SuccessLog; "ServiceLog" = $null; "Error" = $null} +$passResponseForResource2 = @{"MachineName" = $validMachineName2; "Status" = $PassedStatus; "DeploymentLog" = $SuccessLog; "ServiceLog" = $null; "Error" = $null} +$failedResponseForCopy = @{"MachineName" = $machineNamesForFailCopy; "Status" = $FailedStatus; "DeploymentLog" = $FailedCopyLog; "ServiceLog" = $null; "Error" = @{"Message" = $FailedCopyError}} +$failedResponseForDeploy = @{"MachineName" = $machineNamesForFailDeploy; "Status" = $FailedStatus; "DeploymentLog" = $FailedDeployLog; "ServiceLog" = $null; "Error" = @{"Message" = $FailedDeployError}} +$JobPassResponse = @{"Status" = $PassedStatus; "DeploymentLog" = $SuccessLog; "ServiceLog" = $null; "Error" = $null} +$JobFailResponseForDeploy = @{"Status" = $FailedStatus; "DeploymentLog" = $FailedDeployLog; "ServiceLog" = $null; "Error" = $null} +$JobFailResponseForCopy = @{"Status" = $FailedStatus; "DeploymentLog" = $FailedCopyLog; "ServiceLog" = $null; "Error" = $null} + +#Jobs +$Job1 = @{"Id" = "1"; "Status" = "Completed"} +$Job2 = @{"Id" = "2"; "Status" = "Completed"} +$Job3 = @{"Id" = "3"; "Status" = "Completed"} + +#SkipCA Key and value +$doSkipCACheckOption = '-SkipCACheck' + +#WindowsFileCopy Constants +$validSourcePackage = $testPath +$validApplicationPath = $testPath +$invalidSourcePath = "Invalid" +$invalidTargetPath = "`$env:abc\123" + +#path to WindowsMachineFileCopy.ps1 +$copyFilesToMachinesPath = "$PSScriptRoot\..\WindowsMachineFileCopy.ps1" \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/Utility.ps1 b/Tasks/WindowsMachineFileCopyV1/Utility.ps1 new file mode 100644 index 000000000000..f9a9ca2c8625 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/Utility.ps1 @@ -0,0 +1,105 @@ +function ThrowError +{ + param([string]$errorMessage) + + throw "$errorMessage" +} + +function Get-ResourceConnectionDetails +{ + param( + [string]$envName, + [object]$resource + ) + + $resourceProperties = @{} + + $resourceName = $resource.Name + $resourceId = $resource.Id + + Write-Verbose "`t`t Starting Get-EnvironmentProperty cmdlet call on environment name: $environmentName with resource id: $resourceId(Name : $resourceName) and key: $resourceFQDNKeyName" + $fqdn = Get-EnvironmentProperty -Environment $environment -Key $resourceFQDNKeyName -ResourceId $resourceId -ErrorAction Stop + Write-Verbose "`t`t Completed Get-EnvironmentProperty cmdlet call on environment name: $environmentName with resource id: $resourceId(Name : $resourceName) and key: $resourceFQDNKeyName" + + Write-Verbose "`t`t Resource fqdn - $fqdn" + + $resourceProperties.fqdn = $fqdn + $resourceProperties.credential = Get-ResourceCredentials -resource $resource + + return $resourceProperties +} + +function Get-ResourcesProperties +{ + param( + [string]$envName, + [object]$resources + ) + + [hashtable]$resourcesPropertyBag = @{} + + foreach ($resource in $resources) + { + $resourceName = $resource.Name + $resourceId = $resource.Id + Write-Verbose "Get Resource properties for $resourceName (ResourceId = $resourceId)" + + # Get other connection details for resource like - fqdn wirmport, http protocol, skipCACheckOption, resource credentials + + $resourceProperties = Get-ResourceConnectionDetails -envName $envName -resource $resource + + $resourcesPropertyBag.Add($resourceId, $resourceProperties) + } + return $resourcesPropertyBag +} + +function Validate-Null( + [string]$value, + [string]$variableName + ) +{ + $value = $value.Trim() + if(-not $value) + { + ThrowError -errorMessage (Get-LocalizedString -Key "Parameter '{0}' cannot be null or empty." -ArgumentList $variableName) + } +} + +function Validate-SourcePath( + [string]$value + ) +{ + Validate-Null -value $value -variableName "sourcePath" + + if(-not (Test-Path -LiteralPath $value)) + { + ThrowError -errorMessage (Get-LocalizedString -Key "Source path '{0}' does not exist." -ArgumentList $value) + } +} + +function Validate-DestinationPath( + [string]$value, + [string]$environmentName + ) +{ + Validate-Null -value $value -variableName "targetPath" + + if($environmentName -and $value.StartsWith("`$env:")) + { + ThrowError -errorMessage (Get-LocalizedString -Key "Remote destination path '{0}' cannot contain environment variables." -ArgumentList $value) + } +} +# $sourcePath, $targetPath, $credential, $cleanTargetBeforeCopy, $additionalArguments +# $adminUserName, $adminPassword +function Copy-OnLocalMachine( + [string] $sourcePath, + [string] $targetPath, + [string] $adminUserName, + [string] $adminPassword, + [string] $cleanTargetBeforeCopy, + [string] $additionalArguments + ) +{ + $credential = New-Object 'System.Net.NetworkCredential' -ArgumentList $adminUserName, $adminPassword + Invoke-Command -ScriptBlock $CopyJob -ArgumentList "", $sourcePath, $targetPath, $credential, $cleanTargetBeforeCopy, $additionalArguments +} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/WindowsMachineFileCopy.ps1 b/Tasks/WindowsMachineFileCopyV1/WindowsMachineFileCopy.ps1 new file mode 100644 index 000000000000..a8235e2a9963 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/WindowsMachineFileCopy.ps1 @@ -0,0 +1,122 @@ +param ( + [string]$environmentName, + [string]$adminUserName, + [string]$adminPassword, + [string]$resourceFilteringMethod, + [string]$machineNames, + [string]$sourcePath, + [string]$targetPath, + [string]$additionalArguments, + [string]$cleanTargetBeforeCopy, + [string]$copyFilesInParallel + ) + +Write-Verbose "Entering script WindowsMachineFileCopy.ps1" +Write-Verbose "environmentName = $environmentName" +Write-Verbose "adminUserName = $adminUserName" +Write-Verbose "resourceFilteringMethod = $resourceFilteringMethod" +Write-Verbose "machineNames = $machineNames" +Write-Verbose "sourcePath = $sourcePath" +Write-Verbose "targetPath = $targetPath" +Write-Verbose "additionalArguments = $additionalArguments" +Write-Verbose "copyFilesInParallel = $copyFilesInParallel" +Write-Verbose "cleanTargetBeforeCopy = $cleanTargetBeforeCopy" + +. $PSScriptRoot/RoboCopyJob.ps1 +. $PSScriptRoot/Utility.ps1 + +import-module "Microsoft.TeamFoundation.DistributedTask.Task.Common" +import-module "Microsoft.TeamFoundation.DistributedTask.Task.Internal" +import-module "Microsoft.TeamFoundation.DistributedTask.Task.DevTestLabs" +import-module "Microsoft.TeamFoundation.DistributedTask.Task.Deployment.Internal" -ErrorAction Ignore + +# keep machineNames parameter name unchanged due to back compatibility +$machineFilter = $machineNames +$sourcePath = $sourcePath.Trim('"') +$targetPath = $targetPath.Trim('"') + +# Default + constants # +$resourceFQDNKeyName = Get-ResourceFQDNTagKey + +$envOperationStatus = 'Passed' + +Validate-SourcePath $sourcePath +Validate-DestinationPath $targetPath $environmentName + +if([string]::IsNullOrWhiteSpace($environmentName)) +{ + Write-Verbose "No environment found. Copying to destination." + + Write-Output (Get-LocalizedString -Key "Copy started for - '{0}'" -ArgumentList $targetPath) + Copy-OnLocalMachine -sourcePath $sourcePath -targetPath $targetPath -adminUserName $adminUserName -adminPassword $adminPassword ` + -cleanTargetBeforeCopy $cleanTargetBeforeCopy -additionalArguments $additionalArguments + Write-Verbose "Files copied to destination successfully." +} +else +{ + + $connection = Get-VssConnection -TaskContext $distributedTaskContext + + Write-Verbose "Starting Register-Environment cmdlet call for environment : $environmentName with filter $machineFilter" + $environment = Register-Environment -EnvironmentName $environmentName -EnvironmentSpecification $environmentName -UserName $adminUserName -Password $adminPassword -Connection $connection -TaskContext $distributedTaskContext -ResourceFilter $machineFilter + Write-Verbose "Completed Register-Environment cmdlet call for environment : $environmentName" + + $fetchedEnvironmentName = $environment.Name + + Write-Verbose "Starting Get-EnvironmentResources cmdlet call on environment name: $fetchedEnvironmentName" + $resources = Get-EnvironmentResources -Environment $environment + Write-Verbose "Completed Get-EnvironmentResources cmdlet call for environment name: $fetchedEnvironmentName" + + if ($resources.Count -eq 0) + { + throw (Get-LocalizedString -Key "No machine exists under environment: '{0}' for deployment" -ArgumentList $environmentName) + } + + $resourcesPropertyBag = Get-ResourcesProperties -envName $fetchedEnvironmentName -resources $resources + + if($copyFilesInParallel -eq "false" -or ( $resources.Count -eq 1 )) + { + foreach($resource in $resources) + { + $resourceProperties = $resourcesPropertyBag.Item($resource.Id) + $machine = $resourceProperties.fqdn + + Write-Output (Get-LocalizedString -Key "Copy started for - '{0}'" -ArgumentList $machine) + + Invoke-Command -ScriptBlock $CopyJob -ArgumentList $machine, $sourcePath, $targetPath, $resourceProperties.credential, $cleanTargetBeforeCopy, $additionalArguments + } + } + else + { + [hashtable]$Jobs = @{} + + foreach($resource in $resources) + { + $resourceProperties = $resourcesPropertyBag.Item($resource.Id) + + $machine = $resourceProperties.fqdn + + Write-Output (Get-LocalizedString -Key "Copy started for - '{0}'" -ArgumentList $machine) + + $job = Start-Job -ScriptBlock $CopyJob -ArgumentList $machine, $sourcePath, $targetPath, $resourceProperties.credential, $cleanTargetBeforeCopy, $additionalArguments + + $Jobs.Add($job.Id, $resourceProperties) + } + + While ($Jobs.Count -gt 0) + { + Start-Sleep 10 + foreach($job in Get-Job) + { + if($Jobs.ContainsKey($job.Id) -and $job.State -ne "Running") + { + Receive-Job -Id $job.Id + Remove-Job $Job + $Jobs.Remove($job.Id) + } + } + } + } +} + +Write-Verbose "Leaving script WindowsMachineFileCopy.ps1" diff --git a/Tasks/WindowsMachineFileCopyV1/WindowsMachineFileCopyJob.ps1 b/Tasks/WindowsMachineFileCopyV1/WindowsMachineFileCopyJob.ps1 new file mode 100644 index 000000000000..09f7e8140e4b --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/WindowsMachineFileCopyJob.ps1 @@ -0,0 +1,38 @@ +$CopyJob = { +param ( + [string]$fqdn, + [string]$sourcePath, + [string]$targetPath, + [object]$credential, + [string]$cleanTargetBeforeCopy, + [string]$winRMPort, + [string]$httpProtocolOption, + [string]$skipCACheckOption + ) + + Get-ChildItem $env:AGENT_HOMEDIRECTORY\Agent\Worker\*.dll | % { + [void][reflection.assembly]::LoadFrom( $_.FullName ) + Write-Verbose "Loading .NET assembly:`t$($_.name)" + } + + Get-ChildItem $env:AGENT_HOMEDIRECTORY\Agent\Worker\Modules\Microsoft.TeamFoundation.DistributedTask.Task.DevTestLabs\*.dll | % { + [void][reflection.assembly]::LoadFrom( $_.FullName ) + Write-Verbose "Loading .NET assembly:`t$($_.name)" + } + + $cleanTargetPathOption = '' + if($cleanTargetBeforeCopy -eq "true") + { + $cleanTargetPathOption = '-CleanTargetPath' + } + + Write-Verbose "Initiating copy on $fqdn " + + [String]$copyFilesToTargetMachineBlockString = "Copy-FilesToTargetMachine -MachineDnsName $fqdn -SourcePath `$sourcePath -DestinationPath `$targetPath -Credential `$credential -WinRMPort $winRMPort $cleanTargetPathOption $skipCACheckOption $httpProtocolOption" + + [scriptblock]$copyFilesToTargetMachineBlock = [scriptblock]::Create($copyFilesToTargetMachineBlockString) + + $copyResponse = Invoke-Command -ScriptBlock $copyFilesToTargetMachineBlock + + Write-Output $copyResponse +} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/icon.png b/Tasks/WindowsMachineFileCopyV1/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1dde864d40ef3d45d71b5a1964eb33fbab313025 GIT binary patch literal 620 zcmV-y0+aoTP)`DL z365DL=pUh!5~)zyO>nHGi&Qk5e?TZXPEK$sL=c7Ybx3UV`rhS^yTm0wxZ&P=_ulWm zd*AmC9(m+{dq}%O^ zllgoug+gI75?_I6ki|SbY}+>Nb{l|vKF?q-YinQhdOcMnOyGr=#X`;J zTCIj*7ywU!KJfBE1uB&ajYeZFk>dJuxJrax(MqKf&1REGBm%I1Bk@W%K2Q!jDVNK{ zVle#^#EoeOA><5Vg|JRS#Nxm+@tOaQPfi*!02nt*l_PN&mA6JNc1zIFekbHQnE`+ZN} zh*F@WXgdH-0vTyg34Bnr9X_OhEB$_-WHRYGMpqYtaEP}b5&=Bd2@#P{OYtVg=igiR z@_rA#Ig;+JdK0QRN1d`dw3W}v&I{4b1^k57Ga};o6Z{2|U`nVbkgf0l0000 + + + + + + + + + + + + Data + + + sql-database-generic + + + + image/svg+xml + + + Amido Limited + + + Richard Slater + + + + + + + + + + + + + + + diff --git a/Tasks/WindowsMachineFileCopyV1/task.json b/Tasks/WindowsMachineFileCopyV1/task.json new file mode 100644 index 000000000000..330a15b91fd0 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/task.json @@ -0,0 +1,124 @@ +{ + "id": "731004D4-1D66-4F70-8C05-638018B22210", + "name": "WindowsMachineFileCopy", + "friendlyName": "Windows Machine File Copy", + "description": "Copy files to remote machine(s)", + "helpMarkDown": "[More Information](https://go.microsoft.com/fwlink/?linkid=627415)", + "category": "Deploy", + "visibility": [ + "Build", + "Release" + ], + "author": "Microsoft Corporation", + "version": { + "Major": 1, + "Minor": 0, + "Patch": 43 + }, + "minimumAgentVersion": "1.104.0", + "groups": [ + { + "name": "advanced", + "displayName": "Advanced Options", + "isExpanded": false + } + ], + "inputs": [ + { + "name": "SourcePath", + "type": "filePath", + "label": "Source", + "defaultValue": "", + "required": true, + "helpMarkDown": "Absolute path of the source folder or file on the local machine, or a UNC Share like c:\\fabrikamfiber or \\\\\\\\fabrikamshare\\fabrikamfiber." + }, + { + "name": "EnvironmentName", + "type": "multiLine", + "label": "Machines", + "defaultValue": "", + "required": false, + "helpMarkDown": "Provide a comma separated list of machine IP addresses or FQDNs.
Eg: dbserver.fabrikam.com,192.168.12.34
Or provide output variable of other tasks. Eg: $(variableName)" + }, + { + "name": "AdminUserName", + "type": "string", + "label": "Admin Login", + "defaultValue": "", + "required": false, + "helpMarkDown": "Administrator login for the target machines." + }, + { + "name": "AdminPassword", + "type": "string", + "label": "Password", + "defaultValue": "", + "required": false, + "helpMarkDown": "Password for administrator login for the target machines.
It can accept variable defined in Build/Release definitions as '$(passwordVariable)'.
You may mark variable type as 'secret' to secure it. " + }, + { + "name": "TargetPath", + "type": "string", + "label": "Destination Folder", + "defaultValue": "", + "required": true, + "helpMarkDown": "Local Path on the target machines or an accessible UNC path for copying the files from the source like d:\\fabrikam or \\\\\\\\fabrikam\\Web." + }, + { + "name": "CleanTargetBeforeCopy", + "type": "boolean", + "label": "Clean Target", + "defaultValue": "false", + "required": false, + "groupName": "advanced", + "helpMarkDown": "Selecting it will clean the destination folder before copying the files." + }, + { + "name": "CopyFilesInParallel", + "type": "boolean", + "label": "Copy Files in Parallel", + "defaultValue": "true", + "required": false, + "groupName": "advanced", + "helpMarkDown": "Selecting it will copy files in parallel to the machines." + }, + { + "name": "AdditionalArguments", + "type": "multiLine", + "label": "Additional Arguments", + "required": false, + "groupName": "advanced", + "defaultValue": "", + "helpMarkDown": "Additional robocopy arguments that will be applied when copying files like, /min:33553332 /l." + }, + { + "name": "ResourceFilteringMethod", + "type": "radio", + "label": "Select Machines By", + "required": false, + "defaultValue": "machineNames", + "groupName": "advanced", + "options": { + "machineNames": "Machine Names", + "tags": "Tags" + } + }, + { + "name": "MachineNames", + "type": "string", + "label": "Filter Criteria", + "defaultValue": "", + "groupName": "advanced", + "required": false, + "helpMarkDown": "This input is valid only for machine groups and is not supported for flat list of machines or output variables yet. Provide a list of machines like, dbserver.fabrikam.com, webserver.fabrikam.com, 192.168.12.34, or tags like, Role:DB; OS:Win8.1. If multiple tags are provided, then the task will run in all the machines with the specified tags. The default is to run the task in all machines." + } + ], + "instanceNameFormat": "Copy files from $(SourcePath)", + "execution": { + "PowerShell": { + "target": "$(currentDirectory)\\WindowsMachineFileCopy.ps1", + "argumentFormat": "", + "workingDirectory": "$(currentDirectory)" + } + } +} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/task.loc.json b/Tasks/WindowsMachineFileCopyV1/task.loc.json new file mode 100644 index 000000000000..3bb81168b784 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/task.loc.json @@ -0,0 +1,124 @@ +{ + "id": "731004D4-1D66-4F70-8C05-638018B22210", + "name": "WindowsMachineFileCopy", + "friendlyName": "ms-resource:loc.friendlyName", + "description": "ms-resource:loc.description", + "helpMarkDown": "ms-resource:loc.helpMarkDown", + "category": "Deploy", + "visibility": [ + "Build", + "Release" + ], + "author": "Microsoft Corporation", + "version": { + "Major": 1, + "Minor": 0, + "Patch": 43 + }, + "minimumAgentVersion": "1.104.0", + "groups": [ + { + "name": "advanced", + "displayName": "ms-resource:loc.group.displayName.advanced", + "isExpanded": false + } + ], + "inputs": [ + { + "name": "SourcePath", + "type": "filePath", + "label": "ms-resource:loc.input.label.SourcePath", + "defaultValue": "", + "required": true, + "helpMarkDown": "ms-resource:loc.input.help.SourcePath" + }, + { + "name": "EnvironmentName", + "type": "multiLine", + "label": "ms-resource:loc.input.label.EnvironmentName", + "defaultValue": "", + "required": false, + "helpMarkDown": "ms-resource:loc.input.help.EnvironmentName" + }, + { + "name": "AdminUserName", + "type": "string", + "label": "ms-resource:loc.input.label.AdminUserName", + "defaultValue": "", + "required": false, + "helpMarkDown": "ms-resource:loc.input.help.AdminUserName" + }, + { + "name": "AdminPassword", + "type": "string", + "label": "ms-resource:loc.input.label.AdminPassword", + "defaultValue": "", + "required": false, + "helpMarkDown": "ms-resource:loc.input.help.AdminPassword" + }, + { + "name": "TargetPath", + "type": "string", + "label": "ms-resource:loc.input.label.TargetPath", + "defaultValue": "", + "required": true, + "helpMarkDown": "ms-resource:loc.input.help.TargetPath" + }, + { + "name": "CleanTargetBeforeCopy", + "type": "boolean", + "label": "ms-resource:loc.input.label.CleanTargetBeforeCopy", + "defaultValue": "false", + "required": false, + "groupName": "advanced", + "helpMarkDown": "ms-resource:loc.input.help.CleanTargetBeforeCopy" + }, + { + "name": "CopyFilesInParallel", + "type": "boolean", + "label": "ms-resource:loc.input.label.CopyFilesInParallel", + "defaultValue": "true", + "required": false, + "groupName": "advanced", + "helpMarkDown": "ms-resource:loc.input.help.CopyFilesInParallel" + }, + { + "name": "AdditionalArguments", + "type": "multiLine", + "label": "ms-resource:loc.input.label.AdditionalArguments", + "required": false, + "groupName": "advanced", + "defaultValue": "", + "helpMarkDown": "ms-resource:loc.input.help.AdditionalArguments" + }, + { + "name": "ResourceFilteringMethod", + "type": "radio", + "label": "ms-resource:loc.input.label.ResourceFilteringMethod", + "required": false, + "defaultValue": "machineNames", + "groupName": "advanced", + "options": { + "machineNames": "Machine Names", + "tags": "Tags" + } + }, + { + "name": "MachineNames", + "type": "string", + "label": "ms-resource:loc.input.label.MachineNames", + "defaultValue": "", + "groupName": "advanced", + "required": false, + "helpMarkDown": "ms-resource:loc.input.help.MachineNames" + } + ], + "instanceNameFormat": "ms-resource:loc.instanceNameFormat", + "execution": { + "PowerShell": { + "target": "$(currentDirectory)\\WindowsMachineFileCopy.ps1", + "argumentFormat": "", + "workingDirectory": "$(currentDirectory)" + } + } +} \ No newline at end of file diff --git a/Tasks/WindowsMachineFileCopyV1/tsconfig.json b/Tasks/WindowsMachineFileCopyV1/tsconfig.json new file mode 100644 index 000000000000..79a868c8d1e3 --- /dev/null +++ b/Tasks/WindowsMachineFileCopyV1/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES6", + "module": "commonjs" + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file