Home > Back-end >  Powershell script to install multiple applications using an array and scriptblock
Powershell script to install multiple applications using an array and scriptblock

Time:03-11

Need help troubleshooting an the Array and Scriptblock OR Maybe this is better using param and functions???

Script Objective: To easily update the list of applications to be installed

Getting error below.

' At C:\Temp\appinstall.ps1:7 char:10 $Firefox={
~ The assignment expression is not valid. The input to an assignment operator must be an object that is able to accept assignments, such as a variable or a property. CategoryInfo : ParserError: (:) [], ParseException FullyQualifiedErrorId : InvalidLeftHandSide '

Start-Transcript -Append c:\Deploy\log.txt
$ProgressPreference = 'SilentlyContinue';
#Change App Name, Source, MSI/EXE, Argument

$AppArray= (

$Firefox={
$App= "Firefox";
$App_source= "https://download.mozilla.org/?product=firefox-latest&os=win64&lang=en-US";
$destination = "c:\Deploy\$App.exe";
$Argument= "/S";
},

$Chrome=
{
$App= "Chrome";
$App_source= "https://dl.google.com/tag/s/defaultbrowser/edgedl/chrome/install/GoogleChromeStandaloneEnterprise64.msi";
$destination = "c:\Deploy\$App.exe";
$Argument= "/norestart","/qn";
}
)


$InstallScriptBlock=
{
$installed = (Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Where { $_.DisplayName -Match "$App" });
$installed.displayname
if ($installed.displayname -Match $App) {
    Write-Host "$software installed"
}else{
If ((Test-Path $destination) -eq $false) {
    New-Item -ItemType File -Path $destination -Force
}
#install software
Invoke-WebRequest $App_source -OutFile $destination
Start-Process -FilePath "$destination" -ArgumentList "$Argument" -Wait
#Delete installer
Remove-Item -recurse "$destination"
}
}

ForEach ($Program in $AppArray) {Invoke-Command -ScriptBlock $InstallScriptBlock}

Stop-Transcript

CodePudding user response:

It looks like you're trying to create a nested hashtable (@{ ... }), but your syntax is flawed - see the linked docs.

However:

  • It should suffice in your case to create an array of hashtables to iterate over with foreach

  • There's no need to use a separate script block ({ ... }) - just use the body of the foreach loop statement.

    • As an aside: While using Invoke-Command for local invocation of script blocks works, it usually isn't necessary, because &, the call operator, will do (e.g. $sb = { 'hi' }; & $sb). Invoke-Command's primary purpose is to execute a script block on a remote machine.
  • Generally, you can use variables as-is as command arguments, without enclosing them in "..." - even if their values contain spaces. E.g., Write-Output $foo is sufficient, no need for Write-Output "$foo"

To put it all together:

# Create an array whose elements are hashtables.
$appArray = (
  @{
    App         = ($thisApp = 'Firefox')
    App_source  = 'https://download.mozilla.org/?product=firefox-latest&os=win64&lang=en-US'
    Destination = "c:\Deploy\$thisApp.exe"
    Argument    = '/S'
  },
  @{
    App         = ($thisApp = 'Chrome')
    App_source  = 'https://dl.google.com/tag/s/defaultbrowser/edgedl/chrome/install/GoogleChromeStandaloneEnterprise64.msi'
    Destination = "c:\Deploy\$thisApp.exe"
    Argument    = '/norestart /qn'
  }
)

foreach ($app in $appArray) {
  # Note how $app.<key> is used to refer to the entries of the hashtable at hand,
  # e.g. $app.App yields "Firefox" for the first hashtable.
  $installed = Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Where-Object { $_.DisplayName -Match $app.App }
  $installed.displayname
  if ($installed.displayname -Match $app.App) {
    Write-Host "$($app.App) already installed."
  }
  else {
    if ((Test-Path $app.Destination) -eq $false) {
      New-Item -ItemType File -Path $app.Destination -Force
    }
    #install software
    Invoke-WebRequest $app.App_source -OutFile $app.Destination
    Start-Process -FilePath $app.Destination -ArgumentList $app.Argument -Wait
    #Delete installer
    Remove-Item -Recurse $app.Destination
  }
}

Note:

  • I've removed unnecessary ; and I've switched to using verbatim (single-quoted) strings ('...') when no string interpolation via expandable (double-quoted) strings ("...") is required, both for conceptual clarity and to avoid potentially unwanted expansions.

  • Note the use of aux. variable $thisApp in the App key, which allows referencing it in the later Destination key, in an expandable string ("c:\Deploy\$thisApp.exe").

  • Related