0

I'm trying to write a powershell script that looks for certain programs and if it doesn't find them, installs them. I'm using chocolatey to do this and it seems to work pretty well. Here's what I have so far:

if (!([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal
.WindowsBuiltInRole] "Administrator")) { Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs; exit }

Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

$tempdir = Get-Location
$tempdir = $tempdir.tostring()
$FirstAppToMatch = '*Google Earth*'
$SecondAppToMatch = '*Google Chrome*'
$ThirdAppToMatch = '*FireFox*'
$FourthAppToMatch = '*Notepad++*'
$FifthAppToMatch = '*Adobe Reader*'
$SixthAppToMatch = '*Office*'
$msiFile = $tempdir+"\microsoft.interopformsredist.msi"
$msiArgs = "-qb"

function Get-InstalledApps
{
if ([IntPtr]::Size -eq 4) {
    $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
    $regpath = @(
        'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )
}
Get-ItemProperty $regpath | .{process{if($_.DisplayName -and 
$_.UninstallString) { $_ } }} | Select DisplayName, Publisher, InstallDate, 
DisplayVersion, UninstallString |Sort DisplayName
}

$result = Get-InstalledApps | where {$_.DisplayName -like $FirstAppToMatch}

If ($result -eq $null) {
(cinst googleearthpro -y)
}

Write-Host "Press any key to continue ..."

$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

{
if ([IntPtr]::Size -eq 4) {
    $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
$regpath = @(
        'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )
}
Get-ItemProperty $regpath | .{process{if($_.DisplayName -and 
$_.UninstallString) { $_ } }} | Select DisplayName, Publisher, InstallDate, 
DisplayVersion, UninstallString |Sort DisplayName
}

$result = Get-InstalledApps | where {$_.DisplayName -like $SecondAppToMatch}

If ($result -eq $null) {
(cinst googlechrome -y)
}

Write-Host "Press any key to continue ..."

$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

{
if ([IntPtr]::Size -eq 4) {
    $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
    $regpath = @(
        'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )
}
Get-ItemProperty $regpath | .{process{if($_.DisplayName -and 
$_.UninstallString) { $_ } }} | Select DisplayName, Publisher, InstallDate, 
DisplayVersion, UninstallString |Sort DisplayName
}

$result = Get-InstalledApps | where {$_.DisplayName -like $ThirdAppToMatch}

If ($result -eq $null) {
(cinst firefox -y)
}

Write-Host "Press any key to continue ..."

$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

{
if ([IntPtr]::Size -eq 4) {
    $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
    $regpath = @(
        'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )
}
Get-ItemProperty $regpath | .{process{if($_.DisplayName -and 
$_.UninstallString) { $_ } }} | Select DisplayName, Publisher, InstallDate, 
DisplayVersion, UninstallString |Sort DisplayName
}

$result = Get-InstalledApps | where {$_.DisplayName -like $FourthAppToMatch}

If ($result -eq $null) {
(cinst notepadplus -y)
}

Write-Host "Press any key to continue ..."

$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

{
if ([IntPtr]::Size -eq 4) {
    $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
    $regpath = @(
        'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )
}
Get-ItemProperty $regpath | .{process{if($_.DisplayName -and 
$_.UninstallString) { $_ } }} | Select DisplayName, Publisher, InstallDate, 
DisplayVersion, UninstallString |Sort DisplayName
}

$result = Get-InstalledApps | where {$_.DisplayName -like $FifthAppToMatch}

If ($result -eq $null) {
(cinst adobereader -y)
}

Write-Host "Press any key to continue ..."

$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

{
if ([IntPtr]::Size -eq 4) {
    $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
    $regpath = @(
        'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )
}
Get-ItemProperty $regpath | .{process{if($_.DisplayName -and 
$_.UninstallString) { $_ } }} | Select DisplayName, Publisher, InstallDate, 
DisplayVersion, UninstallString |Sort DisplayName
}

$result = Get-InstalledApps | where {$_.DisplayName -like $SixthAppToMatch}

If ($result -eq $null) {
(cinst office365business -y)
}

Write-Host "Press any key to continue ..."

$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

So I have 2 issues. The first is that everything after the first "write-host" gets displayed. I know I can hide the window but I don't want to do that for the purposes of testing and debugging my code. I tried encapsulating all the write-host commands with { } but it still showed all the code, as in, when the script runs, I see all of the commands shown in the PS window. If this was batch I would just @echo off but I'm not sure how to do that in powershell.

The second problem is it's messy. It works but it's messy. I feel like I should be able to condense this code down some, I'm just not sure how. In this case, I need it to iterate over each "App" and then install a different program if that particular "App" isn't found on the system. So it looks for google earth, and if it doesn't find it, it runs the command to install it...then it loops back to the next app...doesn't find it, and runs a different command to install that one and so on. I've hit a wall with this one though. I've been looking at for loops and arrays to try to see if I could find a better way to do it other than listing each command over and over like I am here, but it seems that for loops and for-each loops are meant to do the same thing to each element in the array. In this case, I need it to do something different, i.e run a different command, or not, based on the app it's looking for.

Any tips or advice would be greatly appreciated. Thanks in advance.

0

1 Answer 1

0

1- Curly braces.

There are many unnecessary pairs of them. Check out the difference they can make:

PS C:\> Write-Host "foo"
foo
PS C:\> {Write-Host "foo"}
Write-Host "foo"

The repeating if/else and Get-ItemProperty sections of your code are always wrapped together in a pair of curly braces. Remove these extra pairs. They are the cause for your code appearing in the console output. Looks like they are the result of pasting the contents of the function from the beginning of the script. Some more info on braces/brackets/parenthesis: http://www.powertheshell.com/scriptblock/

2- This is subjective.

A general tip: improve the consistency of indentation. Troubleshooting code can be hard. Troubleshooting messy code is harder. Make things easier by improving readability as you write.

For example, the extra braces from the block of code I mentioned when answering your first question could have been more easily identified had indentation been maintained more consistently.

The original block (redacted):

{
if ([IntPtr]::Size -eq 4) {
    $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
}
else {
    $regpath = @(
        'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )
}
...
}

This time with improved indentation:

{
    if ([IntPtr]::Size -eq 4) {
        $regpath = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
    }
    else {
        $regpath = @(
            'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'

            'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
        )
    }
    ...
}

The biggest factor contributing to messiness in this script is the repeated use of this code block. The script starts with a function. This is great, because you're going to be calling the contents of the function many times. This is what functions are for. However, the contents of the function are being repeatedly called throughout the script. There is no need to continuously set the $regpath variable and query it with Get-ItemProperty. This is what your function does. Removing these blocks cuts your code in half.

You're right - loops and arrays would help your code in many ways. Keeping your function, you could loop through each app with something like this:

# the index of each App matches the installer name
$apps = @('Google Earth','Google Chrome','FireFox','Notepad++','Adobe Reader','Office')
$name = @('googleearthpro','googlechrome','firefox','notepadplus','adobereader','office365business')

foreach($app in $apps) {

    $result = Get-InstalledApps | Where-Object { $_.DisplayName -like $app }

    if($result -eq $null) {

        # get the index for the installer for this app
        $i = $apps.IndexOf($app)
        Write-Host "$app not found. Installing" $name[$i]

        (cinst $name[$i] -y)
    }

}
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .