Search code examples
powershellcitrixxenapp

Unable to pass command properly to remote computers in Powershell


I've got a Powershell script that when copied to a local remote server and executed remotely using PSExec it correctly runs and does what I need it to do. I was trying to adapt the same script to be able to be run remotely not relying on the script being copied and executed remotely with PSExec. Its a command to have Citrix XenApp server join its Farm.

# Establish Variables
$ZoneName = "MyZone"
$OdbcUserName = "MyCitrixUserAcct"
$OdbcPassword = "MyPassword"
$LicenseServerName = "mylicenseserver.company.com"
$XAConfigConsoleExe = "C:\Progra~2\Citrix\XenApp\ServerConfig\XenAppConfigConsole.exe"
$Servers = Get-Content -path .\serverlist.txt
ForEach ($Server in $Servers)
{
# Creating mf20.dsn File
if ((Test-Path \\$Server\c$\temp\mf20.dsn) -eq $true)
{
remove-item \\$Server\c$\temp\mf20.dsn
}
add-Content \\$Server\c$\temp\mf20.dsn "[ODBC]"
add-content \\$Server\c$\temp\mf20.dsn "DRIVER=SQL Server"
add-content \\$Server\c$\temp\mf20.dsn "DATABASE=MyDB"
add-content \\$Server\c$\temp\mf20.dsn "APP=Citrix IMA"
add-content \\$Server\c$\temp\mf20.dsn "UID="
add-content \\$Server\c$\temp\mf20.dsn "SERVER=dbserver.company.com,1433" 
add-content \\$Server\c$\temp\mf20.dsn "Trusted_Connection=No"

# Setting setup arguments

[Array]$XAArguments = @()

$XAArguments += "/ExecutionMode:Join"
$XAArguments += "/ZoneName:$ZoneName"
$XAArguments += "/ImaWorkerMode:True"
$XAArguments += "/DsnFile:c:\temp\mf20.dsn"
$XAArguments += "/AuthenticationType:Sql"
$XAArguments += "/OdbcUserName:$OdbcUserName"
$XAArguments += "/OdbcPassword:$OdbcPassword"
$XAArguments += "/LicenseServerName:$LicenseServerName"
$XAArguments += "/LicenseServerPort:27000"
$XAArguments += "/LicenseModel:XA"
$XAArguments += "/CustomXMLServicePort:8080"

# running setup with arguments

Invoke-Command -ComputerName $Server {$XAConfigConsoleExe + $XAArguments}
}

I've tried a ton of different permutations and combinations (i.e. using -scriptblock, using pssessions) of things I've found through searches. I know my command line is good because if I take the output of write-host $XAConfigConsoleExe $XAArguments and passed that to the Powershell console of the server it works.

I'm sure I'm missing something obvious. I didn't write the original script. I can't defend things like how the arguments are built, so if there is a better (more reliable way) then what I am doing here I am open to any ideas.

Here is what the original script that I can execute remotely with PSExec looks like:

# Establish Variables
$ZoneName = "MyZone"
$OdbcUserName = "MyCitrixUserAcct"
$OdbcPassword = "MyPassword"
$LicenseServerName = "mylicenseserver.company.com"
$XAConfigConsoleExe = "C:\Program Files (x86)\Citrix\XenApp\ServerConfig\XenAppConfigConsole.exe"

# Creating mf20.dsn File
if ((Test-Path C:\temp\mf20.dsn) -eq $true)
{
remove-item C:\temp\mf20.dsn
}
add-Content C:\temp\mf20.dsn "[ODBC]"
add-content C:\temp\mf20.dsn "DRIVER=SQL Server"
add-content C:\temp\mf20.dsn "DATABASE=MyDB"
add-content C:\temp\mf20.dsn "APP=Citrix IMA"
add-content C:\temp\mf20.dsn "UID="
add-content C:\temp\mf20.dsn "SERVER=mydbserver.company.com,1433" 
add-content C:\temp\mf20.dsn "Trusted_Connection=No"

# Setting setup arguments
[Array]$XAArguments = @()

$XAArguments += "/ExecutionMode:Join"
$XAArguments += "/ZoneName:$ZoneName"
$XAArguments += "/ImaWorkerMode:True"
$XAArguments += "/DsnFile:C:\Temp\mf20.dsn"
$XAArguments += "/AuthenticationType:Sql"
$XAArguments += "/OdbcUserName:$OdbcUserName"
$XAArguments += "/OdbcPassword:$OdbcPassword"
$XAArguments += "/LicenseServerName:$LicenseServerName"
$XAArguments += "/LicenseServerPort:27000"
$XAArguments += "/LicenseModel:XA"
$XAArguments += "/CustomXMLServicePort:8080"

# running setup with arguments

& $XAConfigConsoleExe $XAArguments

Solution

  • I've edited this answer to reflect the final version of the script I went with. I didn't change any logic or methods. I merely cleaned up some things and added more variables to make the script more flexible for editing in the future.

    Well it isn't the prettiest solution, but its the only reliable solution I could find that was completely self contained (outside of the input .txt file for the server list). Nothing is copied from/to anywhere, everything is created dynamically. Anyone with a sense of adventure is free to look it over and suggest better/cleaner ways to achieve my goal.

    # Establish Variables
    $ZoneName = Read-Host 'Zone Name?'
    $OdbcUserName = "Citrixuser"
    $OdbcPassword = Read-Host 'Password for Citrixuser?'
    $LicenseServerName = "MyLicenseServer.company.com"
    $DataBase= "DBName"
    $DBServer= "MyDBServer.company.com"
    $EdgeSightServer = "MyEdgeSightServer.company.com"
    
    $XAConfigConsoleExe = "C:\Program Files (x86)\Citrix\XenApp\ServerConfig\XenAppConfigConsole.exe"
    $Servers = Get-Content -path .\serverlist.txt
    
    # Setting setup arguments
    
    [Array]$XAArguments = @()
    
    $XAArguments += "/ExecutionMode:Join"
    $XAArguments += "/ZoneName:$ZoneName"
    $XAArguments += "/ImaWorkerMode:True"
    $XAArguments += "/DsnFile:C:\temp\mf20.dsn"
    $XAArguments += "/AuthenticationType:Sql"
    $XAArguments += "/OdbcUserName:$OdbcUserName"
    $XAArguments += "/OdbcPassword:$OdbcPassword"
    $XAArguments += "/LicenseServerName:$LicenseServerName"
    $XAArguments += "/LicenseServerPort:27000"
    $XAArguments += "/LicenseModel:XA"
    $XAArguments += "/CustomXMLServicePort:8080"
    $XAArguments += "/EdgeSightCompanyName:CompanyName"
    $XAArguments += "/EdgeSightServerName:$EdgeSightServer"
    $XAArguments += "/EdgeSightServerPort:80"
    
    # Running against server list
    
    ForEach ($Server in $Servers)
    {
    # Creating joinfarm.bat file on the remote server
    if ((Test-Path \\$Server\c$\temp\joinfarm.bat) -eq $true) {Remove-Item \\$Server\c$\temp\joinfarm.bat}
    Add-Content \\$Server\c$\temp\joinfarm.bat "$([char]34)$XAConfigConsoleExe$([char]34) $XAArguments"
    Add-Content \\$Server\c$\temp\joinfarm.bat "shutdown.exe /r /f /t 10"
    Add-Content \\$Server\c$\temp\joinfarm.bat "del c:\temp\mf20.dsn /q"
    Add-Content \\$Server\c$\temp\joinfarm.bat "del c:\temp\joinfarm.bat /q"
    
    # Creating mf20.dsn file on the remote server
    if ((Test-Path \\$Server\c$\temp\mf20.dsn) -eq $true) {remove-item \\$Server\c$\temp\mf20.dsn}
    add-Content \\$Server\c$\temp\mf20.dsn "[ODBC]"
    add-content \\$Server\c$\temp\mf20.dsn "DRIVER=SQL Server"
    add-content \\$Server\c$\temp\mf20.dsn "DATABASE=$DataBase"
    add-content \\$Server\c$\temp\mf20.dsn "APP=Citrix IMA"
    add-content \\$Server\c$\temp\mf20.dsn "UID="
    add-content \\$Server\c$\temp\mf20.dsn "SERVER=$DBServer,1433" 
    add-content \\$Server\c$\temp\mf20.dsn "Trusted_Connection=No"
    
    # running setup with arguments
    Invoke-Command -ComputerName $Server -ScriptBlock {c:\temp\joinfarm.bat} -AsJob
    }
    

    What I'm doing is writing a batch file (I tried writing a PS1 file but it wasn't running any commands after the initial command when I ran the PS1 -AsJob even though it seemed to work if I didn't use -AsJob) to the remote server and executing the batch with -AsJob switch so that I can join multiple servers simultaneously. Without the -AsJob each server had to complete before the next was processed. The batch cleans itself, the dsn file, and reboots the server.