# Specify the working directory for this script to place files
$WORK_DIR = "${Env:ProgramData}\Atera"

# Specify the name of the previous generation file to compare with the current generation file
$P_FILE = "${WORK_DIR}\InstalledSoftware_Previous.csv"

# Specify the name of the current generation file to compare with the previous generation file
$C_FILE = "${WORK_DIR}\InstalledSoftware_Current.csv"

# Function to append a line to a file
Function Write-LineToFile {
    param([Parameter(Mandatory=$true)][string]$Path,[Parameter(Mandatory=$true)][string]$Line)
    If ( -not (Test-Path $Path) ) {
        Write-Output "Creating file ""${Path}"""
        Try { New-Item -Path $Path -ItemType File -Force | Out-Null }
        Catch { Write-Error $_.Exception.Message }
    }
    Try { Add-Content -Path $Path -Value $Line }
    Catch { Write-Error $_.Exception.Message }
}

# Function to test whether or not an item has a specified property
Function Test-ItemProperty {
    param([Parameter(Mandatory=$true)][string]$Path,[Parameter(Mandatory=$true)][string]$Name)
    Try {
        $test = Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue 
        If (($test -eq $null) -or ($test.Length -eq 0)) { Return $false } Else { Return $true }
    }
    Catch { Return $false }
}

# Function to retrieve an item property
Function Get-ItemPropertyInfo {
    param([Parameter(Mandatory=$true)][string]$Path,[Parameter(Mandatory=$true)][string]$Name)
    If ( Test-ItemProperty -Path $Path -Name $Name ) {
        $retval = Get-ItemPropertyValue -Path $Path -Name $Name
        $retval = $retval -replace ",", " "

        # Additional processing for specific properties
        Switch ( $Name ) {

            # Convert the estimated size into MB
            "EstimatedSize" {
                $size = [int]$retval
                $size = [Math]::Ceiling($size / 1024)
                $retval = [string]$size
            }

            # Convert the date format into YYYY-MM-DD
            "InstallDate" {

                $origRetval = $retval
                
                # Try to find each type of date string so we can convert it
                Switch -Regex ( $retval ) {
                    '^\d{8}$'                   { $formatstring = 'yyyyMMdd' }
                    '^\d{1,2}\\\d{1,2}\\\d{4}$' { $formatstring = 'MM\dd\yyyy' }
                    '^\d{4}\\\d{1,2}\\\d{1,2}$' { $formatstring = 'yyyy\MM\dd' }
                    Default                     { $formatstring = $null }
                }

                # Make sure $formatstring has a value, if not just try to cast directly to a datetime
                Try {
                If ( $formatstring -eq $null ) {
                    $date = [datetime]$retval
                } Else {
                    $date = [datetime]::ParseExact($retval,$formatstring,$null)
                }

                # Set $retval to a standard datetime format
                $retval = $date.ToString('yyyy-MM-dd')
                }
                Catch { Write-Error $_.Exception.Message; $retval = $origRetval }
            }
            Default {}
        }
    }
    Else { $retval = "" }

    Return $retval
}

# Create $WORK_DIR if it doesn't exist
If ( -not (Test-Path $WORK_DIR) ) { New-Item -Path $WORK_DIR -ItemType Directory -Force }

# Create $C_FILE if it doesn't exist
If ( -not (Test-Path $C_FILE) ) {
    # Write the headers to the output file
    Write-LineToFile -Path $C_FILE -Line "Name,Publisher,Version,InstallDate,EstimatedSize(MB),UninstallString,ModifyPath"

    # Iterate through all Registry Uninstall keys and build the CSV file of all installed software
    ForEach ( $item in $(Get-ChildItem -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall','HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall') ) {
        $DisplayName     = Get-ItemPropertyInfo -Path $item.PSPath -Name DisplayName
        $DisplayVersion  = Get-ItemPropertyInfo -Path $item.PSPath -Name DisplayVersion
        $EstimatedSize   = Get-ItemPropertyInfo -Path $item.PSPath -Name EstimatedSize
        $InstallDate     = Get-ItemPropertyInfo -Path $item.PSPath -Name InstallDate
        $Publisher       = Get-ItemPropertyInfo -Path $item.PSPath -Name Publisher
        $UninstallString = Get-ItemPropertyInfo -Path $item.PSPath -Name UninstallString
        $ModifyPath      = Get-ItemPropertyInfo -Path $item.PSPath -Name ModifyPath

        # Write installed software to output file only if it has a name and uninstall/modify path, otherwise discard
        If ( -not (($DisplayName -eq "") -and ($UninstallString -eq "") -and ($ModifyPath -eq "")) ) {
            $content = "${DisplayName},${Publisher},${DisplayVersion},${InstallDate},${EstimatedSize},${UninstallString},${ModifyPath}"
            Write-LineToFile -Path $C_FILE -Line $content
        }
    }
}

# Compare $C_FILE and $P_FILE
If ( (Test-Path $C_FILE) -and (Test-Path $P_FILE) ) {
    $current = Import-Csv -Path $C_FILE
    $previous = Import-Csv -Path $P_FILE

    # Items that exist in $C_FILE but not $P_FILE have been installed
    $installed = Compare-Object -ReferenceObject $current -DifferenceObject $previous -Property Name,Version -PassThru | Where-Object { (![string]::IsNullOrEmpty($_.Name)) -and ($_.SideIndicator -eq '<=') }

    # Items that exist in $P_FILE but not $C_FILE have been uninstalled
    $uninstalled = Compare-Object -ReferenceObject $current -DifferenceObject $previous -Property Name,Version -PassThru | Where-Object { (![string]::IsNullOrEmpty($_.Name)) -and ($_.SideIndicator -eq '=>') }

    # Alert trigger for Atera
    If ( $installed -or $uninstalled ) { Write-Output "Application change detected" }

    # Display installed apps
    If ( $installed ) {
        Write-Output "`nAPPLICATIONS RECENTLY INSTALLED:"
        $installed | Select-Object * -ExcludeProperty SideIndicator
    }

    # Display uninstalled apps
    If ( $uninstalled ) {
        Write-Output "`nAPPLICATIONS RECENTLY UNINSTALLED:"
        $uninstalled | Select-Object * -ExcludeProperty UninstallString,ModifyPath,SideIndicator
    }
}

# Delete $P_FILE
If ( Test-Path $P_FILE ) {
    Write-Output "Deleting ""${P_FILE}"""
    Remove-Item -Path $P_FILE -Force
}

# Rename $C_FILE to $P_FILE
If ( Test-Path $C_FILE ) {
    Write-Output "Renaming ""${C_FILE}"" to ""${P_FILE}"""
    Move-Item -Path $C_FILE -Destination $P_FILE -Force
}

