Install Software on multiple remote servers.

This topic contains 1 reply, has 2 voices, and was last updated by  Dave Wyatt 4 years, 6 months ago.

  • Author
  • #12449

    Jay Jones

    Hi all,

    Was wondering if you could lend some help. I need to install some software on a load of servers and the manual process is very time consuming. Before the installation i need to check whether the OS is 64 bit or 32 bit, i then want the script to install the 32bit.exe or 64bit.exe depending on the result of the initial check. I want to repeat this process on a list of defined servers in the script. This is what i have so far.

    # List of target servers
    $serverlist = server1, server2, server3, etc.
    #Function to check os version
    Function CheckOSBitVersion
                if ([System.IntPtr]::Size -eq 4) { "32-bit" } else { "64-bit" }
    #Function to Install 32bit version software
    Function InstallPCNS32Bit
                ([WMICLASS]"\\$ServerList\ROOT\CIMV2:win32_process").Create("cmd.exe /c PathOfOurProgram.exe /s /v`" /qn")
    #Function to Install 64bit version software
    Function InstallPCNS64Bit
                ([WMICLASS]"\\$ServerList\ROOT\CIMV2:win64_process").Create("cmd.exe /c PathOfOurProgram.exe /s /v`" /qn")
    #Function to check os version and install appropriate software for each server in $serverlist
    Function InstallPCNS
                 foreach-object in $serverlist | CheckOSBitVersion
    if (CheckOSBitVersion = "32-bit") < <<# Would this work?

    Hope this make sense. Look forward to getting some feedback.

  • #12450

    Dave Wyatt

    Hi Jay,

    Here are a few observations on your code so far:

    • The simplest way to check whether the local operating system is 64-bit is to look at the [System.Environment]::Is64BitOperatingSystem property, which is a boolean. Since you're working with remote computers via WMI already, you'll probably want to rely on the OSArchitecture property of the Win32_OperatingSystem class (though this property wasn't available in Windows XP / Server 2003, so hopefully you've already upgraded from those old platforms.) It's a string property that will be set to either "32-bit" or "64-bit".
    • Win64_Process is not a valid WMI class name. You can launch both 32-bit and 64-bit executables with the Win32_Process class. Whether a process is 32-bit or 64-bit depends on how the executable was compiled; you have no control over that. If you have separate installers for 32-bit or 64-bit operating systems, though, you can use different values for your "PathOfOurProgram.exe" placeholder.
    • In this case, the code is fairly small and there's not a lot of need to split the code into multiple functions. When you do write functions, however, it's almost always best to define parameters and pass them in when calling the function, instead of accessing variables in the parent or global scope (such as the way you're currently using $ServerList in your InstallPCNS32Bit and InstallPCNS64Bit functions.)
    • Be careful not to mix up the "=" and "-eq" operators. "=" is the assignment operator, and "-eq" is used for testing equality. Right now you're trying to use "=" in a conditional, which isn't going to work the way you intended.
    • When building your server list, you need to put each servername in quotes. PowerShell has two parsing modes: "expression mode" and "argument mode", which you can read about in the about_Parsing help file. In the case of the statement "$serverlist = server1, server2, server3", you're in expression mode, which requires all string literals to be quoted.

    There may be other tweaks that you can make to improve this code, but here's a quick revision based on what I've mentioned so far:

    $serverList = 'server1', 'server2', 'server3'
    foreach ($server in $serverList)
        $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $server
        # there was an error connecting to the remote computer, Get-WmiObject will output an error, and 
        # $os will be set to $null.  Here, I'm not really handling the Get-WmiObject error, just skipping
        # the rest of the code in the loop if it happened to fail.
        if ($os)
            if ($os.OSArchitecture -eq '32-bit')
                $path = 'C:\Path\To\32BitInstaller.exe'
                $path = 'C:\Path\To\64BitInstaller.exe'
            # There are probably problems with this command string.  That embedded, lonely quotation mark after /v looks odd, but since I
            # don't know what you need to pass to your installer, I've left it alone.
            $result = Invoke-WmiMethod -Class Win32_Process -Name Create -ArgumentList "cmd.exe /c $path /s /v`" /qn"
            if ($result.ReturnValue -ne 0)
                # An error occurred.  $result.ReturnValue holds the win32 error code for the failure.
                # You can handle this however you like; here, I'll use Write-Error with a descriptive message
                # from the Win32Exception class.
                $exception = New-Object System.ComponentModel.Win32Exception([int]$result.ReturnValue)
                Write-Error "Error launching installer on computer ${server}: $($exception.Message)"

    I don't know which PowerShell version you're using, so I went with the older WMI cmdlets instead of the CIM cmdlets (which were added in PowerShell 3.0). I also opted for Invoke-WmiMethod, instead of using something like ([wmiclass]'\\$server\root\cimv2:Win32_Process').Create() . The end result is the same, but getting used to the cmdlet version makes it easier to transition to the new Cim-based cmdlets, once you have that option.

You must be logged in to reply to this topic.