diff --git a/src/VirtualClient/VirtualClient.Dependencies/PSCPInstallation.cs b/src/VirtualClient/VirtualClient.Dependencies/PSCPInstallation.cs
new file mode 100644
index 000000000..be7d72919
--- /dev/null
+++ b/src/VirtualClient/VirtualClient.Dependencies/PSCPInstallation.cs
@@ -0,0 +1,157 @@
+namespace VirtualClient.Dependencies
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.IO.Abstractions;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using global::VirtualClient;
+ using global::VirtualClient.Common;
+ using global::VirtualClient.Common.Extensions;
+ using global::VirtualClient.Common.Telemetry;
+ using global::VirtualClient.Contracts;
+ using Microsoft.Extensions.DependencyInjection;
+
+ ///
+ /// Installation component for the PSCP
+ ///
+ public class PSCPInstallation : VirtualClientComponent
+ {
+ private IFileSystem fileSystem;
+ private IPackageManager packageManager;
+ private ProcessManager processManager;
+ private ISystemManagement systemManagement;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// An enumeration of dependencies that can be used for dependency injection.
+ /// A series of key value pairs that dictate runtime execution.
+ public PSCPInstallation(IServiceCollection dependencies, IDictionary parameters)
+ : base(dependencies, parameters)
+ {
+ this.fileSystem = dependencies.GetService();
+ this.packageManager = dependencies.GetService();
+ this.processManager = dependencies.GetService();
+ this.systemManagement = dependencies.GetService();
+ this.SupportingExecutables = new List();
+ }
+
+ ///
+ /// The path to the pscp executable.
+ ///
+ public string ExecutablePath { get; set; }
+
+ ///
+ /// The path where the pscp JSON results file should be output.
+ ///
+ public string ResultsFilePath { get; set; }
+
+ ///
+ /// path to store pscp.exe
+ ///
+ public string PscpPath
+ {
+ get
+ {
+ return this.Parameters.GetValue(nameof(PSCPInstallation.PscpPath));
+ }
+ }
+
+ ///
+ /// A set of paths for supporting executables of the main process
+ /// (e.g. pscp_x86_64, pscp_aarch64). These typically need to
+ /// be cleaned up/terminated at the end of each round of processing.
+ ///
+ protected IList SupportingExecutables { get; }
+
+ ///
+ /// Executes Geek bench
+ ///
+ protected override async Task ExecuteAsync(EventContext telemetryContext, CancellationToken cancellationToken)
+ {
+ this.DeleteResultsFile(telemetryContext);
+
+ string commandLineArguments = string.Empty;
+
+ using (BackgroundOperations profiling = BackgroundOperations.BeginProfiling(this, cancellationToken))
+ {
+ string pscpPath = Path.Combine(this.PscpPath, "pscp.exe");
+ if (!Directory.Exists(this.PscpPath))
+ {
+ // If it doesn't exist, create dir
+ Directory.CreateDirectory(this.PscpPath);
+ }
+
+ await Task.Run(() =>
+ {
+ this.systemManagement.FileSystem.File.Copy(this.ExecutablePath, pscpPath);
+ });
+ }
+ }
+
+ ///
+ /// Initializes the environment and dependencies for running the pscp workload.
+ ///
+ protected override async Task InitializeAsync(EventContext telemetryContext, CancellationToken cancellationToken)
+ {
+ DependencyPath workloadPackage = await this.packageManager.GetPackageAsync(this.PackageName, CancellationToken.None);
+
+ if (workloadPackage == null)
+ {
+ throw new DependencyException(
+ $"The expected package '{this.PackageName}' does not exist on the system or is not registered.",
+ ErrorReason.WorkloadDependencyMissing);
+ }
+
+ workloadPackage = this.PlatformSpecifics.ToPlatformSpecificPath(workloadPackage, this.Platform, this.CpuArchitecture);
+
+ switch (this.PlatformArchitectureName)
+ {
+ case "win-x64":
+ this.ExecutablePath = this.PlatformSpecifics.Combine(workloadPackage.Path, "pscp.exe");
+ ConsoleLogger.Default.LogMessage($"win-x64 path {this.ExecutablePath}", telemetryContext);
+ this.SupportingExecutables.Add("pscp.exe");
+ break;
+
+ case "win-arm64":
+ this.ExecutablePath = this.PlatformSpecifics.Combine(workloadPackage.Path, "pscp.exe");
+ ConsoleLogger.Default.LogMessage($"win-arm64 path {this.ExecutablePath}", telemetryContext);
+ this.SupportingExecutables.Add("pscp.exe");
+ break;
+
+ case "win-x32":
+ this.ExecutablePath = this.PlatformSpecifics.Combine(workloadPackage.Path, "pscp.exe");
+ ConsoleLogger.Default.LogMessage($"win-x32 path {this.ExecutablePath}", telemetryContext);
+ this.SupportingExecutables.Add("pscp.exe");
+
+ break;
+ }
+
+ this.ResultsFilePath = this.PlatformSpecifics.Combine(workloadPackage.Path, $"{this.PackageName}-output.txt");
+
+ if (!this.fileSystem.File.Exists(this.ExecutablePath))
+ {
+ throw new DependencyException(
+ $"PSCP executable not found at path '{this.ExecutablePath}'",
+ ErrorReason.WorkloadDependencyMissing);
+ }
+ }
+
+ private void DeleteResultsFile(EventContext telemetryContext)
+ {
+ try
+ {
+ if (this.fileSystem.File.Exists(this.ResultsFilePath))
+ {
+ this.fileSystem.File.Delete(this.ResultsFilePath);
+ }
+ }
+ catch (IOException exc)
+ {
+ this.Logger.LogErrorMessage(exc, telemetryContext);
+ }
+ }
+ }
+}
diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PSCP.json b/src/VirtualClient/VirtualClient.Main/profiles/PSCP.json
new file mode 100644
index 000000000..9046ad483
--- /dev/null
+++ b/src/VirtualClient/VirtualClient.Main/profiles/PSCP.json
@@ -0,0 +1,33 @@
+{
+ "Description": "PSCP Installation Workload",
+ "Metadata": {
+ "RecommendedMinimumExecutionTime": "00:05:00",
+ "SupportedPlatforms": "win-x64,win-arm64,,win-arm32",
+ "SupportedOperatingSystems": "Windows"
+ },
+ "Parameters": {
+ "PscpPath": "C:\\ToolkitUserFiles\\Binaries\\Putty"
+ },
+ "Actions": [
+ {
+ "Type": "PSCPInstallation",
+ "Parameters": {
+ "Scenario": "PSCPInstallation",
+ "PackageName": "pscp",
+ "PscpPath": "$.Parameters.PscpPath"
+ }
+ }
+ ],
+ "Dependencies": [
+ {
+ "Type": "DependencyPackageInstallation",
+ "Parameters": {
+ "Scenario": "PSCPInstallation",
+ "BlobContainer": "packages",
+ "BlobName": "pscp.zip",
+ "PackageName": "pscp",
+ "Extract": true
+ }
+ }
+ ]
+}
\ No newline at end of file