Repository URL to install this package:
|
Version:
6.0.0 ▾
|
#!powershell
# Copyright: (c) 2020, Brian Scholer <@briantist>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#AnsibleRequires -CSharpUtil Ansible.Basic
$spec = @{
supports_check_mode = $true
options = @{
source = @{
type = 'path'
default = '%LOCALAPPDATA%\Microsoft\Windows\PowerShell\PowerShellGet\PSRepositories.xml'
}
name = @{
type = 'list'
elements = 'str'
default = @(
'*'
)
}
exclude = @{
type = 'list'
elements = 'str'
}
profiles = @{
type = 'list'
elements = 'str'
default = @(
'*'
)
}
exclude_profiles = @{
type = 'list'
elements = 'str'
default = @(
'systemprofile'
'LocalService'
'NetworkService'
)
}
}
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$module.Diff.before = @{}
$module.Diff.after = @{}
function Select-Wildcard {
<#
.SYNOPSIS
Compares a value to an Include and Exclude list of wildcards,
returning the input object if a match is found
.DESCRIPTION
If $Property is specified, that property of the input object is
compared rather than the object itself, but the original object
is returned, not the property.
#>
[CmdletBinding()]
[OutputType([object])]
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[object]
$InputObject ,
[Parameter()]
[String]
$Property ,
[Parameter()]
[String[]]
$Include ,
[Parameter()]
[String[]]
$Exclude
)
Process {
$o = if ($Property) {
$InputObject.($Property)
}
else {
$InputObject
}
foreach ($inc in $Include) {
$imatch = $o -like $inc
if ($imatch) {
break
}
}
if (-not $imatch) {
return
}
foreach ($exc in $Exclude) {
if ($o -like $exc) {
return
}
}
$InputObject
}
}
function Get-ProfileDirectory {
<#
.SYNOPSIS
Returns DirectoryInfo objects for each profile on the system, as reported by the registry
.DESCRIPTION
The special "Default" profile, used as a template for newly created users, is explicitly
added to the list of possible profiles returned. Public is explicitly excluded.
Paths reported by the registry that don't exist on the filesystem are silently skipped.
#>
[CmdletBinding()]
[OutputType([System.IO.DirectoryInfo])]
param(
[Parameter()]
[String[]]
$Include ,
[Parameter()]
[String[]]
$Exclude
)
$regPL = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'
# note: this is a key named "Default", not the (Default) key
$default = Get-ItemProperty -LiteralPath $regPL | Select-Object -ExpandProperty Default
# "ProfileImagePath" is always the local side of the profile, even if roaming profiles are used
# This is what we want, because PSRepositories are stored in AppData/Local and don't roam
$profiles = (
@($default) +
(Get-ChildItem -LiteralPath $regPL | Get-ItemProperty | Select-Object -ExpandProperty ProfileImagePath)
) -as [System.IO.DirectoryInfo[]]
$profiles |
Where-Object -Property Exists -EQ $true |
Select-Wildcard -Property Name -Include $Include -Exclude $Exclude
}
function Compare-Hashtable {
<#
.SYNOPSIS
Attempts to naively compare two hashtables by serializing them and string comparing the serialized versions
#>
[CmdletBinding()]
[OutputType([bool])]
param(
[Parameter(Mandatory)]
[hashtable]
$ReferenceObject ,
[Parameter(Mandatory)]
[hashtable]
$DifferenceObject ,
[Parameter()]
[int]
$Depth
)
if ($PSBoundParameters.ContainsKey('Depth')) {
$sRef = [System.Management.Automation.PSSerializer]::Serialize($ReferenceObject, $Depth)
$sDif = [System.Management.Automation.PSSerializer]::Serialize($DifferenceObject, $Depth)
}
else {
$sRef = [System.Management.Automation.PSSerializer]::Serialize($ReferenceObject)
$sDif = [System.Management.Automation.PSSerializer]::Serialize($DifferenceObject)
}
$sRef -ceq $sDif
}
# load the repositories from the source file
try {
$src = $module.Params.source
$src_repos = Import-Clixml -LiteralPath $src -ErrorAction Stop
}
catch [System.IO.FileNotFoundException] {
$module.FailJson("The source file '$src' was not found.", $_)
}
catch {
$module.FailJson("There was an error loading the source file '$src': $($_.Exception.Message).", $_)
}
$profiles = Get-ProfileDirectory -Include $module.Params.profiles -Exclude $module.Params.exclude_profiles
foreach ($user in $profiles) {
$username = $user.Name
$repo_dir = $user.FullName | Join-Path -ChildPath 'AppData\Local\Microsoft\Windows\PowerShell\PowerShellGet'
$repo_path = $repo_dir | Join-Path -ChildPath 'PSRepositories.xml'
if (Test-Path -LiteralPath $repo_path) {
$cur_repos = Import-Clixml -LiteralPath $repo_path
}
else {
$cur_repos = @{}
}
$new_repos = $cur_repos.Clone()
$updated = $false
$src_repos.Keys |
Select-Wildcard -Include $module.Params.name -Exclude $module.Params.exclude |
ForEach-Object -Process {
# explicit scope used inside ForEach-Object to satisfy lint (PSUseDeclaredVarsMoreThanAssignment)
# see https://github.com/PowerShell/PSScriptAnalyzer/issues/827
$Script:updated = $true
$Script:new_repos[$_] = $Script:src_repos[$_]
}
$module.Diff.before[$username] = $cur_repos
$module.Diff.after[$username] = $new_repos
if ($updated -and -not (Compare-Hashtable -ReferenceObject $cur_repos -DifferenceObject $new_repos)) {
if (-not $module.CheckMode) {
$null = New-Item -Path $repo_dir -ItemType Directory -Force -ErrorAction SilentlyContinue
$new_repos | Export-Clixml -LiteralPath $repo_path -Force
}
$module.Result.changed = $true
}
}
$module.ExitJson()