I am attempting to setup a Cake task that will use MsDeploy to sync a powershell script to a remote server and then execute that script as a post sync command.
The problem I'm facing is finding a combination of quotes and escapes within the cake file that allow the command to make it all the way to powershell with the file path quoted correctly to allow powershell to find the script; the path has spaces in it.
This appears to be so difficult because of the chain of execution that this command goes through at run time.
First because this is written in C# the string either needs to be verbatim (@"command here"
) or to have all internal double quotes escaped with \
.
Next Cake performs several operations on the arguments, though none seemed to affect things like quoting and escaping until it actually got to the execution, at which point it uses the C# Process.Start()
static method to run the MsDeploy executable. In my reading it was suggested that this method of running a command requires three double quotes to be properly escaped, though that didn't mesh with what I was seeing when I tried.
Then once on the remote machine MsDeploy uses CMD.exe
to execute the command which notably has no support for single quotes so double quotes either needed to be escaped by using \"
or ""
.
The closest I have gotten looks like this:
Task("InitializeIISApplication")
.IsDependentOn("InjectVariables")
.Does(() => {
MsDeploy(new MsDeploySettings
{
Verb = Operation.Sync,
RetryAttempts = 3,
RetryInterval = 10000,
Source = new FilePathProvider
{
Direction = Direction.source,
Path = MakeAbsolute(File(@".\MyPowershell.ps1")).ToString()
},
Destination = new FilePathProvider
{
Direction = Direction.dest,
Path = File(deployParameters.ApplicationDestinationPath + @"\MyPowershell.ps1").ToString(),
Username = deployParameters.MsDeployUserName,
Password = deployParameters.MsDeployUserPassword,
WebManagementService = deployParameters.DeploymentTargetUrl
},
AllowUntrusted = true,
EnableRules = new List<string> {
"DoNotDeleteRule"
},
PostSyncCommand = new CommandProvider {
AppendQuotesToPath = false,
Direction = Direction.dest,
Path = $"powershell -file '{deployParameters.ApplicationDestinationPath}\\MyPowershell.ps1' ",
}
});
MsDeploy(new MsDeploySettings
{
Verb = Operation.Delete,
Destination = new FilePathProvider
{
Direction = Direction.dest,
Path = File(deployParameters.ApplicationDestinationPath + "\MyPowershell.ps1").ToString(),
Username = deployParameters.MsDeployUserName,
Password = deployParameters.MsDeployUserPassword,
WebManagementService = deployParameters.DeploymentTargetUrl
},
AllowUntrusted = true
});
});
The task dependency is just setting up the deployParameters
object.
Which with Cake's Diagnostic verbosity enabled produces the following command into the logs (new lines added for clarity):
"C:/Program Files/IIS/Microsoft Web Deploy V3/msdeploy.exe"
-verb:sync
-source:filePath="Y:/PathToBuildArtifact/Deploy/MyPowershell.ps1"
-dest:filePath="C:/Application - With Spaces/MyPowershell.ps1",wmsvc="https://deploy-server/msdeploy.axd",userName=msdeployuser,password=********
-enableRule:DoNotDeleteRule
-retryAttempts:3
-retryInterval:10000
-allowUntrusted
-postSync:runCommand="powershell -file 'C:\Application - With Spaces\MyPowershell.ps1' "
Then ends with the error:
Warning: Processing -File ''C:/Application' failed: The given path's format is not supported. Specify a valid path for the -File parameter.
Any variant I've tried using double quotes inside the postSync command instead results in this error:
Error: Unrecognized argument '-'.
If it matters this is being done on the Bamboo CI server.