How to create a registry value name with the first available number - powershell

I want to install browser add-ons using local group policy. Here, for example, for Firefox the add-on Zotero Connector. For this I use the Powershell module PolicyFileEditor.
Write-host "Trusting PS Gallery"
Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted -ErrorAction Stop
Write-Host "Installing PolicyFileEditor"
Install-Module -Name PolicyFileEditor -Scope CurrentUser -ErrorAction Stop
Write-Host "Installing Firefox Add-On (set LGPO)"
$MachineDir = "$env:windir\system32\GroupPolicy\Machine\registry.pol"
$RegPath = 'SOFTWARE\Policies\Mozilla\Firefox\Extensions\Install'
$RegData = 'https://download.zotero.org/connector/firefox/release/Zotero_Connector-5.0.102.xpi'
$RegName = '1'
$RegType = 'ExpandString'
Set-PolicyFileEntry -Path $MachineDir -Key $RegPath -ValueName $RegName -Data $RegData -Type $RegType -ErrorAction Stop
So far so good. But now I want to avoid that a potentially already existing value name (= an already installed add-on) is overwritten. Therefore I don't want $RegName = '1', but the next not existing number as name (string), e.g. $RegName = '2' or $RegName = '3', if 1 and 2 are already available as value names etc. etc.
Currently I use the following:
$RegName = '1'
if (Get-PolicyFileEntry $MachineDir -Key $RegPath -ValueName $RegName) { $RegName = '2' }
if (Get-PolicyFileEntry $MachineDir -Key $RegPath -ValueName $RegName) { $RegName = '3' }
if (Get-PolicyFileEntry $MachineDir -Key $RegPath -ValueName $RegName) { $RegName = '4' }
With this option I just assume that no more than 4 add-ons are installed.
Isn't there a nicer and more purposeful solution (e.g. with RegEx), which I can pass to the variable $RegName?
Somthing like: $RegName = "Take the next available (not existing) number as name (string)"

Related

Required registry changes to adjust Windows screen brightness?

Windows 10 21H2 19044.1949
Powershell 7.2.6
I wrote a script to adjust the Windows screen brightness, based on
https://winaero.com/change-screen-brightness-registry-tweak/
https://devblogs.microsoft.com/scripting/get-windows-power-plan-settings-on-your-computer-by-using-powershell/
https://stackoverflow.com/a/35844259/308851 (the function 'Take-Permissions' was copied from here, unmodified)
However, it doesn't do the job. The appropriate registry values get set as desired, but the screen brightness doesn't actually change. When I open Windows settings->System->Screen, the brightness values are still unchanged. If I change them manually there, the registry values get updated accordingly. What am I missing?
# Show / set brightness
param ( [parameter(Mandatory=$false)]
[Int32]
[ValidateRange(0,100)]
$number = -1
);
function Take-Permissions {
# SOURCE: https://stackoverflow.com/a/35844259/308851
# Developed for PowerShell v4.0
# Required Admin privileges
# Links:
# http://shrekpoint.blogspot.ru/2012/08/taking-ownership-of-dcom-registry.html
# http://www.remkoweijnen.nl/blog/2012/01/16/take-ownership-of-a-registry-key-in-powershell/
# https://powertoe.wordpress.com/2010/08/28/controlling-registry-acl-permissions-with-powershell/
param($rootKey, $key, [System.Security.Principal.SecurityIdentifier]$sid = 'S-1-5-32-545', $recurse = $true)
switch -regex ($rootKey) {
'HKCU|HKEY_CURRENT_USER' { $rootKey = 'CurrentUser' }
'HKLM|HKEY_LOCAL_MACHINE' { $rootKey = 'LocalMachine' }
'HKCR|HKEY_CLASSES_ROOT' { $rootKey = 'ClassesRoot' }
'HKCC|HKEY_CURRENT_CONFIG' { $rootKey = 'CurrentConfig' }
'HKU|HKEY_USERS' { $rootKey = 'Users' }
}
### Step 1 - escalate current process's privilege
# get SeTakeOwnership, SeBackup and SeRestore privileges before executes next lines, script needs Admin privilege
$import = '[DllImport("ntdll.dll")] public static extern int RtlAdjustPrivilege(ulong a, bool b, bool c, ref bool d);'
$ntdll = Add-Type -Member $import -Name NtDll -PassThru
$privileges = #{ SeTakeOwnership = 9; SeBackup = 17; SeRestore = 18 }
foreach ($i in $privileges.Values) {
$null = $ntdll::RtlAdjustPrivilege($i, 1, 0, [ref]0)
}
function Take-KeyPermissions {
param($rootKey, $key, $sid, $recurse, $recurseLevel = 0)
### Step 2 - get ownerships of key - it works only for current key
$regKey = [Microsoft.Win32.Registry]::$rootKey.OpenSubKey($key, 'ReadWriteSubTree', 'TakeOwnership')
$acl = New-Object System.Security.AccessControl.RegistrySecurity
$acl.SetOwner($sid)
$regKey.SetAccessControl($acl)
### Step 3 - enable inheritance of permissions (not ownership) for current key from parent
$acl.SetAccessRuleProtection($false, $false)
$regKey.SetAccessControl($acl)
### Step 4 - only for top-level key, change permissions for current key and propagate it for subkeys
# to enable propagations for subkeys, it needs to execute Steps 2-3 for each subkey (Step 5)
if ($recurseLevel -eq 0) {
$regKey = $regKey.OpenSubKey('', 'ReadWriteSubTree', 'ChangePermissions')
$rule = New-Object System.Security.AccessControl.RegistryAccessRule($sid, 'FullControl', 'ContainerInherit', 'None', 'Allow')
$acl.ResetAccessRule($rule)
$regKey.SetAccessControl($acl)
}
### Step 5 - recursively repeat steps 2-5 for subkeys
if ($recurse) {
foreach($subKey in $regKey.OpenSubKey('').GetSubKeyNames()) {
Take-KeyPermissions $rootKey ($key+'\'+$subKey) $sid $recurse ($recurseLevel+1)
}
}
}
Take-KeyPermissions $rootKey $key $sid $recurse
}
### MAIN ###
# get the GUID of the current active power plan:
$guid = (Get-CimInstance -classname Win32_PowerPlan -Namespace "root\cimv2\power" -Filter "IsActive='True'").InstanceID.tostring()
$regex = [regex]"{(.*?)}$"
$planGuid = $regex.Match($guid).groups[1].value
# set the registry path values:
$regPath0="HKLM"
$regPath1=$regPath0+":\"
$regPath2= "SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes\"+$planGuid+"\7516b95f-f776-4464-8c53-06167f40cc99\aded5e82-b909-4619-9949-f5d71dac0bcb"
$regPath=$regPath1+$regPath2
$regProp1="ACSettingIndex";
$regProp2="DCSettingIndex";
if ($number -eq -1) {
Write-Host "Current brightness:" (Get-ItemProperty $regPath $regProp2).$regProp2
} else {
# initially, Admin has no rights to modify this key:
$AdminGroupSid="S-1-5-32-544"
Take-Permissions $regPath0 $regPath2 $AdminGroupSid $false
Set-ItemProperty $regPath $regProp1 $number
Set-ItemProperty $regPath $regProp2 $number
# verify the new setting:
Write-Host "Brightness set to:" (Get-ItemProperty $regPath $regProp2).$regProp2
}
As mentionned by Daniel in the comment, the changes in the registry are not taken into account. I suppose when you change the settings from the Control Panel Windows is doing another stuff in the background. It reminds me this question where the OP had to run a P/Invoke to disable immediately the Stick Key.
Anyway, it looks very complicated for this simple task. Why not using WMI ?
https://learn.microsoft.com/fr-fr/windows/win32/wmicoreprov/wmisetbrightness-method-in-class-wmimonitorbrightnessmethods
$brightness = 75
$timeout = 0
$(Get-WmiObject -ns root/wmi -class wmiMonitorBrightNessMethods).WmiSetBrightness($timeout,$brightness)
Edit: without using the depreciated Get-WMiObject:
Get-CimInstance -Namespace "root/WMI" -ClassName "WmiMonitorBrightnessMethods" | Invoke-CimMethod -MethodName "WmiSetBrightness" -Arguments #{Timeout=$timeout; Brightness=$brightness;}
Short script to set and/or query monitor brightness:
param ( [parameter(Mandatory=$false)]
[Int32]
[ValidateRange(0,100)]
$number = -1
);
if ($number -ne -1) {
Get-CimInstance -Namespace "root/WMI" -ClassName "WmiMonitorBrightnessMethods" |
Invoke-CimMethod -MethodName "WmiSetBrightness" -Arguments #{Timeout=0; Brightness=$number;} |
Out-Null
}
Write-Host Brightness is (Get-Ciminstance -Namespace root/WMI -ClassName WmiMonitorBrightness).CurrentBrightness %

Local Group Policy Powershell script to add MyGlue extension in chrome and edge

**I installed the module PolicyFileEditor through the command "Install-Module -Name PolicyFileEditor"
Is there a way in which i can write a script using this module that would add MyGlue on Favorites Bar (with favorites bar being always shown) for Chrome and Edge**
I have done it through the LGPO but not through PowerShell script.
We cant do it manually as we are the MSP and we need to push this script through our RMM on all the workstations which we manage
Any help is appreciated
PS: This Needs to be a powershell script for LGPO as the workstations are not in domain but workgroup.
Thanks
I'd do it like this:
Write-host "Trusting PS Gallery"
Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted -ErrorAction Stop
Write-Host "Installing PolicyFileEditor"
Install-Module -Name PolicyFileEditor -Scope CurrentUser -ErrorAction Stop
Write-Host "Installing MyGlue Chrome Extension Add-On (set LGPO)"
$MachineDir = "$env:windir\system32\GroupPolicy\Machine\registry.pol"
$RegPath = 'SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist'
$RegData = 'bfcdaalpeodhimbiipneeaoeogkkminc;https://clients2.google.com/service/update2/crx'
$RegName = '1'
$RegType = 'ExpandString'
Set-PolicyFileEntry -Path $MachineDir -Key $RegPath -ValueName $RegName -Data $RegData -Type $RegType -ErrorAction Stop
Write-Host "Updating LGPO w/o restart"
echo N | gpupdate.exe /target:Computer /force
But please make sure that the value name '1' does not already exist for another add-on, otherwise it will be overwritten.
You could solve it e.g. like this:
$RegName = '1'
if ((Get-ItemProperty "HKLM:$RegPath").PSObject.Properties.Name -contains $RegName) { $RegName = '2' }
if ((Get-ItemProperty "HKLM:$RegPath").PSObject.Properties.Name -contains $RegName) { $RegName = '3' }
if ((Get-ItemProperty "HKLM:$RegPath").PSObject.Properties.Name -contains $RegName) { $RegName = '4' }
But this is not nice, maybe there is a better solution: e.g. with RegEx or a loop, which checks the value names and takes the next free available number as name.
Somthing like: $RegName = "Take the next available (not existing) number as name (string)"

Cannot install fonts with Powershell on Windows 10

On my work computer, I don't have admin privileges.
Installing new fonts cannot be done "the easy way".
At the time I was using Windows 7, I managed to run a PowerShell script that was launched at session startup and that installed the fonts from a given folder.
Here is the code I used:
add-type -name Session -namespace "" -member #"
[DllImport("gdi32.dll")]
public static extern int AddFontResource(string filePath);
"#
$FontFolder = "C:\Users\myusername\Documents\Fonts"
$null = foreach($font in Get-ChildItem -Path $FontFolder -Recurse -Include *.ttf, *.otg, *.otf) {
Write-Host "Installing : $($font.FullName)"
$result = [Session]::AddFontResource($font.FullName)
Write-Host "Installed $($result) fonts"
}
Now that I have switched to Windows 10, I thought I could go back to installing fonts "the easy way", as it is supposed to be possible to install fonts for your user without admin privileges.
This however still does not work: there is a popup window saying that "The requested file is not a valid font file". One solution is apparently to start the Windows firewall, which of course is not allowed by my administrator... but it is already running (see Edit below)
Back to the PowerShell then. The script unfortunately does not work anymore and does not provide any interesting pointers to where the problem comes from:
Installing : C:\Users\myusername\Documents\Fonts\zilla-slab\ZillaSlab-SemiBold.otf
Installed 0 fonts
Installing : C:\Users\myusername\Documents\Fonts\zilla-slab\ZillaSlab-SemiBoldItalic.otf
Installed 0 fonts
Installing : C:\Users\myusername\Documents\Fonts\zilla-slab\ZillaSlabHighlight-Bold.otf
Installed 0 fonts
I tried using a try catch, but still have no identified error:
add-type -name Session -namespace "" -member #"
[DllImport("gdi32.dll")]
public static extern int AddFontResource(string filePath);
"#
$FontFolder = "C:\Users\myusername\Documents\Fonts"
$null = foreach($font in Get-ChildItem -Path $FontFolder -Recurse -Include *.ttf, *.otg, *.otf) {
try {
Write-Host "Installing : $($font.FullName)"
$result = [Session]::AddFontResource($font.FullName)
Write-Host $result
}
catch {
Write-Host "An error occured installing $($font)"
Write-Host "$($error)"
Write-Host "$($error[0].ToString())"
Write-Host ""
1
}
}
And the resulting output
Installing : C:\Users\myusername\Documents\Fonts\zilla-slab\ZillaSlabHighlight-Bold.otf
0
Installing : C:\Users\myusername\Documents\Fonts\zilla-slab\ZillaSlabHighlight-Regular.otf
0
Installing : C:\Users\myusername\Documents\Fonts\ZillaSlab-Light.otf
0
Any idea how to solve this issue?
Edit:
Regarding the status of the security applications, here is the McAfee status:
McAfee Data Exchange Layer OK
McAfee DLP Endpoint OK
Programme de mise à jour McAfee OK
McAfee Endpoint Security OK
"Programme de mise à jour" means "update program" in French.
I also checked the list of running services :
mpssvc service (Windows defender firewall) is running
mfefire (McAfee Firewall core service) is not running
Edit2:
My last attempt is the following:
I copied the font file manually to the $($env:LOCALAPPDATA)\Microsoft\Windows\Fonts\ folder
Using regedit, I added the entry as shown below
I restarted. Still no Bebas font in WordPad or Publisher
Here's how I do it with a com object. This works for me as non-admin based on Install fonts without administrative privileges. I can see the fonts installed to "$env:LOCALAPPDATA\Microsoft\Windows\Fonts" in the Fonts area under Settings. I have Windows 10 20H2 (it should work in 1803 or higher). I also see the fonts installed in Wordpad.
$Destination = (New-Object -ComObject Shell.Application).Namespace(20)
$TempFolder = "$($env:windir)\Temp\Fonts\"
New-Item -Path $TempFolder -Type Directory -Force | Out-Null
Get-ChildItem -Path $PSScriptRoot\fonts\* -Include '*.ttf','*.ttc','*.otf' |
ForEach {
If (-not(Test-Path "$($env:LOCALAPPDATA)\Microsoft\Windows\Fonts\$($_.Name)")) {
$Font = "$($env:windir)\Temp\Fonts\$($_.Name)"
Copy-Item $($_.FullName) -Destination $TempFolder
$Destination.CopyHere($Font)
Remove-Item $Font -Force
} else { "font $($env:LOCALAPPDATA)\Microsoft\Windows\Fonts\$($_.Name) already installed" }
}
Example REG_SZ registry entry:
dir 'HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Fonts*' | ft -a
Hive: HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion
Name Property
---- --------
Fonts Nunito Black (TrueType) : C:\Users\myuser\AppData\Local\Microsoft\Windows\Fonts\Nunito-Black.ttf
You can install fonts on windows using following powershell scripts.
param(
[Parameter(Mandatory=$true,Position=0)]
[ValidateNotNull()]
[array]$pcNames,
[Parameter(Mandatory=$true,Position=1)]
[ValidateNotNull()]
[string]$fontFolder
)
$padVal = 20
$pcLabel = "Connecting To".PadRight($padVal," ")
$installLabel = "Installing Font".PadRight($padVal," ")
$errorLabel = "Computer Unavailable".PadRight($padVal," ")
$openType = "(Open Type)"
$regPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"
$objShell = New-Object -ComObject Shell.Application
if(!(Test-Path $fontFolder))
{
Write-Warning "$fontFolder - Not Found"
}
else
{
$objFolder = $objShell.namespace($fontFolder)
foreach ($pcName in $pcNames)
{
Try{
Write-Output "$pcLabel : $pcName"
$null = Test-Connection $pcName -Count 1 -ErrorAction Stop
$destination = "\\",$pcname,"\c$\Windows\Fonts" -join ""
foreach ($file in $objFolder.items())
{
$fileType = $($objFolder.getDetailsOf($file, 2))
if(($fileType -eq "OpenType font file") -or ($fileType -eq "TrueType font file"))
{
$fontName = $($objFolder.getDetailsOf($File, 21))
$regKeyName = $fontName,$openType -join " "
$regKeyValue = $file.Name
Write-Output "$installLabel : $regKeyValue"
Copy-Item $file.Path $destination
Invoke-Command -ComputerName $pcName -ScriptBlock { $null = New-ItemProperty -Path $args[0] -Name $args[1] -Value $args[2] -PropertyType String -Force } -ArgumentList $regPath,$regKeyname,$regKeyValue
}
}
}
catch{
Write-Warning "$errorLabel : $pcName"
}
}
}

Enable Windows 10 Developer Mode programmatically

I know you can enable Windows 10 Developer mode interactively by going to Settings | For developers, selecting 'Developer mode' and then rebooting.
Is there a way to enable this programmatically? (eg. via PowerShell or similar so that I can include it as a step in a Boxstarter script when refreshing my developer workstation)
Turns out Nickolaj Andersen has written an article which includes just such a PowerShell script..
http://www.scconfigmgr.com/2016/09/11/enable-ubuntu-in-windows-10-during-osd-with-configmgr/
Here are the relevant lines extracted from his post:
# Create AppModelUnlock if it doesn't exist, required for enabling Developer Mode
$RegistryKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
if (-not(Test-Path -Path $RegistryKeyPath)) {
New-Item -Path $RegistryKeyPath -ItemType Directory -Force
}
# Add registry value to enable Developer Mode
New-ItemProperty -Path $RegistryKeyPath -Name AllowDevelopmentWithoutDevLicense -PropertyType DWORD -Value 1
I modified the accepted answer and ended up with the following script:
param([Switch]$WaitForKey)
if (([Version](Get-CimInstance Win32_OperatingSystem).version).Major -lt 10)
{
Write-Host -ForegroundColor Red "The DeveloperMode is only supported on Windows 10"
exit 1
}
# Get the ID and security principal of the current user account
$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)
# Get the security principal for the Administrator role
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator
if ($myWindowsPrincipal.IsInRole($adminRole))
{
$RegistryKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
if (! (Test-Path -Path $RegistryKeyPath))
{
New-Item -Path $RegistryKeyPath -ItemType Directory -Force
}
if (! (Get-ItemProperty -Path $RegistryKeyPath -Name AllowDevelopmentWithoutDevLicense))
{
# Add registry value to enable Developer Mode
New-ItemProperty -Path $RegistryKeyPath -Name AllowDevelopmentWithoutDevLicense -PropertyType DWORD -Value 1
}
$feature = Get-WindowsOptionalFeature -FeatureName Microsoft-Windows-Subsystem-Linux -Online
if ($feature -and ($feature.State -eq "Disabled"))
{
Enable-WindowsOptionalFeature -FeatureName Microsoft-Windows-Subsystem-Linux -Online -All -LimitAccess -NoRestart
}
if ($WaitForKey)
{
Write-Host -NoNewLine "Press any key to continue..."
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}
}
else
{
# We are not running "as Administrator" - so relaunch as administrator
# Create a new process object that starts PowerShell
$newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell";
# Specify the current script path and name as a parameter
$newProcess.Arguments = "-NoProfile",$myInvocation.MyCommand.Definition,"-WaitForKey";
# Indicate that the process should be elevated
$newProcess.Verb = "runas";
# Start the new process
[System.Diagnostics.Process]::Start($newProcess);
# Exit from the current, unelevated, process
exit
}
It automatically elevates itself, if not already running elevated and enables the optional feature mentioned by Julian Knight.

Installing Citrix receiver VIA PowerShell

I'm trying to create a script to install receiver but I'm getting this popup box asking if I'm sure.
$InstallFiles = "C:\Users\raw.admin\Documents\CitrixReceiver.exe"
$ArgumentList = '/silent /includeSSON enable_SSON=yes enableprelaunch=true allowaddstore=a STORE0="Kiewit;https://apps.kiewit.com/citrix/kiewit/discovery;on"'
Write-host "Installing Citrix receiver"
Start-Process -FilePath $InstallFiles -ArgumentList $ArgumentList -wait
##Make sure Single Sign is in Provider Order Key
$Path = "HKLM:\system\CurrentControlSet\Control\NetworkProvider\Order"
$ProviderOrder = (Get-ItemProperty -path $path).ProviderOrder
If ($ProviderOrder -NotLike "*PnSson*")
{
Set-ItemProperty -path $path -Name ProviderOrder -value ($ProviderOrder + ",PnSson")
$NewProviderOrder = (Get-ItemProperty -path $path).ProviderOrder
Write-Host Provider Order key now has the following values $NewProviderOrder
}
Else
{
Write-Host "PnSson already present in Provider Order"
}
Write-host "Installation Complete"
I don't want the user to get prompt to answer the question of yes or no. Can it be done or is it something the user will just have to live with?