Private/Functions.ps1
<#
.SYNOPSIS Private functions for running FudgePop .DESCRIPTION Private functions for FudgePop module .NOTES 1.0.10 - 11/16/2017 - David Stein #> function Write-FPLog { <# .SYNOPSIS Output Writing Handler .DESCRIPTION Yet another stupid Write-Log function like everyone else has .PARAMETER Category Describes type of information as 'Info','Warning','Error' (default is 'Info') .PARAMETER Message Information to display or write to log file .EXAMPLE Write-FPLog -Category 'Info' -Message 'This is a message' #> param ( [parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $Message, [parameter(Mandatory = $False)] [ValidateSet('Info', 'Warning', 'Error')] [string] $Category = 'Info' ) Write-Verbose "$(Get-Date -f 'yyyy-M-dd HH:mm:ss') $Category $Message" "$(Get-Date -f 'yyyy-M-dd HH:mm:ss') $Category $Message" | Out-File $Script:FPLogFile -Encoding Default -Append } function Install-Chocolatey { <# .SYNOPSIS Insure Chocolatey is installed .DESCRIPTION Check if Chocolatey is installed. If not, then install it. .EXAMPLE Install-Chocolatey #> [CmdletBinding(SupportsShouldProcess = $True)] param () Write-FPLog -Category Info -Message "verifying chocolatey is installed" if (!(Test-Path "$($env:ProgramData)\chocolatey\choco.exe")) { Write-FPLog -Category Info -Message "installing chocolatey..." try { iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) } catch { Write-FPLog -Category Error -Message $_.Exception.Message } } else { Write-FPLog -Category Info -Message "chocolatey is already installed" } } function Get-FPConfiguration { <# .SYNOPSIS Import Control Data from Registry .DESCRIPTION Fetch data from Registry or return Default if none found .PARAMETER RegPath Registry Path (default is HKLM:\SOFTWARE\FudgePop) .PARAMETER Name Registry Value name .PARAMETER Default Data to return if no value found in registry .INPUTS Registry Key, Value Name, Default Value (if not found in registry) .OUTPUTS Information returned from registry (or default value) #> [CmdletBinding()] param ( [parameter(Mandatory = $False)] [ValidateNotNullOrEmpty()] [string] $RegPath = $FPRegRoot, [parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $Name, [parameter(Mandatory = $False)] [string] $Default = "" ) if (Test-Path $RegPath) { Write-Verbose "registry path confirmed: $RegPath ($Name)" try { $result = Get-ItemProperty -Path $RegPath -ErrorAction Stop | Select-Object -ExpandProperty $Name -ErrorAction Stop if ($result -eq $null -or $result -eq "") { Write-Verbose "no data returned from query. using default: $Default" $result = $Default } } catch { Write-Verbose "error: returning $Default" $result = $Default } } else { Write-Verbose "registry path does not yet exist: $RegPath" $result = $Default } Write-Output $result } function Set-FPConfiguration { <# .SYNOPSIS Write data to Registry .DESCRIPTION Write Data to FudgePop Registry location .PARAMETER RegPath Registry Path (default is HKLM:\SOFTWARE\FudgePop) .PARAMETER Name Registry Value name .PARAMETER Data Data to store in registry value .INPUTS Registry Key (or default), Value Name, Data #> [CmdletBinding(SupportsShouldProcess = $True)] param ( [parameter(Mandatory = $False)] [ValidateNotNullOrEmpty()] [string] $RegPath = $FPRegRoot, [parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $Name, [parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $Data ) if (!(Test-Path $RegPath)) { try { Write-Verbose "creating new registry key root" New-Item -Path $RegPath -Force -ErrorAction Stop | Out-Null $created = $True } catch { Write-FPLog -Category 'Error' -Message $_.Exception.Message break } } if ($created) { Set-ItemProperty -Path $RegPath -Name "ModuleVersion" -Value $FPVersion -ErrorAction Stop Set-ItemProperty -Path $RegPath -Name "InitialSetup" -Value (Get-Date) -ErrorAction Stop } try { Set-ItemProperty -Path $RegPath -Name $Name -Value $Data -ErrorAction Stop } catch { Write-FPLog -Category 'Error' -Message $_.Exception.Message break } Write-Output 0 } function Test-FPControlSource { <# .SYNOPSIS Validate File or URI is accessible .DESCRIPTION Verifies Control XML file is accessible .PARAMETER Path Full Path or URI to file .INPUTS Path to file .OUTPUTS $True or $null #> param ( [parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $Path ) if ($Path.StartsWith('http')) { Write-FPLog "verifying URI resource: $Path" try { $test = Invoke-WebRequest -UseBasicParsing -Uri $Path -Method Get -ErrorAction SilentlyContinue if ($test) { Write-Output ($test.StatusCode -eq 200) } } catch {} } else { Write-FPLog "verifying file system resource: $Path" Write-Output (Test-Path $Path) } } function Get-FPControlData { <# .SYNOPSIS Import XML data from Control File .DESCRIPTION Import XML data from Control File .PARAMETER FilePath Full path or URI to XML file #> param ( [parameter(Mandatory = $True, HelpMessage = "Path or URI to XML control file")] [ValidateNotNullOrEmpty()] [string] $FilePath ) Write-FPLog "preparing to import control file: $FilePath" if ($FilePath.StartsWith("http")) { try { [xml]$result = ((New-Object System.Net.WebClient).DownloadString($FilePath)) } catch { Write-FPLog -Category 'Error' -Message "failed to import data from Uri: $FilePath" Write-Output -3 break; } Write-FPLog 'control data loaded successfully' } else { if (Test-Path $FilePath) { try { [xml]$result = Get-Content -Path $FilePath } catch { Write-FPLog -Category 'Error' -Message "unable to import control file: $FilePath" Write-Output -4 break; } } else { Write-FPLog -Category 'Error' -Message "unable to locate control file: $FilePath" Write-Output -5 break; } } Write-Output $result } function Get-FPFilteredSet { <# .SYNOPSIS Return Targeted XML data set .DESCRIPTION Return Targeted XML data set for this device or associated collection .PARAMETER XmlData XML data set for specific control group (e.g. files, folders, etc.) .PARAMETER Collections Array of collection names .EXAMPLE $dataset = Get-FPFilteredSet -XmlData $ControlData.configuration.files.file -Collections (Get-FPDeviceCollections -XmlData $ControlData) .NOTES #> param ( [parameter(Mandatory = $True)] $XmlData, [parameter(Mandatory = $False)] $Collections ) $thisDevice = $env:COMPUTERNAME if ($Collections -ne $null) { $result = $XmlData | Where-Object {$_.enabled -eq 'true' -and ($Collections.Contains($_.collection)) } } else { $result = $XmlData | Where-Object {$_.enabled -eq 'true' -and ($_.device -eq 'all' -or $_.device -eq $thisDevice)} } Write-Output $result } function Get-FPServiceAvailable { <# .SYNOPSIS Verify FudgePop Control Item is Enabled .DESCRIPTION Return TRUE if enabled="true" in control section of XML .PARAMETER DataSet XML data from control file import .INPUTS XML data .OUTPUTS $True or $null #> param ( [parameter(Mandatory = $True)] $DataSet ) if ($DataSet.configuration.control.enabled -eq 'true') { if (($DataSet.configuration.control.exclude -split ',') -contains $MyPC) { Write-FPLog 'FudgePop services are enabled, but this device is excluded' break } else { Write-FPLog 'FudgePop services are enabled for all devices' Write-Output $True } } else { Write-FPLog 'FudgePop services are currently disabled for all devices' } } function Test-FPDetectionRule { <# .SYNOPSIS Return TRUE if detection rule is valid .PARAMETER DataSet XML data from control file import .PARAMETER RuleName Name of rule in control XML file #> param ( [parameter(Mandatory = $True)] $DataSet, [parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $RuleName ) Write-FPLog "detection rule: $RuleName" try { $detectionRule = $DataSet.configuration.detectionrules.detectionrule | Where-Object {$_.name -eq $RuleName} $rulePath = $detectionRule.path Write-FPLog "detection test: $rulePath" Write-Output (Test-Path $rulePath) } catch {} } function Test-FPControlRuntime { <# .SYNOPSIS Confirm Task Execution Time .DESCRIPTION Return TRUE if a task runtime is active .PARAMETER RunTime Date Value, or 'now' or 'daily' .PARAMETER Key Label to map to Registry for get/set operations .EXAMPLE Test-FPControlRuntime -RunTime "now" .EXAMPLE Test-FPControlRuntime -RunTime "11/12/2017 10:05:00 PM" .EXAMPLE Test-FPControlRuntime -RunTime "daily" -Key "TestValue" #> param ( [parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $RunTime, [parameter(Mandatory = $False)] [string] $Key = "" ) switch ($RunTime) { 'now' { Write-Output $True; break } 'daily' { $lastrun = Get-FPConfiguration -Name "$Key" -Default "" if ($lastrun -ne "") { $prevDate = $(Get-Date($lastrun)).ToShortDateString() Write-FPLog "previous run: $prevDate" if ($prevDate -ne (Get-Date).ToShortDateString()) { Write-FPLog "$prevDate is not today: $((Get-Date).ToShortDateString())" Write-Output $True } } else { Write-FPLog "no previous run" Write-Output $True } break } default { Write-FPLog "checking explicit runtime" if ((Get-Date).ToLocalTime() -ge $RunTime) { Write-Output $True } } } # switch } function Assert-Chocolatey { <# .SYNOPSIS Install Chocolatey .DESCRIPTION Process Configuration Control: Install or Upgrade Chocolatey .EXAMPLE Assert-Chocolatey #> param () Write-FPLog "verifying chocolatey installation" if (-not(Test-Path "$($env:ProgramData)\chocolatey\choco.exe" )) { try { Write-FPLog "installing chocolatey" Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) } catch { Write-FPLog -Category "Error" -Message $_.Exception.Message break } } else { Write-FPLog "checking for newer version of chocolatey" choco upgrade chocolatey -y } } function Set-FPControlFiles { <# .SYNOPSIS Create and Manipulate Files .DESCRIPTION Process Configuration Control: Files .PARAMETER DataSet XML data from control file import .EXAMPLE Set-FPControlFiles -DataSet $xmldata #> [CmdletBinding(SupportsShouldProcess = $True)] param ( [parameter(Mandatory = $True)] $DataSet ) Write-FPLog "--------- file assignments: begin ---------" foreach ($file in $DataSet) { $fileDevice = $file.device $collection = $file.collection $fileSource = $file.source $fileTarget = $file.target $action = $file.action Write-FPLog "device name.......: $fileDevice" Write-FPLog "collection........: $collection" Write-FPLog "action............: $action" Write-FPLog "source............: $fileSource" Write-FPLog "target............: $fileTarget" if ($TestMode) { Write-FPLog "TEST MODE: no changes will be applied" } else { switch ($action) { 'download' { Write-FPLog "downloading file" if ($fileSource.StartsWith('http') -or $fileSource.StartsWith('ftp')) { try { <# $WebClient = New-Object System.Net.WebClient $WebClient.DownloadFile($fileSource, $fileTarget) | Out-Null #> Import-Module BitsTransfer Start-BitsTransfer -Source $fileSource -Destination $fileTarget if (Test-Path $fileTarget) { Write-FPLog "file downloaded successfully" } else { Write-FPLog -Category "Error" -Message "failed to download file!" } } catch { Write-FPLog -Category "Error" -Message $_.Exception.Message } } else { try { Copy-Item -Source $fileSource -Destination $fileTarget -Force | Out-Null if (Test-Path $fileTarget) { Write-FPLog "file downloaded successfully" } else { Write-FPLog -Category "Error" -Message "failed to download file!" } } catch { Write-FPLog -Category "Error" -Message "failed to download file!" } } break } 'rename' { Write-FPLog "renaming file" if (Test-Path $fileSource) { Rename-Item -Path $fileSource -NewName $fileTarget -Force | Out-Null if (Test-Path $fileTarget) { Write-FPLog "file renamed successfully" } else { Write-FPLog -Category "Error" -Message "failed to rename file!" } } else { Write-FPLog -Category "Warning" -Message "source file not found: $fileSource" } break } 'move' { Write-FPLog "moving file" if (Test-Path $fileSource) { Move-Item -Path $fileSource -Destination $fileTarget -Force | Out-Null if (Test-Path $fileTarget) { Write-FPLog "file moved successfully" } else { Write-FPLog -Category "Error" -Message "failed to move file!" } } else { Write-FPLog -Category "Warning" -Message "source file not found: $fileSource" } break } 'delete' { Write-FPLog "deleting file" if (Test-Path $fileSource) { try { Remove-Item -Path $fileSource -Force | Out-Null if (-not(Test-Path $fileSource)) { Write-FPLog "file deleted successfully" } else { Write-FPLog -Category "Error" -Message "failed to delete file!" } } catch { Write-FPLog -Category "Error" -Message $_.Exception.Message } } else { Write-FPLog "source file not found: $fileSource" } break } } # switch } } # foreach Write-FPLog "--------- file assignments: finish ---------" } function Set-FPControlFolders { <# .SYNOPSIS Create Folders .DESCRIPTION Process Configuration Control: Folders .PARAMETER DataSet XML data from control file import .EXAMPLE Set-FPControlFolders -DataSet $xmldata #> [CmdletBinding(SupportsShouldProcess = $True)] param ( [parameter(Mandatory = $True)] $DataSet ) Write-FPLog -Category "Info" -Message "--------- folder assignments: begin ---------" foreach ($folder in $DataSet) { $deviceName = $folder.device $collection = $folder.collection $action = $folder.action $folderPath = $folder.path Write-FPLog -Category "Info" -Message "device...........: $deviceName" Write-FPLog -Category "Info" -Message "collection.......: $collection" Write-FPLog -Category "Info" -Message "folder action....: $action" switch ($action) { 'create' { Write-FPLog -Category "Info" -Message "folder path: $folderPath" if (-not(Test-Path $folderPath)) { Write-FPLog -Category "Info" -Message "creating new folder" if (-not $TestMode) { mkdir -Path $folderPath -Force | Out-Null } else { Write-FPLog -Category "Info" -Message "TEST MODE: no changes are being applied" } } else { Write-FPLog -Category "Info" -Message "folder already exists" } break } 'empty' { $filter = $folder.filter if ($filter -eq "") { $filter = "*.*" } Write-FPLog -Category "Info" -Message "deleting $filter from $folderPath and subfolders" if (-not $TestMode) { Get-ChildItem -Path "$folderPath" -Filter "$filter" -Recurse | ForEach-Object { Remove-Item -Path $_.FullName -Confirm:$False -Recurse -ErrorAction SilentlyContinue } Write-FPLog -Category "Info" -Message "some files may remain if they were in use" } else { Write-FPLog -Category "Info" -Message "TEST MODE: no changes are being applied" } break } 'delete' { if (Test-Path $folderPath) { Write-FPLog -Category "Info" -Message "deleting $folderPath and subfolders" if (-not $TestMode) { try { Remove-Item -Path $folderPath -Recurse -Force | Out-Null Write-FPLog -Category "Info" -Message "folder may remain if files are still in use" } catch { Write-FPLog -Category "Error" -Message $_.Exception.Message } } else { Write-FPLog -Category "Info" -Message "TEST MODE: no changes are being applied" } } else { } break } } # switch } # foreach Write-FPLog -Category "Info" -Message "--------- folder assignments: finish ---------" } function Set-FPControlServices { <# .SYNOPSIS Process Control Changes on Services .DESCRIPTION Process Configuration Control: Windows Services .PARAMETER DataSet XML data from control file import .EXAMPLE Set-FPControlServices -DataSet $xmldata #> [CmdletBinding()] param ( [parameter(Mandatory = $True)] $DataSet ) Write-FPLog -Category "Info" -Message "--------- services assignments: begin ---------" foreach ($service in $DataSet) { $deviceName = $service.device $collection = $service.collection $svcName = $service.name $svcConfig = $service.config $svcAction = $service.action Write-FPLog -Category "Info" -Message "device name.....: $deviceName" Write-FPLog -Category "Info" -Message "collection......: $collection" Write-FPLog -Category "Info" -Message "service name....: $svcName" Write-FPLog -Category "Info" -Message "action..........: $svcAction" Write-FPLog -Category "Info" -Message "config type.....: $svcConfig" try { $scfg = Get-Service -Name $svcName switch ($svcAction) { 'modify' { $sst = $scfg.StartType if ($svcConfig -ne "") { $cfgList = $svcConfig -split ('=') $cfgName = $cfgList[0] $cfgData = $cfgList[1] switch ($cfgName) { 'startup' { if ($cfgData -ne "" -and $scfg.StartType -ne $cfgData) { Write-FPLog -Category "Info" -Message "current startup type is: $sst" Write-FPLog -Category "Info" -Message "setting service startup to: $cfgData" if (-not $TestMode) { Set-Service -Name $svcName -StartupType $cfgData | Out-Null } else { Write-FPLog -Category "Info" -Message "TEST MODE: $cfgName -> $cfgData" } } break } } # switch } else { Write-FPLog -Category 'Error' -Message 'configuration properties have not been specified' } break } 'start' { if ($scfg.Status -ne 'Running') { Write-FPLog -Category "Info" -Message "starting service..." if (-not $TestMode) { Start-Service -Name $svcName | Out-Null } else { Write-FPLog -Category "Info" -Message "TEST MODE" } } else { Write-FPLog -Category "Info" -Message "service is already running" } break } 'restart' { Write-FPLog -Category "Info" -Message "restarting service..." if (-not $TestMode) { Restart-Service -Name $svcName -ErrorAction SilentlyContinue } else { Write-FPLog -Category "Info" -Message "TEST MODE" } break } 'stop' { Write-FPLog -Category "Info" -Message "stopping service..." if (-not $TestMode) { Stop-Service -Name $svcName -Force -NoWait -ErrorAction SilentlyContinue } else { Write-FPLog -Category "Info" -Message "TEST MODE" } break } } # switch } catch { Write-FPLog -Category "Error" -Message "service not found: $svcName" } } # foreach Write-FPLog -Category "Info" -Message "--------- services assignments: finish ---------" } function Set-FPControlShortcuts { <# .SYNOPSIS Process Shortcut Controls .DESCRIPTION Process Configuration Control: File and URL Shortcuts .PARAMETER DataSet XML data from control file import .EXAMPLE Set-FPControlShortcuts -DataSet $xmldata #> param ( [parameter(Mandatory = $True)] $DataSet ) Write-FPLog "--------- shortcut assignments: begin ---------" foreach ($sc in $DataSet) { $scDevice = $sc.device $collection = $sc.collection $scName = $sc.name $scAction = $sc.action $scTarget = $sc.target $scPath = $sc.path $scType = $sc.type $scForce = $sc.force $scDesc = $sc.description $scArgs = $sc.args $scWindow = $sc.windowstyle $scWorkPath = $sc.workingpath Write-FPLog "device................: $scDevice" Write-FPLog "collection............: $collection" Write-FPLog "shortcut name.........: $scName" try { if (-not (Test-Path $scPath)) { $scRealPath = [environment]::GetFolderPath($scPath) } else { $scRealPath = $scPath } } catch { $scRealPath = $null } if ($scRealPath) { Write-FPLog "shortcut action: $scAction" switch ($scAction) { 'create' { if ($scWindow.length -gt 0) { switch ($scWindow) { 'normal' {$scWin = 1; break; } 'max' {$scWin = 3; break; } 'min' {$scWin = 7; break; } } } else { $scWin = 1 } Write-FPLog "shortcut path....: $scPath ($scRealPath)" Write-FPLog "shortcut target..: $scTarget" Write-FPLog "shortcut descrip.: $scDesc" Write-FPLog "shortcut args....: $scArgs" Write-FPLog "shortcut workpath: $scWorkPath" Write-FPLog "shortcut window..: $scWindow" Write-FPLog "device name......: $scDevice" $scFullName = "$scRealPath\$scName.$scType" Write-FPLog "full linkpath: $scFullName" if ($scForce -eq 'true' -or (-not(Test-Path $scFullName))) { Write-FPLog "creating new shortcut" try { if (-not $TestMode) { $wShell = New-Object -ComObject WScript.Shell $shortcut = $wShell.CreateShortcut("$scFullName") $shortcut.TargetPath = $scTarget if ($scType -eq 'lnk') { if ($scArgs -ne "") { $shortcut.Arguments = "$scArgs" } #$shortcut.HotKey = "" if ($scWorkPath -ne "") { $shortcut.WorkingDirectory = "$scWorkPath" } $shortcut.WindowStyle = $scWin $shortcut.Description = $scName } #$shortcut.IconLocation = $scFullName $shortcut.Save() } else { Write-FPLog "TEST MODE: $scFullName" } } catch { Write-FPLog -Category "Error" -Message "failed to create shortcut: $($_.Exception.Message)" } } else { Write-FPLog "shortcut already created - no updates" } break } 'delete' { $scFullName = "$scRealPath\$scName.$scType" Write-FPLog "shortcut path....: $scPath" Write-FPLog "device name......: $scDevice" Write-FPLog "full linkpath....: $scFullName" if (Test-Path $scFullName) { Write-FPLog "deleting shortcut" try { if (-not $TestMode) { Remove-Item -Path $scFullName -Force | Out-Null } else { Write-FPLog "TEST MODE: $scFullName" } } catch { Write-FPLog -Category 'Error' -Message $_.Exception.Message } } else { Write-FPLog "shortcut not found: $scFullName" } break } } # switch } else { Write-FPLog -Category "Error" -Message "failed to convert path key" } } # foreach Write-FPLog "--------- shortcut assignments: finish ---------" } function Set-FPControlPermissions { <# .SYNOPSIS Apply Folder and File Permissions Controls .DESCRIPTION Process Configuration Control: ACL Permissions on Files, Folders .PARAMETER DataSet XML data from control file import .EXAMPLE Set-FPControlPermissions -DataSet $xmldata #> param ( [parameter(Mandatory = $True)] $DataSet ) Write-FPLog "--------- permissions assignments: begin ---------" foreach ($priv in $DataSet) { $device = $priv.device $collection = $priv.collection $privPath = $priv.path $privPrinc = $priv.principals $privRights = $priv.rights if ($privPath.StartsWith('HK')) { $privType = 'registry' } else { $privType = 'filesystem' } Write-FPLog "device...........: $device" Write-FPLog "collection.......: $collection" Write-FPLog "priv path........: $privPath" Write-FPLog "priv principals..: $privPrinc" Write-FPLog "priv rights......: $privRights" if (Test-Path $privPath) { switch ($privType) { 'filesystem' { switch ($privRights) { 'full' {$pset = '(OI)(CI)(F)'; break} 'modify' {$pset = '(OI)(CI)(M)'; break} 'read' {$pset = '(OI)(CI)(R)'; break} 'write' {$pset = '(OI)(CI)(W)'; break} 'delete' {$pset = '(OI)(CI)(D)'; break} 'readexecute' {$pset = '(OI)(CI)(RX)'; break} } # switch Write-FPLog "permission set: $pset" if (-not $TestMode) { Write-FPlog "command: icacls `"$privPath`" /grant `"$privPrinc`:$pset`" /T /C /Q" try { icacls "$privPath" /grant "$privPrinc`:$pset" /T /C /Q } catch { Write-FPLog -Category "Error" -Message $_.Exception.Message } } else { Write-FPLog "TESTMODE: icacls `"$privPath`" /grant `"$privPrinc`:$pset`" /T /C /Q" } break } 'registry' { Write-FPLog "registry permissions feature is not yet fully baked" break } } # switch } else { Write-FPLog -Category "Error" -Message "" } } # switch Write-FPLog "--------- permissions assignments: finish ---------" } function Set-FPControlPackages { <# .SYNOPSIS Install Chocolatey Packages .DESCRIPTION Process Configuration Control: Chocolatey Package Installs and Upgrades .PARAMETER DataSet XML data from control file import .EXAMPLE Set-FPControlPackages -DataSet $xmldata #> [CmdletBinding(SupportsShouldProcess = $True)] param ( [parameter(Mandatory = $True)] $DataSet ) Write-FPLog -Category "Info" -Message "--------- installation assignments: begin ---------" foreach ($package in $DataSet) { $deviceName = $package.device $collection = $package.collection $runtime = $package.when $autoupdate = $package.autoupdate $username = $package.user $extparams = $package.params $update = $package.update Write-FPLog -Category "Info" -Message "device......: $deviceName" Write-FPLog -Category "Info" -Message "collection..: $collection" Write-FPLog -Category "Info" -Message "user........: $username" Write-FPLog -Category "Info" -Message "runtime.....: $runtime" Write-FPLog -Category "Info" -Message "autoupdate..: $autoupdate" if (Test-FPControlRuntime -RunTime $runtime) { Write-FPLog -Category "Info" -Message "run: runtime is now or already passed" $pkglist = $package.InnerText -split ',' if ($extparams.length -gt 0) { $params = $extparam } else { $params = ' -y -r' } foreach ($pkg in $pkglist) { Write-FPLog "package...: $pkg" if (Test-Path "$($env:PROGRAMDATA)\chocolatey\lib\$pkg") { if ($update -eq 'true') { Write-FPLog "package is already installed (upgrade)" $params = "upgrade $pkg $params" } else { Write-FPLog "package is already installed (no upgrade.. skip)" break } } else { Write-FPLog "package is not installed (install)" $params = "install $pkg $params" } Write-FPLog "command......: choco $params" if (-not $TestMode) { $p = Start-Process -FilePath "choco.exe" -NoNewWindow -ArgumentList "$params" -Wait -PassThru if ($p.ExitCode -eq 0) { Write-FPLog "package was successful" } else { Write-FPLog -Category 'Error' -Message "package exit code: $($p.ExitCode)" } } else { Write-FPLog "TESTMODE: Would have been applied" } } # foreach } else { Write-FPLog -Category "Info" -Message "skip: not yet time to run this assignment" } } # foreach Write-FPLog -Category "Info" -Message "--------- installation assignments: finish ---------" } function Set-FPControlUpgrades { <# .SYNOPSIS Upgrade Chocolatey Packages .DESCRIPTION Process Configuration Control: Chocolatey Package Upgrades .PARAMETER DataSet XML data from control file import .EXAMPLE Set-FPControlUpgrades -DataSet $xmldata #> [CmdletBinding(SupportsShouldProcess = $True)] param ( [parameter(Mandatory = $True)] $DataSet ) Write-FPLog -Category "Info" -Message "--------- upgrade assignments: begin ---------" foreach ($upgrade in $DataSet) { # later / maybe } Write-FPLog -Category "Info" -Message "--------- upgrade assignments: finish ---------" } function Set-FPControlAppxRemovals { <# .SYNOPSIS Remove Appx Packages .DESCRIPTION Process Configuration Control: Chocolatey Package Removals .PARAMETER DataSet XML data from control file import .EXAMPLE Set-FPControlAppxRemovals -DataSet $xmldata #> [CmdletBinding(SupportsShouldProcess = $True)] param ( [parameter(Mandatory = $True)] $DataSet ) Write-FPLog -Category "Info" -Message "--------- appx removal assignments: begin ---------" foreach ($appx in $DataSet) { $deviceName = $appx.device $runtime = $appx.when $username = $appx.user $appxcomm = $appx.comment Write-FPLog -Category "Info" -Message "device...: $deviceName" Write-FPLog -Category "Info" -Message "user.....: $username" Write-FPLog -Category "Info" -Message "runtime..: $runtime" Write-FPLog -Category "Info" -Message "comment..: $appxcomm" if (Test-FPControlRuntime -RunTime $runtime) { Write-FPLog -Category "Info" -Message "run: runtime is now or already passed" $pkglist = $appx.InnerText -split ',' foreach ($pkg in $pkglist) { Write-FPLog "package...: $pkg" if (-not $TestMode) { try { Get-AppxPackage -AllUsers -ErrorAction Stop | Where-Object {$_.Name -match $pkg} | Remove-AppxPackage -AllUsers -Confirm:$False Write-FPLog -Category "Info" -Message "successfully uninstalled: $pkg" } catch { Write-FPLog -Category 'Error' -Message $_.Exception.Message break } } else { Write-FPLog "TESTMODE: Would have been applied" } } # foreach } else { Write-FPLog -Category "Info" -Message "skip: not yet time to run this assignment" } } # foreach Write-FPLog -Category "Info" -Message "--------- appx removal assignments: finish ---------" } function Set-FPControlRemovals { <# .SYNOPSIS Uninstall Chocolatey Packages .DESCRIPTION Uninstall Chocolatey Packages applicable to this computer .PARAMETER DataSet XML data .EXAMPLE Set-FPControlRemovals -DataSet $xmldata .NOTES #> [CmdletBinding(SupportsShouldProcess = $True)] param ( [parameter(Mandatory = $True)] $DataSet ) Write-FPLog -Category "Info" -Message "--------- removal assignments: begin ---------" foreach ($package in $DataSet) { $deviceName = $package.device $collection = $package.collection $runtime = $package.when $autoupdate = $package.autoupdate $username = $package.user $extparams = $package.params Write-FPLog -Category "Info" -Message "device......: $deviceName" Write-FPLog -Category "Info" -Message "collection..: $collection" Write-FPLog -Category "Info" -Message "user........: $username" Write-FPLog -Category "Info" -Message "autoupdate..: $autoupdate" Write-FPLog -Category "Info" -Message "runtime.....: $runtime" if (Test-FPControlRuntime -RunTime $runtime) { Write-FPLog -Category "Info" -Message "run: runtime is now or already passed" $pkglist = $package.InnerText -split ',' if ($extparams.length -gt 0) { $params = $extparam } else { $params = ' -y -r' } foreach ($pkg in $pkglist) { Write-FPLog "package...: $pkg" if (Test-Path "$($env:PROGRAMDATA)\chocolatey\lib\$pkg") { Write-FPLog "package is installed" $params = "uninstall $pkg $params" } else { Write-FPLog "package is not installed (skip)" break } Write-FPLog "command......: choco $params" if (-not $TestMode) { $p = Start-Process -FilePath "choco.exe" -NoNewWindow -ArgumentList "$params" -Wait -PassThru if ($p.ExitCode -eq 0) { Write-FPLog "removal was successful" } else { Write-FPLog -Category 'Error' -Message "removal exit code: $($p.ExitCode)" } } else { Write-FPLog "TESTMODE: Would have been applied" } } # foreach } else { Write-FPLog -Category "Info" -Message "skip: not yet time to run this assignment" } } # foreach Write-FPLog -Category "Info" -Message "--------- removal assignments: finish ---------" } function Set-FPControlRegistry { <# .SYNOPSIS Process Configuration Control: Registry Settings .DESCRIPTION Process Configuration Control: Registry Settings .PARAMETER DataSet XML data from control file import .EXAMPLE Set-FPControlRegistry -DataSet $xmldata #> [CmdletBinding(SupportsShouldProcess = $True)] param ( [parameter(Mandatory = $True)] $DataSet ) Write-FPLog -Category "Info" -Message "--------- registry assignments: begin ---------" foreach ($reg in $DataSet) { $deviceName = $reg.device $collection = $reg.collection $regAction = $reg.action $regpath = $reg.path $regval = $reg.value $regdata = $reg.data $regtype = $reg.type Write-FPLog -Category "Info" -Message "device......: $deviceName" Write-FPLog -Category "Info" -Message "collection..: $collection" Write-FPLog -Category "Info" -Message "keypath.....: $regpath" Write-FPLog -Category "Info" -Message "action......: $regAction" switch ($regAction) { 'create' { if ($regdata -eq '$controlversion') { $regdata = $controlversion } if ($regdata -eq '$(Get-Date)') { $regdata = Get-Date } Write-FPLog -Category "Info" -Message "value: $regval" Write-FPLog -Category "Info" -Message "data: $regdata" Write-FPLog -Category "Info" -Message "type: $regtype" if (-not(Test-Path $regpath)) { Write-FPLog -Category "Info" -Message "key not found, creating registry key" if (-not $TestMode) { New-Item -Path $regpath -Force | Out-Null Write-FPLog -Category "Info" -Message "updating value assignment to $regdata" New-ItemProperty -Path $regpath -Name $regval -Value "$regdata" -PropertyType $regtype -Force | Out-Null } else { Write-FPLog "TESTMODE: Would have been applied" } } else { Write-FPLog -Category "Info" -Message "key already exists" if (-not $TestMode) { try { $cv = Get-ItemProperty -Path $regpath -Name $regval -ErrorAction SilentlyContinue | Select-Object -ExpandProperty $regval } catch { Write-FPLog -Category "Info" -Message "$regval not found" $cv = "" } Write-FPLog -Category "Info" -Message "current value of $regval is $cv" if ($cv -ne $regdata) { Write-FPLog -Category "Info" -Message "updating value assignment to $regdata" New-ItemProperty -Path $regpath -Name $regval -Value "$regdata" -PropertyType $regtype -Force | Out-Null } } else { Write-FPLog "TESTMODE: Would have been applied" } } break } 'delete' { if (Test-Path $regPath) { if (-not $TestMode) { try { Remove-Item -Path $regPath -Recurse -Force | Out-Null Write-FPLog -Category "Info" -Message "registry key deleted" } catch { Write-FPLog -Category "Error" -Message $_.Exception.Message } } else { Write-FPLog "TESTMODE: Would have been applied" } } else { Write-FPLog -Category "Info" -Message "registry key not found: $regPath" } break } } # switch } # foreach Write-FPLog -Category "Info" -Message "--------- registry assignments: finish ---------" } function Set-FPControlWin32Apps { <# .SYNOPSIS Install Win32 Applications .DESCRIPTION Process Configuration Control: Windows Application Installs and Uninstalls .PARAMETER DataSet XML data from control file import .EXAMPLE Set-FPControlWin32Apps -DataSet $xmldata #> param ( [parameter(Mandatory = $True)] $DataSet ) Write-FPLog -Category "Info" -Message "--------- win32 app assignments: begin ---------" foreach ($app in $DataSet) { $device = $app.device $collection = $app.collection $appName = $app.name $action = $app.action $appPlat = $app.platforms $appRun = $app.run $appParams = $app.params $runtime = $app.when Write-FPLog -Category "Info" -Message "device......: $device" Write-FPLog -Category "Info" -Message "collection..: $collection" Write-FPLog -Category "Info" -Message "appname.....: $appName" Write-FPLog -Category "Info" -Message "app run.....: $appRun" Write-FPLog -Category "Info" -Message "action......: $action" Write-FPLog -Category "Info" -Message "platform....: $appPlat" Write-FPLog -Category "Info" -Message "runtime.....: $runtime" switch ($action) { 'install' { if ($appRun.EndsWith('.msi')) { $proc = "msiexec.exe" $args = "/i `"$appRun`" /q" if ($appParams -ne "") { $args += " $appParams" } } elseif ($appRun.EndsWith('.exe')) { $proc = $appRun $args = $appParams } else { Write-FPLog -Category "Error" -Message "invalid file type" break } Write-FPLog -Category "Info" -Message "process.....: $proc" Write-FPLog -Category "Info" -Message "args........: $args" Write-FPLog -Category "Info" -Message "contacting source to verify availability..." if (Test-Path $appRun) { if (-not $TestMode) { try { $p = Start-Process -FilePath $proc -ArgumentList $args -NoNewWindow -Wait -PassThru if ((0, 3010) -contains $p.ExitCode) { Write-FPLog -Category "Info" -Message "installation successful!" } else { Write-FPLog -Category "Error" -Message "installation failed with $($p.ExitCode)" } } catch { Write-FPLog -Category "Error" -Message $_.Exception.Message } } else { Write-FPLog "TESTMODE: Would have been applied" } } else { Write-FPLog -Category "Info" -Message "installer file is not accessible (skipping)" } break } 'uninstall' { $detect = $app.detect if (Test-FPDetectionRule -DataSet $DataSet -RuleName $detect) { Write-FPLog -Category "Info" -Message "ruletest = TRUE" if ($appRun.StartsWith('msiexec /x')) { $proc = "msiexec" $args = ($appRun -replace ("msiexec", "")).trim() Write-FPLog "process.....: $proc" Write-FPLog "args........: $args" if (-not $TestMode) { try { $p = Start-Process -FilePath $proc -ArgumentList $args -NoNewWindow -Wait -PassThru if ((0, 3010, 1605) -contains $p.ExitCode) { Write-FPLog -Category "Info" -Message "uninstall was successful!" } else { Write-FPLog -Category "Error" -Message "uninstall failed with $($p.ExitCode)" } } catch { Write-FPLog -Category "Error" -Message $_.Exception.Message } } else { Write-FPLog "TESTMODE: Would have been applied" } } } else { Write-FPLog -Category "Info" -Message "ruletest = FALSE" } break } } # switch } # foreach Write-FPLog -Category "Info" -Message "--------- win32 app assignments: finish ---------" } function Set-FPControlWindowsUpdate { <# .SYNOPSIS Run Windows Update Scan and Install Cycle .DESCRIPTION Process Configuration Control: Windows Updates .PARAMETER DataSet XML data from control file import .EXAMPLE Set-FPControlWindowsUpdate -DataSet $xmldata #> param ( [parameter(Mandatory = $True)] $DataSet ) Write-FPLog -Category "Info" -Message "--------- updates assignments: begin ---------" foreach ($dvc in $DataSet) { $device = $dvc.device $collection = $dvc.collection $runtime = $dvc.when Write-FPLog -Category "Info" -Message "device......: $device" Write-FPLog -Category "Info" -Message "collection..: $collection" Write-FPLog -Category "Info" -Message "runtime.....: $runtime" if (Test-FPControlRuntime -RunTime $runtime -Key "LastRunUpdates") { if (-not $TestMode) { Write-FPLog -Category "Info" -Message "run: runtime is now or already passed" try { $Criteria = "IsInstalled=0 and Type='Software'" $Searcher = New-Object -ComObject Microsoft.Update.Searcher $SearchResult = $Searcher.Search($Criteria).Updates $Session = New-Object -ComObject Microsoft.Update.Session $Downloader = $Session.CreateUpdateDownloader() $Downloader.Updates = $SearchResult $Downloader.Download() $Installer = New-Object -ComObject Microsoft.Update.Installer $Installer.Updates = $SearchResult $Result = $Installer.Install() Set-FPConfiguration -Name "LastRunUpdates" -Data (Get-Date) If ($Result.rebootRequired) { Restart-Computer } } catch { if ($_.Exception.Message -like "*0x80240024*") { Write-FPLog -Category 'Info' -Message "No updates are available for download" Set-FPConfiguration -Name "LastRunUpdates" -Data (Get-Date) | Out-Null } else { Write-FPLog -Category 'Error' -Message $_.Exception.Message } } } else { Write-FPLog "TESTMODE: Would have been applied" } } else { Write-FPLog -Category "Info" -Message "skip: not yet time to run this assignment" } } # foreach Write-FPLog -Category "Info" -Message "--------- updates assignments: finish ---------" } function Set-FPControlModules { <# .SYNOPSIS Install PowerShell Modules .DESCRIPTION Install Specified PowerShell Modules .PARAMETER DataSet XML data .EXAMPLE Set-FPControlModules -DataSet $xmldata .NOTES #> [CmdletBinding(SupportsShouldProcess=$True)] param ( [parameter(Mandatory = $True, HelpMessage="XML data")] [ValidateNotNullOrEmpty()] $DataSet ) Write-FPLog -Category "Info" -Message "--------- module assignments: begin ---------" foreach ($module in $DataSet) { $device = $module.device $collection = $module.collection $modname = $module.name $modver = $module.version $runtime = $module.when $comment = $module.comment Write-FPLog -Category "Info" -Message "device......: $device" Write-FPLog -Category "Info" -Message "collection..: $collection" Write-FPLog -Category "Info" -Message "module......: $modname" Write-FPLog -Category "Info" -Message "version.....: $modver" Write-FPLog -Category "Info" -Message "runtime.....: $runtime" Write-FPLog -Category "Info" -Message "comment.....: $comment" if (Test-FPControlRuntime -RunTime $runtime) { Write-FPLog -Category 'Info' -Message "Runtime is now or overdue" if ($m = Get-Module -Name $modname -ListAvailable) { $lv = $m.Version -join '.' Write-FPLog -Category 'Info' -Message "Module version $lv is already installed" if ($r = Find-Module -Name $modname) { $rv = $r.Version -join '.' if ($modver -eq 'latest') { Write-FPLog -Category 'Info' -Message 'Latest version is requested via control policy.' if ($lv -lt $rv) { try { Write-FPLog -Category 'Info' -Message "Updating module to $rv" Update-Module -Name $modname -Force -ErrorAction Stop } catch { } } else { Write-FPLog -Category 'Info' -Message "Local version is the latest. No update required." } } else { Write-FPLog -Category 'Info' -Message 'Specific version is requested via control policy.' if ($lv -lt $modver) { try { Write-FPLog -Category 'Info' -Message "Updating module to $modver" Update-Module -Name $modname -RequiredVersion $modver -ErrorAction Stop } catch { Write-FPLog -Category 'Error' -Message $_.Exception.Message break } } else { Write-FPLog -Category 'Info' -Message "Local version is the latest. No update required." } } } } else { Write-FPLog -Category 'Info' -Message "Module is not installed. Installing it now." try { if (!($TestMode)) { Install-Module -Name $modname -Force -ErrorAction Stop Write-FPLog -Category 'Info' -Message "Module has been installed successfully." } else { Write-FPLog "TESTMODE: Would have been installed" } } catch { Write-FPLog -Category 'Error' -Message "Installation failed: "+$_.Exception.Message } } } else { Write-FPLog -Category 'Info' -Message 'skip: not yet time to run this assignment' } } # foreach Write-FPLog -Category 'Info' -Message '--------- module assignments: finish ---------' } function Write-CenteredText { param ( [parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()] [string] $Caption, [parameter(Mandatory=$False)] [string] $Filler = "*", [parameter(Mandatory=$False)] [int] $MaxLen = 73 ) $caplen = $Caption.Length + 2 $remlen = $MaxLen - $caplen $halflen = [math]::Round($remlen/2,0) $text = "$($Filler*$halflen) $Caption $($Filler*$halflen)" if ($text.Length -lt $MaxLen) { $remx = $MaxLen - $text.Length $text += "$($Filler*$remx)" } Write-Output $text } function Get-FPDeviceCollections { <# .SYNOPSIS Get Device Collection Memberships .DESCRIPTION Get List of Collections this Device is a Member of .PARAMETER XmlData Control Data XML .EXAMPLE $colls = Get-FPDeviceCollections -XmlData $ControlData .NOTES #> param ( [parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()] $XmlData ) try { $($XmlData.configuration.collections.collection | Where-Object {$_.members -match $env:COMPUTERNAME}).name } catch { Write-Output "" } } function Invoke-FPControls { <# .SYNOPSIS Main process invocation .DESCRIPTION Main process for executing FudgePop services .PARAMETER DataSet XML data from control file import .EXAMPLE Invoke-FPControls -DataSet $xmldata #> param ( [parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] $DataSet ) Write-FPLog -Category "Info" -Message $(Write-CenteredText -Caption "control processing: begin") $MyPC = $env:COMPUTERNAME Write-FPLog "module version.....: $($Script:FPVersion)" Write-FPLog "device name........: $MyPC" $collections = Get-FPDeviceCollections -XmlData $DataSet if ($collections -ne "") { Write-FPLog -Category 'Info' -Message "collections........: $($collections -join ',')" } $priority = $DataSet.configuration.priority.order $installs = Get-FPFilteredSet -XmlData $DataSet.configuration.deployments.deployment -Collections $collections $removals = Get-FPFilteredSet -XmlData $DataSet.configuration.removals.removal -Collections $collections $folders = Get-FPFilteredSet -XmlData $DataSet.configuration.folders.folder -Collections $collections $files = Get-FPFilteredSet -XmlData $DataSet.configuration.files.file -Collections $collections $registry = Get-FPFilteredSet -XmlData $DataSet.configuration.registry.reg -Collections $collections $services = Get-FPFilteredSet -XmlData $DataSet.configuration.services.service -Collections $collections $shortcuts = Get-FPFilteredSet -XmlData $DataSet.configuration.shortcuts.shortcut -Collections $collections $opapps = Get-FPFilteredSet -XmlData $DataSet.configuration.opapps.opapp -Collections $collections $updates = Get-FPFilteredSet -XmlData $DataSet.configuration.updates.update -Collections $collections $appx = Get-FPFilteredSet -XmlData $DataSet.configuration.appxremovals.appxremoval -Collections $collections $modules = Get-FPFilteredSet -XmlData $DataSet.configuration.modules.module -Collections $collections $permissions = Get-FPFilteredSet -XmlData $DataSet.configuration.permissions.permission -Collections $collections Write-FPLog "template version...: $($DataSet.configuration.version)" Write-FPLog "template comment...: $($DataSet.configuration.comment)" Write-FPLog "control version....: $($DataSet.configuration.control.version) ***" Write-FPLog "control enabled....: $($DataSet.configuration.control.enabled)" Write-FPLog "control comment....: $($DataSet.configuration.control.comment)" Set-FPConfiguration -Name "TemplateVersion" -Data $DataSet.configuration.version | Out-Null Set-FPConfiguration -Name "ControlVersion" -Data $DataSet.configuration.control.version | Out-Null if (!(Get-FPServiceAvailable -DataSet $DataSet)) { Write-FPLog 'FudgePop is not enabled'; break } Write-FPLog "priority list: $($priority -replace(',',' '))" foreach ($key in $priority -split ',') { switch ($key) { 'folders' { if ($folders) { Set-FPControlFolders -DataSet $folders } else { Write-FPLog -Category 'Info' -Message "no assignments for group: Folders" } break } 'files' { if ($files) { Set-FPControlFiles -DataSet $files } else { Write-FPLog -Category 'Info' -Message "no assignments for group: Files" } break } 'registry' { if ($registry) { Set-FPControlRegistry -DataSet $registry } else { Write-FPLog -Category 'Info' -Message "no assignments for group: Registry" } break } 'deployments' { if ($installs) { Set-FPControlPackages -DataSet $installs } else { Write-FPLog -Category 'Info' -Message "no assignments for group: Package Installs" } break } 'removals' { if ($removals) { Set-FPControlRemovals -DataSet $removals } else { Write-FPLog -Category 'Info' -Message "no assignments for group: Package Removals" } break } 'appxremovals' { if ($appx) { Set-FPControlAppxRemovals -DataSet $appx } else { Write-FPLog -Category 'Info' -Message "no assignments for group: AppxRemovals" } break } 'services' { if ($services) { Set-FPControlServices -DataSet $services } else { Write-FPLog -Category 'Info' -Message "no assignments for group: Services" } break } 'shortcuts' { if ($shortcuts) { Set-FPControlShortcuts -DataSet $shortcuts } else { Write-FPLog -Category 'Info' -Message "no assignments for group: Shortcuts" } break } 'opapps' { if ($opapps) { Set-FPControlWin32Apps -DataSet $opapps } else { Write-FPLog -Category 'Info' -Message "no assignments for group: Win32 Apps" } break } 'permissions' { if ($permissions) { Set-FPControlPermissions -DataSet $permissions } else { Write-FPLog -Category 'Info' -Message "no assignments for group: Permissions" } break } 'updates' { if ($updates) { Set-FPControlWindowsUpdate -DataSet $updates } else { Write-FPLog -Category 'Info' -Message "no assignments for group: WindowsUpdate" } break } 'modules' { if ($modules) { Set-FPControlModules -DataSet $modules } else { Write-FPLog -Category 'Info' -Message "no assignments for group: PowerShell Modules" } break } 'upgrades' { break } default { Write-FPLog -Category 'Error' -Message "invalid priority key: $key"; break } } # switch } # foreach Write-FPLog -Category "Info" -Message $(Write-CenteredText -Caption "control processing: finish") } |