#
.SYNOPSIS
Module for Intune tools
.DESCRIPTION
This module is for the Intune Tools View.
# Full ADMX reference can be found here (from 2007):
# http://download.microsoft.com/download/5/0/8/5081217f-4a2a-470e-a7fa-5976e40b0839/Group%20Policy%20ADMX%20Syntax%20Reference%20Guide.doc
# Schema documented 2017
# https://docs.microsoft.com/en-us/previous-versions/windows/desktop/policy/admx-schema
# ADMX schema reference can be found here
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-gpreg/6e10478a-e9e6-4fdc-a1f6-bdd9bd7f2209
.NOTES
Author: Mikael Karlsson
#>
$global:EMToolsViewObject = $null
function Get-ModuleVersion
{
'1.0.5'
}
function Invoke-InitializeModule
{
Add-ADMXRegClasses
Add-EMToolsViewItem
# https://docs.microsoft.com/en-us/windows/client-management/mdm/win32-and-centennial-app-policy-configuration
# ADMX ingestion cannot write to these paths:
$script:unsupportedLocations = @('System','Software\Microsoft','Software\Policies\Microsoft')
# With excemption for these paths:
$script:unsupportedOverride = @('Software\Policies\Microsoft\Office','Software\Microsoft\Office','Software\Microsoft\Windows\CurrentVersion\Explorer','Software\Microsoft\Internet Explorer','software\policies\microsoft\shared tools\proofing tools','software\policies\microsoft\imejp','software\policies\microsoft\ime\shared','software\policies\microsoft\shared tools\graphics filters','software\policies\microsoft\windows\currentversion\explorer','software\policies\microsoft\softwareprotectionplatform','software\policies\microsoft\officesoftwareprotectionplatform','software\policies\microsoft\windows\windows search\preferences','software\policies\microsoft\exchange','software\microsoft\shared tools\proofing tools','software\microsoft\shared tools\graphics filters','software\microsoft\windows\windows search\preferences','software\microsoft\exchange','software\policies\microsoft\vba\security','software\microsoft\onedrive','software\Microsoft\Edge','Software\Microsoft\EdgeUpdate')
$script:admxTemplate = @"
"@
}
function Add-EMToolsViewItem
{
param($viewItem)
if(-not $global:EMToolsViewObject)
{
$viewPanel = Get-XamlObject ($global:AppRootFolder + "\Xaml\EndpointManagerTools.xaml") -AddVariables
if(-not $viewPanel) { return }
#Add menu group and items
$global:EMToolsViewObject = (New-Object PSObject -Property @{
Title = "Intune Tools"
Description = "Additional tools for managing Intune"
ID = "EMTools"
AuthenticationID = "MSAL"
ViewPanel = $viewPanel
ItemChanged = { Show-EMTool }
Activating = { Invoke-EMToolsActivatingView }
Authentication = (Get-MSALAuthenticationObject)
Authenticate = { Invoke-EMToolsAuthenticateToMSAL }
AppInfo = (Get-GraphAppInfo "EMAzureApp" $global:DefaultAzureApp)
SaveSettings = { Invoke-EMSaveSettings }
Permissions = @()
})
Add-ViewObject $global:EMToolsViewObject
Add-ViewItem (New-Object PSObject -Property @{
Title = "ADMX Import"
Id = "ADMXImport"
ViewID = "EMTools"
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
Icon="DeviceConfiguration"
ShowViewItem = { Show-ADMXIngestion }
})
Add-ViewItem (New-Object PSObject -Property @{
Title = "Reg Values"
Id = "ADMXRegValues"
ViewID = "EMTools"
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
Icon="DeviceConfiguration"
ShowViewItem = { Show-ADMXRegValues }
})
}
if($viewItem)
{
Add-ViewItem $viewItem
}
}
function Invoke-EMToolsActivatingView
{
}
function Invoke-EMToolsAuthenticateToMSAL
{
$global:EMToolsViewObject.AppInfo = Get-GraphAppInfo "EMAzureApp" $global:DefaultAzureApp
Set-MSALCurrentApp $global:EMToolsViewObject.AppInfo
$usr = (?? $global:MSALToken.Account.UserName (Get-Setting "" "LastLoggedOnUser"))
if($usr)
{
& $global:msalAuthenticator.Login -Account $usr
}
}
function Show-EMTool
{
if($global:lstMenuItems.SelectedItem.ShowViewItem)
{
& $global:lstMenuItems.SelectedItem.ShowViewItem
}
else
{
$global:grdToolsMain.Children.Clear()
}
}
#region ADMX functions
function Show-ADMXIngestion
{
if(-not $script:admxPanel)
{
$script:admxPanel = Get-XamlObject ($global:AppRootFolder + "\Xaml\EndpointManagerToolsADMX.xaml") -AddVariables
if(-not $script:admxPanel) { return }
$global:btnADMXLoadADMX.Add_Click({
$of = [System.Windows.Forms.OpenFileDialog]::new()
$of.Multiselect = $false
$of.Filter = "ADMX Files (*.admx)|*.admx"
$of.FileName = Get-Setting "Tools" "ADMXLastADMXFile"
if($of.ShowDialog() -eq "OK")
{
$script:currentADMXFile = [IO.FileInfo]$of.FileName
Write-Status "Loading policy settings from $($script:currentADMXFile.Name)"
Save-Setting "Tools" "ADMXLastADMXFile" $of.FileName
Start-AdmxLoadFile $of.FileName
Write-Status ""
}
})
$global:btnADMXLoadADML.Add_Click({
$of = [System.Windows.Forms.OpenFileDialog]::new()
$of.Multiselect = $false
$of.Filter = "ADML Files (*.adml)|*.adml"
$of.FileName = Get-Setting "Tools" "ADMXLastADMLFile"
if($of.ShowDialog() -eq "OK")
{
Write-Status "Loading ADML policy $($of.FileName)"
Save-Setting "Tools" "ADMXLastADMLFile" $of.FileName
Invoke-LoadADMXSettings $of.FileName
Write-Status ""
}
})
$global:btnADMXImport.Add_Click({
Write-Status "Import policy"
Import-ADMXPolicy
Write-Status ""
})
$global:btnADMXPolicyNameRandom.Add_Click({
$guid = [Guid]::NewGuid()
if($global:txtADMXPolicyFileName.Text)
{
if($global:txtADMXPolicyFileName.Text.Split('_')[-1].Length -ne $guid.Guid.Length)
{
$global:txtADMXPolicyFileName.Text = ($global:txtADMXPolicyFileName.Text + "_" + $guid.Guid)
}
}
else
{
$global:txtADMXPolicyFileName.Text = $guid.Guid
}
})
<#
$global:txtADMXFilterSettings.Add_LostFocus({
Invoke-ADMXFilterPolicies $this
})
$global:txtADMXFilterSettings.Add_GotFocus({
if($this.Tag -eq "1" -and $this.Text -eq "Filter") { $this.Text = "" }
Invoke-ADMXFilterPolicies $this
})
$global:txtADMXFilterSettings.Add_TextChanged({
Invoke-ADMXFilterPolicies $this
})
Invoke-ADMXFilterPolicies $global:txtADMXFilterSettings
#>
$global:tvADMXCategories.Add_SelectedItemChanged({
if($global:tvADMXCategories.SelectedItem.AllPolicies -eq $true)
{
$global:dgADMXCategoryPolicies.ColumnWidth = [System.Windows.Controls.DataGridLength]::Auto
$global:dgADMXCategoryPolicies.Columns[0].Width = [System.Windows.Controls.DataGridLength]::Auto
$global:dgADMXCategoryPolicies.Columns[1].Width = [System.Windows.Controls.DataGridLength]::Auto
$global:dgADMXCategoryPolicies.Columns[2].Width = [System.Windows.Controls.DataGridLength]::Auto
$global:dgADMXCategoryPolicies.Columns[2].Visibility = "Visible"
$script:ocADMXSettingsList = [System.Collections.ObjectModel.ObservableCollection[object]]::new(@($global:tvADMXCategories.SelectedItem.Tag))
}
else
{
$global:dgADMXCategoryPolicies.ColumnWidth = [System.Windows.Controls.DataGridLength]"*"
$global:dgADMXCategoryPolicies.Columns[0].Width = [System.Windows.Controls.DataGridLength]"10*"
$global:dgADMXCategoryPolicies.Columns[1].Width = [System.Windows.Controls.DataGridLength]::Auto
$global:dgADMXCategoryPolicies.Columns[2].Visibility = "Collapsed"
$script:ocADMXSettingsList = [System.Collections.ObjectModel.ObservableCollection[object]]::new(@($script:admxPolicies | Where { $_.CategoryId -eq $global:tvADMXCategories.SelectedItem.Tag.CategoryName -and $_.SettingClass -eq $global:tvADMXCategories.SelectedItem.Tag.SettingClass}))
}
$global:dgADMXCategoryPolicies.ItemsSource = $script:ocADMXSettingsList
})
$global:dgADMXCategoryPolicies.Add_MouseDoubleClick({
if(-not $global:dgADMXCategoryPolicies.SelectedItem) { return }
Show-ADMXSettingProperties $global:dgADMXCategoryPolicies.SelectedItem
})
$global:mnuADMXSettingsContextMenu.Add_Opened({
$global:mnuADMXSettingEdit.IsEnabled = $null -ne $global:dgADMXCategoryPolicies.SelectedItem
})
$global:mnuADMXSettingEdit.Add_Click({
if(-not $global:dgADMXCategoryPolicies.SelectedItem) { return }
Show-ADMXSettingProperties $global:dgADMXCategoryPolicies.SelectedItem
})
$winADMLFile = "$($env:WinDir)\PolicyDefinitions\en-US\Windows.adml"
if([IO.File]::Exists($winADMLFile))
{
[xml]$script:windowsADML = Get-Content $winADMLFile
}
else
{
Write-Log "Could not find Windows.adml. Support OS text might not be displayed correctly" 2
}
}
$global:grdToolsMain.Children.Clear()
$global:grdToolsMain.Children.Add($script:admxPanel)
}
function Show-ADMXSettingProperties
{
param($settingObj)
$script:settingsForm = Get-XamlObject ($global:AppRootFolder + "\Xaml\EndpointManagerToolsADMXSettingProperties.xaml") -AddVariables
if(-not $script:settingsForm) { return }
class ListValue
{
[string]$Key
[string]$Value
}
Set-ADMXSettingProperties $settingObj
Add-XamlEvent $script:settingsForm "btnADMXSettingsOK" "add_click" ({
Save-ADMXSettings
Show-ModalObject
})
Add-XamlEvent $script:settingsForm "btnADMXPreviousSetting" "add_click" ({
if($global:dgADMXCategoryPolicies.SelectedIndex -eq 0) { return }
Save-ADMXSettings
$global:dgADMXCategoryPolicies.SelectedIndex = $global:dgADMXCategoryPolicies.SelectedIndex - 1
Set-ADMXSettingButtonsStatus
Set-ADMXSettingProperties $global:dgADMXCategoryPolicies.SelectedItem
})
Add-XamlEvent $script:settingsForm "btnADMXNextSetting" "add_click" ({
if($global:dgADMXCategoryPolicies.SelectedIndex -eq ($global:dgADMXCategoryPolicies.ItemsSource.Count - 1)) { return }
Save-ADMXSettings
$global:dgADMXCategoryPolicies.SelectedIndex = $global:dgADMXCategoryPolicies.SelectedIndex + 1
Set-ADMXSettingButtonsStatus
Set-ADMXSettingProperties $global:dgADMXCategoryPolicies.SelectedItem
})
Add-XamlEvent $script:settingsForm "btnADMXSettingsCancel" "add_click" ({
$global:grdADMXElements.Children.Clear()
$script:settingsForm = $null
Show-ModalObject
})
$global:rbADMXSettingEnabled.Add_Checked({
$script:curItemSettingStatus = 1
Set-ADMXControlStatus
})
$global:rbADMXSettingDisabled.Add_Checked({
$script:curItemSettingStatus = 0
Set-ADMXControlStatus
})
$global:rbADMXSettingNotConfigured.Add_Checked({
$script:curItemSettingStatus = $null
Set-ADMXControlStatus
})
$global:chkADMXManualConfig.Add_Click({
Set-ADMXControlStatus
})
$global:tcADMXPolicyConfig.Add_SelectionChanged({
param($sender, $e)
if($e.AddedItems[0] -eq $global:tabADMXSettings)
{
if($global:dgADMXSettings.SelectedItem.ManualConfig -ne 1)
{
$global:txtADMXSettings.Text = Get-ADMXSettingsString $script:settingsForm.DataContext
}
}
})
Show-ModalForm $settingObj.Name $script:settingsForm -HideButtons
}
function Save-ADMXSettings
{
if($global:chkADMXManualConfig.IsChecked)
{
$script:settingsForm.DataContext.PolicySettings = $global:txtADMXSettings.Text
}
else
{
$script:settingsForm.DataContext.PolicySettings = Get-ADMXSettingsString $script:settingsForm.DataContext
}
$script:settingsForm.DataContext.ManualConfig = (?: ($global:chkADMXManualConfig.IsChecked) 1 0)
$script:settingsForm.DataContext.SettingStatus = $script:curItemSettingStatus
Set-ADMXSettingStatusText $script:settingsForm.DataContext
[System.Windows.Data.CollectionViewSource]::GetDefaultView($global:dgADMXCategoryPolicies.ItemsSource).Refresh()
$global:grdADMXElements.Children.Clear()
}
function Set-ADMXSettingButtonsStatus
{
$global:btnADMXPreviousSetting.IsEnabled = $global:dgADMXCategoryPolicies.SelectedIndex -gt 0
$global:btnADMXNextSetting.IsEnabled = $global:dgADMXCategoryPolicies.SelectedIndex -lt ($global:dgADMXCategoryPolicies.ItemsSource.Count - 1)
}
function Set-ADMXSettingProperties
{
param($settingObj)
$global:grdADMXElements.Children.Clear()
$script:curItemSettingStatus = $settingObj.SettingStatus
if($settingObj.SettingStatus -eq 0)
{
$global:rbADMXSettingDisabled.IsChecked = $true
}
elseif($settingObj.SettingStatus -eq 1)
{
$global:rbADMXSettingEnabled.IsChecked = $true
}
else
{
$global:rbADMXSettingNotConfigured.IsChecked = $true
}
$global:txtADMXSettings.Text = $settingObj.PolicySettings
$global:chkADMXManualConfig.IsChecked = $settingObj.ManualConfig
if(-not $settingObj.PolicyDefinition)
{
$settingObj.PolicyDefinition = Format-XML $settingObj.Definition.OuterXml
}
if(-not $settingObj.supportedOn -and $settingObj.Definition.supportedOn.ref)
{
$settingObj.supportedOn = ($script:supportedOn | Where Id -eq $settingObj.Definition.supportedOn.ref.Split(':')[-1]).DisplayName
}
Set-ADMXElementsPanel $settingObj
$script:settingsForm.DataContext = $settingObj
Set-ADMXControlStatus
}
function Set-ADMXControlStatus
{
$global:grdADMXElements.IsEnabled = ($script:curItemSettingStatus -eq 1 -and $global:chkADMXManualConfig.IsChecked -eq $false)
$global:txtADMXSettings.IsReadOnly = ($script:curItemSettingStatus -ne 1 -or $global:chkADMXManualConfig.IsChecked -eq $false)
}
function Start-AdmxLoadFile
{
param($fileName)
$script:xmlNS = $null
$script:xmlNSPrefix = $null
$script:admx = $null
$script:admxPolicies = @()
$script:supportedOn = @()
$script:admxPoliciesHT = @{}
$script:categoryPaths = @{}
$global:txtADMXProfileName.Text = ""
$global:txtADMXProfileDescription.Text = ""
$global:txtADMXPolicyFileName.Text = ""
$global:btnADMXLoadADML.IsEnabled = $false
$global:txtADMXPolicyIngestName.Text = ""
$global:txtADMXPolicyAppName.Text = $null
$admxFI = [IO.FileInfo]$fileName
if($admxFI.Exists -eq $false) { return }
$admlFile = [IO.Path]::Combine($admxFi.DirectoryName, "en-US\$($admxFi.BaseName).adml")
if([IO.File]::Exists($admlFile) -eq $false)
{
$admlFile = [IO.Path]::Combine($admxFi.DirectoryName, "$($admxFi.BaseName).adml")
}
if([IO.File]::Exists($admlFile) -eq $false)
{
Write-Log "Could not find an ADML file" 2
$admlFile = $null
}
try
{
Write-Log "Load ADMX file $fileName"
[xml]$script:admxXML = Get-Content $fileName
$namespace = $script:admxXML.DocumentElement.NamespaceURI
if($namespace)
{
$script:xmlNS = New-Object System.Xml.XmlNamespaceManager($script:admxXML.NameTable)
$script:xmlNS.AddNamespace("ns", $namespace)
$script:xmlNSPrefix = "ns:"
}
else
{
$script:xmlNS = $null
$script:xmlNSPrefix = ""
}
$prefix = $script:admxXML.policyDefinitions.policyNamespaces.SelectSingleNode("$($script:xmlNSPrefix)target[@prefix]",$script:xmlNS)
if($prefix)
{
if($prefix.namespace)
{
$policyId = $prefix.namespace.Split('.')[-1]
}
else
{
$policyId = $prefix.prefix
}
}
else
{
$policyId = $tmpFI.BaseName -replace " ",""
Write-Log "Failed to get policy id from XML. Using file base name" 2
}
$global:txtADMXPolicyAppName.Text = $policyId
$global:txtADMXPolicyFileName.Text = $policyId
}
catch
{
Write-LogError "Failed to load ADMX file" $_.Exception
}
$global:btnADMXLoadADML.IsEnabled = $true
Invoke-LoadADMXSettings $admlFile
}
function Invoke-LoadADMXSettings
{
param($admlFile)
$script:lngADML = $null
if($admlFile -and [IO.File]::Exists($admlFile))
{
try
{
Write-Log "Load ADML file $admlFile"
[xml]$script:lngADML = Get-Content $admlFile
}
catch
{
Write-LogError "Failed to load ADML file $admlFile" $_.Exception
}
}
$script:stringTable = @{}
foreach($strNode in $script:lngADML.policyDefinitionResources.resources.stringTable.string)
{
$script:stringTable.Add($strNode.id, $strNode.'#text')
}
$script:supportedOn = @()
foreach($polObj in $script:admxXML.policyDefinitions.supportedOn.definitions.definition)
{
$script:supportedOn += [PSCustomObject]@{
Id=$polObj.Name
DisplayName=(Get-ADMXADMLString $polObj)
}
}
if($script:windowsADML)
{
foreach($winString in ($script:windowsADML.policyDefinitionResources.resources.stringTable.string | Where Id -like "SUPPORTED_*"))
{
$script:supportedOn += [PSCustomObject]@{
Id=$winString.id
DisplayName=$winString.'#text'
}
}
}
$devicePolicies = @()
$userPolicies = @()
foreach($polObj in $script:admxXML.policyDefinitions.policies.policy)
{
$displayName = $null
$description = $null
$category = $null
$curSetting = $null
if($polObj.parentCategory.ref)
{
$category = Get-ADMXCategoryNamePath $polObj.parentCategory.ref "/"
}
$displayName = Get-ADMXADMLString $polObj
$description = Get-ADMXADMLString $polObj "explainText"
if($polObj.Class -eq "Both")
{
$classArr = @("Device","User")
}
elseif($polObj.Class -eq "Machine")
{
$classArr = "Device"
}
$settingExists = $false
foreach($class in $classArr)
{
#This will happen when loading a new ADML file
$tmpName = ($polObj.Name + "_" + $class)
if($script:admxPoliciesHT.ContainsKey($tmpName))
{
$curSetting = $script:admxPoliciesHT[$tmpName]
foreach($tmpSetting in $curSetting)
{
$settingExists = $true
$curSetting.Name = $displayName
$curSetting.Description = $description
}
}
}
if($settingExists -eq $false)
{
if($polObj.Class -eq "Both")
{
$classArr = @("Device","User")
}
elseif($polObj.Class -eq "Machine")
{
$classArr = "Device"
}
else
{
$classArr = $polObj.Class
}
foreach($class in $classArr)
{
$newSetting = [PSCustomObject]@{
Name = $displayName
Description = $description
OMAURIName = $null
OMAURIDescription = $null
Category = $category
CategoryId = $polObj.parentCategory.ref
Id = $polObj.Name
Definition = $polObj
SettingStatus = $null
SettingStatusText = $null
PolicySettings = $null
PolicyDefinition = $null #Format-XML $polObj.OuterXml
ElementsPanel = $null
ManualConfig = $false
SettingClass = $class
SupportedOn = $null
}
$script:admxPoliciesHT.Add(($polObj.Name + "_" + $class), $newSetting)
$script:admxPolicies += $newSetting
if($newSetting.SettingClass -eq "User")
{
$userPolicies += $newSetting
}
else
{
$devicePolicies += $newSetting
}
}
}
}
$script:admxPolicies | foreach-object { Set-ADMXSettingStatusText $_ }
$script:admxPolicies = $script:admxPolicies | Sort -Property Name
$global:tvADMXCategories.Items.Clear()
$treeItems = @()
$tvItem = [PSCustomObject]@{
Name = "Computer Configuration"
Children = @()
}
if($script:admxXML.policyDefinitions.policies)
{
$policies = $script:admxXML.policyDefinitions.policies.SelectNodes("$($script:xmlNSPrefix)policy[@class = 'Both' or @class = 'Machine']", $script:xmlNS)
if($policies)
{
$categories = $policies.parentCategory | Select ref -Unique
Add-ADMXCategories $categories $tvItem "Device"
}
}
$treeItems += $tvItem
$tvItem = [PSCustomObject]@{
Name = "User Configuration"
Children = @()
}
if($script:admxXML.policyDefinitions.policies)
{
$policies = $script:admxXML.policyDefinitions.policies.SelectNodes("$($script:xmlNSPrefix)policy[@class = 'Both' or @class = 'User']", $script:xmlNS)
if($policies)
{
$categories = $policies.parentCategory | Select ref -Unique
Add-ADMXCategories $categories $tvItem "User"
}
}
$treeItems += $tvItem
$treeItems | foreach-object { Add-ADMXCategoryTreeNode $_ $global:tvADMXCategories }
if($devicePolicies.Count -gt 0)
{
$tvItem = [System.Windows.Controls.TreeViewItem]::new()
$tvItem.Header = "All Policies"
$tvItem.Tag = $devicePolicies
$tvItem | Add-Member -MemberType NoteProperty -Name "AllPolicies" -Value $true
$global:tvADMXCategories.Items[0].Items.Add($tvItem) | Out-Null
}
if($userPolicies.Count -gt 0)
{
$tvItem = [System.Windows.Controls.TreeViewItem]::new()
$tvItem.Header = "All Policies"
$tvItem.Tag = $userPolicies
$tvItem | Add-Member -MemberType NoteProperty -Name "AllPolicies" -Value $true
}
$global:tvADMXCategories.Items[1].Items.Add($tvItem) | Out-Null
}
function Set-ADMXSettingStatusText
{
param($settingObj)
if($settingObj.SettingStatus -eq 0)
{
$settingObj.SettingStatusText = "Disabled"
}
elseif($settingObj.SettingStatus -eq 1)
{
$settingObj.SettingStatusText = "Enabled"
}
else
{
$settingObj.SettingStatusText = "Not Configured"
}
}
function Add-ADMXCategories
{
param($categories, $parent, $settingClass)
foreach($cat in $categories.ref)
{
$catPath = Get-ADMXCategoryIdPath $cat
$tvObj = $parent
foreach($catName in $catPath.Split('/'))
{
$curParent = $tvObj.Children | Where { $_.CategoryNode.name -eq $catName }
if(-not $curParent)
{
$cat = $script:admxXML.policyDefinitions.categories.selectSingleNode("$($script:xmlNSPrefix)category[@name='$($catName)']",$script:xmlNS)
$curParent += [PSCustomObject]@{
Name = Get-ADMXADMLString $cat
CategoryName = $catName
CategoryNode = $cat
SettingClass = $settingClass
Children = @()
}
$tvObj.Children += $curParent
}
$tvObj = $curParent
}
}
}
Function Add-ADMXCategoryTreeNode
{
Param($obj, $parent)
$tvItem = [System.Windows.Controls.TreeViewItem]::new()
$tvItem.Header = $obj.Name
$tvItem.Tag = $obj
$parent.Items.Add($tvItem) | Out-Null
$obj.Children | Sort -Property Name | foreach-object { Add-ADMXCategoryTreeNode $_ $tvItem }
}
function Get-ADMXADMLString
{
param($xmlNode, $property = "displayName")
$propValue = $xmlNode.$property
if(-not $script:lngADML -or -not $xmlNode.$property) { return $propValue}
$tmpNode = $null
if($xmlNode.$property.StartsWith("`$(") )
{
$tmp = $xmlNode.$property.SubString(2, $xmlNode.$property.Length - 3)
$type,$strId = $tmp.Split('.')
}
else
{
$strId = $propValue
}
if($script:stringTable.ContainsKey($strId))
{
# Way quicker to use a hash table over querying all items
$propValue = $script:stringTable[$strId]
}
else
{
$propValue = $tmpNode."#text"
}
$propValue
}
function Get-ADMXADMLPresentationString
{
param($presentationInfo, $xmlNode)
$tmp = $null
if(-not $script:lngADML) { return $tmp }
$presentationNode = $presentationInfo.selectSingleNode("./*[@refId='$($xmlNode.id)']")
if($presentationNode)
{
$tmp = ?? $presentationNode.Label.'#text' $presentationNode.'#text'
}
else
{
$tmp = $xmlNode.id
}
$tmp
}
function Get-ADMXCategoryIdPath
{
param($categoryId, $delimiter = "/")
$catObj = $script:admxXML.policyDefinitions.categories.selectSingleNode("$($script:xmlNSPrefix)category[@name='$($categoryId)']",$script:xmlNS)
$categories = @()
while($catObj)
{
$categories += $catObj.name
if($catObj.parentCategory.ref)
{
$catObj = $script:admxXML.policyDefinitions.categories.selectSingleNode("$($script:xmlNSPrefix)category[@name='$($catObj.parentCategory.ref)']",$script:xmlNS)
}
else
{
break
}
}
[array]::Reverse($categories)
$categories -join $delimiter
}
function Get-ADMXCategoryNamePath
{
param($categoryId, $delimiter = "/")
if($script:categoryPaths.ContainsKey($categoryId))
{
return $script:categoryPaths[$categoryId]
}
$catObj = $script:admxXML.policyDefinitions.categories.selectSingleNode("$($script:xmlNSPrefix)category[@name='$($categoryId)']",$script:xmlNS)
$categories = @()
while($catObj)
{
$categories += Get-ADMXADMLString $catObj
if($catObj.parentCategory.ref)
{
$catObj = $script:admxXML.policyDefinitions.categories.selectSingleNode("$($script:xmlNSPrefix)category[@name='$($catObj.parentCategory.ref)']", $script:xmlNS)
}
else
{
break
}
}
[array]::Reverse($categories)
$catPath = $categories -join $delimiter
$script:categoryPaths.Add($categoryId, $catPath)
$catPath
}
function Invoke-ADMXFilterPolicies
{
param($txtBox)
$filter = $null
if($txtBox.Text.Trim() -eq "" -and $txtBox.IsFocused -eq $false)
{
$txtBox.FontStyle = "Italic"
$txtBox.Tag = 1
$txtBox.Text = "Filter"
$txtBox.Foreground="Lightgray"
}
elseif($txtBox.Tag -eq "1" -and $txtBox.Text -eq "Filter" -and $txtBox.IsFocused -eq $false)
{
}
else
{
$txtBox.FontStyle = "Normal"
$txtBox.Tag = $null
$txtBox.Foreground="Black"
$txtBox.Background="White"
if($txtBox.Text)
{
$filter = {
param ($item)
return ($item.Name -match [regex]::Escape($txtBox.Text))
}
}
}
if($global:dgADMXSettings.ItemsSource -is [System.Windows.Data.ListCollectionView] -and $txtBox.IsFocused -eq $true)
{
# This causes odd behaviour with focus e.g. and item has to be clicked twice to be selected
$global:dgADMXSettings.ItemsSource.Filter = $filter
#$global:dgADMXSettings.ItemsSource.Refresh()
}
}
function Get-ADMXPresentationNode
{
param($item)
$presentation = $null
if($item.Definition.presentation -and $script:lngADML)
{
if($item.Definition.presentation.StartsWith("`$(") )
{
$tmp = $item.Definition.presentation.SubString(2, $item.Definition.presentation.Length - 3)
$type,$strId = $tmp.Split('.')
$presentation = $script:lngADML.policyDefinitionResources.resources.presentationTable.presentation | Where Id -eq $strId #selectSingleNode("presentation[@id='$($strId)']")
}
else
{
$presentation = $script:lngADML.policyDefinitionResources.resources.presentationTable | Where Id -eq $item.Definition.presentation #.selectSingleNode("presentation[@id='$($item.Definition.presentation)']")
}
}
$presentation
}
function Set-ADMXElementsPanel
{
param($item)
if(-not $item.Definition.elements) { return }
if(-not $item.ElementsPanel)
{
$grd = [System.Windows.Controls.Grid]::new()
$presentation = Get-ADMXPresentationNode $item
$i = 0
if($presentation)
{
# Policy node schema
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-gpreg/81a89003-5121-4216-b788-fde8daa71c78
foreach($presentationNode in $presentation.ChildNodes)
{
$ctrl = $null
$elementNode = $item.Definition.elements.ChildNodes | Where id -eq $presentationNode.refId #selectSingleNode("/*[@id='$($presentationNode.refId)']")
if($presentationNode.Label.'#text')
{ $stringLabel = $presentationNode.Label.'#text' }
elseif($presentationNode.Label)
{ $stringLabel = $presentationNode.Label }
else
{ $stringLabel = $presentationNode.'#text' }
if($stringLabel -and $presentationNode.LocalName -ne "CheckBox") #$presentationNode.LocalName -eq "text")
{
$ctrl = [System.Windows.Controls.TextBlock]::new()
$ctrl.Text = $stringLabel #$presentationNode."#text"
Add-GridObject $grd $ctrl
}
if($presentationNode.LocalName -eq "text")
{
continue
}
if($ctrl)
{
$ctrl.Margin = "0,5,0,0"
}
if($presentationNode.LocalName -eq "textbox")
{
$ctrl = [System.Windows.Controls.TextBox]::new()
if($presentationNode.defaultValue -ne $null)
{
$ctrl.Text = $presentationNode.defaultValue
}
}
elseif($presentationNode.LocalName -eq "DecimalTextBox" -or
$presentationNode.LocalName -eq "LongDecimalTextBox" )
{
$ctrl = Get-NumericUpDownControl $presentationNode.refId (?? $elementNode.minValue 0) (?? $elementNode.maxValue 9999) (?? $elementNode.SpinStep 1)
if(-not $ctrl) { continue }
if($presentationNode.defaultValue -ne $null)
{
$ctrl.Children[0].Text = $presentationNode.defaultValue
}
}
elseif($presentationNode.LocalName -eq "multiText")
{
$ctrl = [System.Windows.Controls.TextBox]::new()
$ctrl.Height = 100
$ctrl.AcceptsReturn = $true
}
elseif($presentationNode.LocalName -eq "CheckBox")
{
$ctrl = [System.Windows.Controls.CheckBox]::new()
$ctrl.Content = $stringLabel
if($presentationNode.defaultChecked -eq $true)
{
$ctrl.IsChecked = $true
}
}
elseif($presentationNode.LocalName -eq "ComboBox" -or
$presentationNode.LocalName -eq "DropdownList" )
{
$ctrl = [System.Windows.Controls.ComboBox]::new()
$ctrl.DisplayMemberPath = "Name"
$ctrl.SelectedValuePath = "Value"
$valItems = @()
foreach($valItem in $elementNode.ChildNodes)
{
$displayName = Get-ADMXADMLString $valItem
if($valItem.value.decimal.value)
{
$value = $valItem.value.decimal.value
}
elseif($valItem.value.longDecimal)
{
$value = $valItem.value.longDecimal.'#text'
}
elseif($valItem.value.string)
{
$value = $valItem.value.string.'#text'
}
else
{
Write-Log "Unsupported value type for $($elementNode.Id): $($valItem.value.InnerXml)" 2
$value = ""
}
$valItems += [PSCustomObject]@{
Name = $displayName
Value = $value
}
}
if($presentationNode.defaultItem -ne $null)
{
try
{
$ctrl.SelectedIndex = $presentationNode.defaultItem
}
catch {}
}
if($presentationNode.NoSort -ne "true")
{
$valItems = $valItems | Sort -Property Name
}
$ctrl.ItemsSource = $valItems
}
elseif($presentationNode.LocalName -eq "listBox")
{
$ctrl = [System.Windows.Controls.DataGrid]::new()
$ctrl.CanUserAddRows = $true
$ctrl.CanUserDeleteRows = $true
$ctrl.CanUserSortColumns = $false
$ctrl.CanUserResizeRows = $false
$ctrl.AutoGenerateColumns = $false
$ctrl.ColumnWidth = [System.Windows.Controls.DataGridLength]"*"
$column = [System.Windows.Controls.DataGridTextColumn]::new()
$column.Header = "Value Name"
$column.Width = [System.Windows.Controls.DataGridLength]"1*"
$binding = [System.Windows.Data.Binding]::new("Key")
$column.Binding = $binding
if($elementNode.explicitValue -ne "true")
{
$column.Visibility = "Collapsed"
}
$ctrl.Columns.Add($column)
$column = [System.Windows.Controls.DataGridTextColumn]::new()
$column.Header = "Value"
$column.Width = [System.Windows.Controls.DataGridLength]"1*"
$binding = [System.Windows.Data.Binding]::new("Value")
$column.Binding = $binding
$ctrl.Columns.Add($column)
$ctrl.ItemsSource = [System.Collections.Generic.List[ListValue]]::new()
}
else
{
Write-Log "Unsupported object type in presentation: $($presentationNode.LocalName). Control: $presentationNode.refId" 2
continue
}
Add-GridObject $grd $ctrl
if($presentationNode.refId)
{
$ctrl.Tag = $elementNode
$ctrl.Name = $presentationNode.refId
}
}
}
elseif($item.Definition.elements.ChildNodes)
{
# This should NOT be used. Presentation settings should be defined in the ADML file
Write-Log "No presentation settings found for $($item.Definition.Name)" 2
foreach($elementNode in $item.Definition.elements.ChildNodes)
{
try
{
$ctrl = $null
if($elementNode.LocalName -eq "text")
{
$ctrl = [System.Windows.Controls.TextBox]::new()
}
elseif($elementNode.LocalName -eq "multiText")
{
$ctrl = [System.Windows.Controls.TextBox]::new()
$ctrl.Height = 100
$ctrl.AcceptsReturn = $true
}
elseif($elementNode.LocalName -eq "enum")
{
$ctrl = [System.Windows.Controls.ComboBox]::new()
$ctrl.DisplayMemberPath = "Name"
$ctrl.SelectedValuePath = "Value"
$valItems = @()
foreach($valItem in $elementNode.ChildNodes)
{
$displayName = Get-ADMXADMLString $valItem
if($valItem.value.decimal.value)
{
$value = $valItem.value.decimal.value
}
elseif($valItem.value.string)
{
$value = $valItem.value.string.'#text'
}
else
{
Write-Log "Unsupported value type for $($elementNode.Id): $($valItem.value.InnerXml)" 2
$value = ""
}
$valItems += [PSCustomObject]@{
Name = $displayName
Value = $value
}
}
$ctrl.ItemsSource = $valItems
}
elseif($elementNode.LocalName -eq "list")
{
$ctrl = [System.Windows.Controls.DataGrid]::new()
$ctrl.CanUserAddRows = $true
$ctrl.CanUserDeleteRows = $true
$ctrl.CanUserSortColumns = $false
$ctrl.CanUserResizeRows = $false
$ctrl.ColumnWidth = [System.Windows.Controls.DataGridLength]::Auto
$ctrl.AutoGenerateColumns = $false
$ctrl.ColumnWidth = [System.Windows.Controls.DataGridLength]"*"
$column = [System.Windows.Controls.DataGridTextColumn]::new()
$column.Header = "Value Name"
$column.Width = [System.Windows.Controls.DataGridLength]"1*"
$binding = [System.Windows.Data.Binding]::new("Key")
$column.Binding = $binding
if($elementNode.explicitValue -ne "true")
{
$column.Visibility = "Collapsed"
}
$ctrl.Columns.Add($column)
$column = [System.Windows.Controls.DataGridTextColumn]::new()
$column.Header = "Value"
$column.Width = [System.Windows.Controls.DataGridLength]"1*"
$binding = [System.Windows.Data.Binding]::new("Value")
$column.Binding = $binding
$ctrl.Columns.Add($column)
$ctrl.ItemsSource = [System.Collections.Generic.List[ListValue]]::new()
}
elseif($elementNode.LocalName -eq "decimal")
{
$ctrl = [System.Windows.Controls.TextBox]::new()
}
elseif($elementNode.LocalName -eq "boolean")
{
$ctrl = [System.Windows.Controls.ComboBox]::new()
}
else
{
Write-Log "Element type not supported: $($elementNode.LocalName)" 2
continue
}
$displayName = Get-ADMXADMLPresentationString $presentation $elementNode
if($displayName)
{
$rd = [System.Windows.Controls.RowDefinition]::new()
$rd.Height = [double]::NaN #[System.Windows.GridLength]::Auto
$grd.RowDefinitions.Add($rd)
$tb = [System.Windows.Controls.TextBlock]::new()
if($i -gt 0) { $tb.Margin = "0,5,0,0" }
$tb.Text = $displayName
$tb.SetValue([System.Windows.Controls.Grid]::RowProperty,$i)
$grd.Children.Add($tb)
$i++
}
$rd = [System.Windows.Controls.RowDefinition]::new()
$rd.Height = [double]::NaN
$grd.RowDefinitions.Add($rd)
$ctrl.SetValue([System.Windows.Controls.Grid]::RowProperty,$i)
$ctrl.Tag = $elementNode
$ctrl.Name = $elementNode.Id
$grd.Children.Add($ctrl)
#$grd.RegisterName($ctrl.Name, $ctrl)
$i++
}
catch
{
Write-LogError "Failed to add ADMX element $($elementNode.LocalName) with id $($elementNode.id)" $_.Exception
}
}
}
$rd = [System.Windows.Controls.RowDefinition]::new()
#$rd.Height = [System.Windows.GridLength]::new(1, "Star")
$grd.RowDefinitions.Add($rd)
$item.ElementsPanel = $grd
}
if($item.ElementsPanel)
{
$global:grdADMXElements.Children.Add($item.ElementsPanel)
foreach($elementNode in $item.Definition.elements.ChildNodes)
{
$ctrl = [System.Windows.LogicalTreeHelper]::FindLogicalNode($item.ElementsPanel, $elementNode.Id)
if(-not $ctrl)
{
Write-Log "Could not find a control with id $($elementNode.Id)" 3
continue
}
#$global:grdADMXElements.RegisterName($ctrl.Name, $ctrl)
}
}
}
function Get-ADMXSettingsString
{
param($item)
if(-not $item -or -not $item.Definition.elements) { return }
if($script:curItemSettingStatus -ne 1)
{
$item.PolicySettings = $null
return
}
$policySettings = @()
foreach($elementNode in $item.Definition.elements.ChildNodes)
{
#$ctrl = $item.ElementsPanel.FindName($elementNode.Id)
$ctrl = [System.Windows.LogicalTreeHelper]::FindLogicalNode($item.ElementsPanel, $elementNode.Id)
if(-not $ctrl)
{
Write-Log "Could not find a control with id $($elementNode.Id)" 3
continue
}
$ctrlValue = $null
if($elementNode.LocalName -eq "text")
{
$ctrlValue = $ctrl.Text
}
elseif($elementNode.LocalName -eq "multiText")
{
$ctrlValue = $ctrl.Text -replace [Environment]::NewLine,""
}
elseif($elementNode.LocalName -eq "enum")
{
$ctrlValue = $ctrl.SelectedValue
}
elseif($elementNode.LocalName -eq "list")
{
$i = 1
$keyValueArr = @()
foreach($keyValue in $ctrl.ItemsSource)
{
if(-not $keyValue.Value) { continue }
$keyValueArr += "$((?? $keyValue.Key $i))$($keyValue.Value)"
$i++
}
$ctrlValue = $keyValueArr -join ""
}
elseif($elementNode.LocalName -eq "decimal" -or
$ctrl.Tag.LocalName -eq "longDecimal")
{
$ctrlValue = $ctrl.Children[0].Text
}
elseif($elementNode.LocalName -eq "boolean")
{
if($ctrl -is [System.Windows.Controls.CheckBox])
{
$ctrlValue = ?: $ctrl.IsChecked "1" "0"
if($ctrl.IsChecked -eq $false)
{
#continue # GPO setting skips unchecked checkbox.
}
}
elseif($ctrl -is [System.Windows.Controls.ComboBox])
{
$ctrlValue = $ctrl.SelectedValue
}
else
{
Write-Log "Boolean element type not supported: $($elementNode.LocalName)" 2
continue
}
}
else
{
Write-Log "Element type not supported: $($elementNode.LocalName)" 2
continue
}
if(-not $ctrlValue)
{
if($elementNode.required -eq $true)
{
Write-Log "Required value is missing for $($elementNode.Id)" 3
}
else
{
Write-Log "Value not set for $($elementNode.Id). Value will not be added"
}
continue
}
$policySettings += ""
}
$policySettings -join [Environment]::NewLine # Or ""?
}
function Get-ADMXCategoryOMAURIPath
{
param($categoryId)
$catObj = $script:admxXML.policyDefinitions.categories.selectSingleNode("$($script:xmlNSPrefix)category[@name='$($categoryId)']",$script:xmlNS)
$categories = @()
while($catObj)
{
$categories += $catObj.name
$catObj = $script:admxXML.policyDefinitions.categories.selectSingleNode("$($script:xmlNSPrefix)category[@name='$($catObj.parentCategory.ref)']",$script:xmlNS)
}
[array]::Reverse($categories)
$categories -join "~"
}
function Import-ADMXPolicy
{
if(-not $global:txtADMXProfileName.Text.Trim())
{
[System.Windows.MessageBox]::Show("Profile Name name must be specified", "Error", "OK", "Error")
return
}
if(-not $global:txtADMXPolicyFileName.Text.Trim())
{
[System.Windows.MessageBox]::Show("ADMX Policy Name name must be specified", "Error", "OK", "Error")
return
}
$admxConfiguredSettings = @()
foreach($admxPolicy in $($script:admxPolicies | Where { $_.SettingStatus -eq 0 -or $_.SettingStatus -eq 1 }))
{
if($admxPolicy.SettingStatus -eq 0)
{
$policyValue = ""
}
else
{
$policyValue = ""
$strValue = $admxPolicy.PolicySettings
if($strValue)
{
$policyValue += "`n`n$strValue"
}
}
$catPath = Get-ADMXCategoryOMAURIPath $admxPolicy.Definition.parentCategory.ref
$omaUriPath = "./$($admxPolicy.SettingClass)/Vendor/MSFT/Policy/Config/$($global:txtADMXPolicyAppName.Text)~Policy~$catPath/$($admxPolicy.Definition.name)"
if($admxPolicy.OMAURIDescription)
{
$desc = $admxPolicy.OMAURIDescription
}
elseif($admxPolicy.Description -and $admxPolicy.Description.Length -gt 1000)
{
$desc = $admxPolicy.Description.SubString(0,1000)
}
else
{
$desc = $admxPolicy.Description
}
$admxConfiguredSettings += [PSCustomObject]@{
"@odata.type" = "#microsoft.graph.omaSettingString"
"displayName" = (?? $admxPolicy.OMAURIName $admxPolicy.Name)
"description" = $desc
"omaUri" = $omaUriPath
"value" = $policyValue
}
}
$intuneObj = [PSCustomObject]@{
"@odata.type" = "#microsoft.graph.windows10CustomConfiguration"
"displayName" = $global:txtADMXProfileName.Text
"omaSettings" = @()
"roleScopeTagIds" = @()
"assignments" = @()
}
if($global:txtADMXProfileDescription.Text)
{
$intuneObj | Add-Member -MemberType NoteProperty -Name "description" -Value $global:txtADMXProfileDescription.Text
}
if($global:chkADMXPolicyIngest.IsChecked)
{
$intuneObj.omaSettings += [PSCustomObject]@{
"@odata.type" = "#microsoft.graph.omaSettingString"
"displayName" = (?? $global:txtADMXPolicyIngestName.Text ("$($script:currentADMXFile.Name) Ingestion"))
"description" = $null
"omaUri" = "./Device/Vendor/MSFT/Policy/ConfigOperations/ADMXInstall/$($global:txtADMXPolicyAppName.Text)/Policy/$($global:txtADMXPolicyFileName.Text)"
"value" = (Format-XML $script:admxXML)
}
}
$intuneObj.omaSettings += $admxConfiguredSettings
$json = $intuneObj | ConvertTo-Json -Depth 20
$obj = Invoke-GraphRequest -Url "/deviceManagement/deviceConfigurations" -Body $json -Method "POST"
if($obj)
{
Write-Log "Device configuration profile '$($intuneObj.displayName)' created with id $($obj.Id)"
}
else
{
$text = "Failed to create device configuration profile '$($intuneObj.displayName)'"
Write-Log $text 3
[System.Windows.MessageBox]::Show(($text + [Environment]::NewLine + [Environment]::NewLine + 'Check log for errors'), "Error!", "OK", "Error")
}
}
#endregion
#region Reg Values
function Show-ADMXRegValues
{
if(-not $script:frmADMXRegProfile)
{
$script:frmADMXRegProfile = Get-XamlObject ($global:AppRootFolder + "\Xaml\EndpointManagerToolsADMXRegValues.xaml") -AddVariables
if(-not $script:frmADMXRegProfile) { return }
$global:cbADMXRegPolicyType.ItemsSource = @(
[PSCustomObject]@{
Name = "Policy"
Value = "Policy"
},
[PSCustomObject]@{
Name = "Preference"
Value = "Preference"
}
)
$script:ADMXRegProfile = [ADMXRegProfile]::new()
$script:frmADMXRegProfile.DataContext = $script:ADMXRegProfile
$global:btnADMXRegClear.Add_Click({
$script:addedRegValues.Clear()
$global:txtADMXRegProfileName.Text = ""
$global:txtADMXRegProfileDescription.Text = ""
$global:cbADMXRegPolicyType.SelectedValue = $null
})
$global:dgADMXRegAddedPolicies.Add_MouseDoubleClick({
if(-not $this.SelectedItem) { return }
Show-ADMXRegSettings $this.SelectedItem
})
$global:mnuADMXRegPoliciesContextMenu.Add_Opened({
$global:mnuADMXRegPolicyEdit.IsEnabled = $null -ne $global:dgADMXRegAddedPolicies.SelectedItem
})
$global:mnuADMXRegPolicyEdit.Add_Click({
if(-not $global:dgADMXRegAddedPolicies.SelectedItem) { return }
Show-ADMXRegSettings $global:dgADMXRegAddedPolicies.SelectedItem
})
$global:btnADMXAddRegValue.Add_Click({
Show-ADMXRegSettings
})
$global:btnADMXRegImport.Add_Click({
Write-Status "Import Reg Settings Policy"
Import-ADMXRegProfile
Write-Status ""
})
}
$global:grdToolsMain.Children.Clear()
$global:grdToolsMain.Children.Add($frmADMXRegProfile)
}
function Show-ADMXRegSettings
{
param($regProfile)
$script:frmADMXRegPolicies = Get-XamlObject ($global:AppRootFolder + "\Xaml\EndpointManagerToolsADMXAddRegPolicy.xaml") -AddVariables
if(-not $script:frmADMXRegPolicies) { return }
$newValue = [ADMXRegPolicyElement]::new()
if(-not $regProfile)
{
$regProfile = [ADMXRegPolicy]::new()
$script:newRegPolicy = $true
}
else
{
$script:newRegPolicy = $false
}
$script:frmADMXRegPolicies.DataContext = [PSCustomObject]@{
RegPolicy = $regProfile
PolicyElement = $newValue
}
$regHives = @()
# Should this be listed for preferences?
$regHives += [PSCustomObject]@{
Name = "HKEY_LOCAL_MACHINE"
Value = "HKLM"
}
$regHives += [PSCustomObject]@{
Name = "HKEY_CURRENT_USER"
Value = "HKCU"
}
$global:cbADMXRegHive.ItemsSource = $regHives
$global:cbADMXRegPolicyStatus.ItemsSource = @(
[PSCustomObject]@{
Name = "Enabled"
Value = "Enabled"
},
[PSCustomObject]@{
Name = "Disabled"
Value = "Disabled"
}
)
$global:dgADMXRegAddedElements.Add_MouseDoubleClick({
if(-not $this.SelectedItem) { return }
$global:btnADMXRegElementAdd.Visibility = "Collapsed"
$global:btnADMXRegElementNew.Visibility = "Visible"
$selectedItem = $this.SelectedItem
$tmp = $script:frmADMXRegPolicies.DataContext
$script:frmADMXRegPolicies.DataContext = $null
$tmp.PolicyElement = $selectedItem
$script:frmADMXRegPolicies.DataContext = $tmp
Set-ADMXRegAttributeControls
[System.Windows.Forms.Application]::DoEvents()
})
$global:cbADMXRegElementDataType.ItemsSource = @(
[PSCustomObject]@{
Name = "String"
Value = "text"
},
[PSCustomObject]@{
Name = "Multi-string"
Value = "multiText"
},
[PSCustomObject]@{
Name = "List"
Value = "list"
},
[PSCustomObject]@{
Name = "DWORD (32-bit)"
Value = "decimal"
}
# Looks like longDecimal is not supported
#,
#[PSCustomObject]@{
# Name = "QWORD (64-bit)"
# Value = "longDecimal"
#}
)
Add-XamlEvent $script:frmADMXRegPolicies "cbADMXRegElementDataType" "Add_SelectionChanged"({
Set-ADMXRegAttributeControls
})
Add-XamlEvent $script:frmADMXRegPolicies "btnADMXRegElementAdd" "add_click" ({
if($global:txtADMXRegElementKey.Text -and (Get-ADMXRegIsKeySupported $global:txtADMXRegElementKey.Text) -eq $false)
{
return
}
if(-not $script:frmADMXRegPolicies.DataContext.RegPolicy.Key)
{
[System.Windows.MessageBox]::Show("The Key value must be specified for the policy", "Error!","OK", "Error")
return
}
$newValue = [ADMXRegPolicyElement]::new()
$tmp = $script:frmADMXRegPolicies.DataContext
$script:frmADMXRegPolicies.DataContext = $null
$tmp.RegPolicy.PolicyElements.Add($tmp.PolicyElement)
$tmp.PolicyElement = $newValue
$script:frmADMXRegPolicies.DataContext = $tmp
[System.Windows.Forms.Application]::DoEvents()
})
Add-XamlEvent $script:frmADMXRegPolicies "btnADMXRegElementNew" "add_click" ({
$newValue = [ADMXRegPolicyElement]::new()
$tmp = $script:frmADMXRegPolicies.DataContext
$script:frmADMXRegPolicies.DataContext = $null
#$tmp.RegPolicy.PolicyElements.Add($tmp.PolicyElement)
$tmp.PolicyElement = $newValue
$script:frmADMXRegPolicies.DataContext = $tmp
[System.Windows.Forms.Application]::DoEvents()
$global:btnADMXRegElementAdd.Visibility = "Visible"
$this.Visibility = "Collapsed"
})
Add-XamlEvent $script:frmADMXRegPolicies "btnADMXRegAddNew" "add_click" ({
if((Get-ADMXRegIsKeySupported $global:txtADMXRegKey.Text) -eq $false)
{
return
}
if($script:newRegPolicy)
{
$script:frmADMXRegProfile.DataContext.ADMXPolicies.Add($script:frmADMXRegPolicies.DataContext.RegPolicy)
}
Show-ModalObject
})
Add-XamlEvent $script:frmADMXRegPolicies "btnADMXRegCancel" "add_click" ({
$script:frmADMXRegPolicies = $null
Show-ModalObject
})
Show-ModalForm "Add new reg policy" $script:frmADMXRegPolicies -HideButtons
}
function Set-ADMXRegAttributeControls
{
if($global:cbADMXRegElementDataType.SelectedValue -eq "list" -or $global:cbADMXRegElementDataType.SelectedValue -eq "multiText")
{
$global:spADMXRegAttributeValueSeparator.Visibility = "Visible"
$global:txtADMXRegAttributeValueSeparator.Visibility = "Visible"
}
else
{
$global:spADMXRegAttributeValueSeparator.Visibility = "Collapsed"
$global:txtADMXRegAttributeValueSeparator.Visibility = "Collapsed"
}
if($global:cbADMXRegElementDataType.SelectedValue -eq "list")
{
$global:spADMXRegAttributeValuePrefix.Visibility = "Visible"
$global:txtADMXRegAttributeValuePrefix.Visibility = "Visible"
}
else
{
$global:spADMXRegAttributeValuePrefix.Visibility = "Collapsed"
$global:txtADMXRegAttributeValuePrefix.Visibility = "Collapsed"
}
$global:txtADMXRegElementValueName.IsReadOnly = ($global:cbADMXRegElementDataType.SelectedValue -eq "list")
if($global:cbADMXRegElementDataType.SelectedValue -eq "text" -or
$global:cbADMXRegElementDataType.SelectedValue -eq "list")
{
$global:spADMXRegAttributeExpandable.Visibility = "Visible"
$global:chkADMXRegAttributeExpandable.Visibility = "Visible"
}
else
{
$global:spADMXRegAttributeExpandable.Visibility = "Collapsed"
$global:chkADMXRegAttributeExpandable.Visibility = "Collapsed"
}
if($global:cbADMXRegElementDataType.SelectedValue -eq "list")
{
$global:spADMXRegAttributeAdditive.Visibility = "Visible"
$global:chkADMXRegAttributeAdditive.Visibility = "Visible"
}
else
{
$global:spADMXRegAttributeAdditive.Visibility = "Collapsed"
$global:chkADMXRegAttributeAdditive.Visibility = "Collapsed"
}
}
function Get-ADMXRegIsKeySupported
{
param($regKey)
$tmpPath = $regKey.Trim('\')
$tmpPath = $tmpPath + "\"
$blockedSource = ""
foreach($blockedPath in $script:unsupportedLocations)
{
if($tmpPath -like "$($blockedPath)\*")
{
$blockedSource = $blockedPath
break
}
}
if($blockedSource)
{
foreach($excemptionPath in $script:unsupportedOverride)
{
if($tmpPath -like "$($excemptionPath)\*")
{
$blockedSource = ""
break
}
}
}
if($blockedSource)
{
[System.Windows.MessageBox]::Show("The registry key '$($global:txtADMXRegKey.Text)' is not supported" + [Environment]::NewLine + [Environment]::NewLine + "Blocked by root key: $blockedSource", "Unsupported reg ket", "OK", "Error")
return $false
}
return $true
}
function Import-ADMXRegProfile
{
$xml = [xml]$script:admxTemplate
$guidId = [Guid]::NewGuid()
$rgPolicyFileName = ("RegPolicy_" + $guidId)
$xml.policyDefinitions.categories.category.name = ($xml.policyDefinitions.categories.category.name + "_" + $guidId)
$intuneObj = [PSCustomObject]@{
"@odata.type" = "#microsoft.graph.windows10CustomConfiguration"
"displayName" = $script:frmADMXRegProfile.DataContext.ProfileName
"omaSettings" = @()
"roleScopeTagIds" = @()
"assignments" = @()
}
$admxRegSettings = @()
foreach($regPolicy in $script:frmADMXRegProfile.DataContext.ADMXPolicies)
{
if($regPolicy.PolicyStatus -eq "Enabled")
{
$OMAURIString = ""
}
else
{
$OMAURIString = ""
}
if($regPolicy.PolicyName)
{
# No space in name
$policyName = $regPolicy.PolicyName -replace " ","_"
}
else
{
$policyName = [Guid]::NewGuid().Guid
}
#build XML
$newNode = $xml.policyDefinitions.policies.ChildNodes[0].CloneNode($true)
$newNode.name = $policyName
$newNode.class = (?: ($regPolicy.Hive -eq "HKLM") "Machine" "User")
$newNode.displayName = "`$(string.$($policyName))"
$newNode.presentation = "`$(presentation.$($policyName))"
$newNode.key = $regPolicy.Key.Trim('\')
$newNode.parentCategory.ref = $xml.policyDefinitions.categories.category.name
if(-not $regPolicy.StatusValueName)
{
$newNode.RemoveChild($newNode.SelectSingleNode("enabledValue"))
$newNode.RemoveChild($newNode.SelectSingleNode("disabledValue"))
}
else
{
$newNode.valueName = $regPolicy.StatusValueName
}
if($regPolicy.PolicyElements -eq $null -or $regPolicy.PolicyElements.Count -eq 0)
{
$newNode.RemoveChild($newNode.SelectSingleNode("elements"))
}
else
{
$omaUriItems = @()
foreach($element in $regPolicy.PolicyElements)
{
$child = $xml.CreateElement($element.DataType)
if($element.DataType -eq "multitext" -or
$element.DataType -eq "list")
{
$splitter = ?? $global:txtADMXRegAttributeValueSeparator.Text ";"
$value = $element.Value -replace $splitter,""
}
else
{
$value = $element.Value
}
if($element.DataType -eq "list")
{
# ToDo:
# Add support for additive in UI
Add-XMLAttribute $child "additive" "true"
Add-XMLAttribute $child "valuePrefix" $element.AttributePrefix
}
else
{
Add-XMLAttribute $child "valueName" $element.ValueName
}
if(($element.DataType -eq "text" -or $element.DataType -eq "list") -and $element.AttributeExpandable)
{
Add-XMLAttribute $child "expandable" "true"
}
if($element.DataType -eq "list" -and $element.AttributeAdditive)
{
Add-XMLAttribute $child "additive" "true"
}
if($element.AttributeSoft)
{
Add-XMLAttribute $child "soft" "true"
}
if($element.DataType -eq "list")
{
$keyStr = ?? $element.Key $regPolicy.Key
if($keyStr)
{
$idStr = $keyStr.Trim("\").Split('\')[-1]
}
else
{
$idStr = [Guid]::NewGuid().Guid
}
Add-XMLAttribute $child "id" ($idStr + "_Id")
}
else
{
Add-XMLAttribute $child "id" ($element.ValueName + "_Id")
}
if($element.Key)
{
Add-XMLAttribute $child "key" $element.Key.Trim('\')
}
$omaUriItems += ""
$newNode.SelectSingleNode("elements").AppendChild($child) | Out-Null
}
}
if($omaUriItems.Count -gt 0)
{
$OMAURIString = ($OMAURIString + [Environment]::NewLine + [Environment]::NewLine + ($omaUriItems -join [Environment]::NewLine))
}
$xml.policyDefinitions.SelectSingleNode("policies").AppendChild($newNode) | Out-Null
$admxRegSettings += [PSCustomObject]@{
"@odata.type" = "#microsoft.graph.omaSettingString"
"displayName" = "Set $($policyName)"
"omaUri" = "./$((?: ($regPolicy.Hive -eq "HKLM") "Device" "User"))/Vendor/MSFT/Policy/Config/IntuneManagementReg~$(($script:frmADMXRegProfile.DataContext.PolicyType))~$($newNode.parentCategory.ref)/$($policyName)"
"value" = $OMAURIString
}
}
$xml.policyDefinitions.SelectSingleNode("policies").RemoveChild($xml.policyDefinitions.policies.SelectSingleNode("policy")) | Out-Null
$intuneObj.omaSettings += [PSCustomObject]@{
"@odata.type" = "#microsoft.graph.omaSettingString"
"displayName" = "Reg ADMX Ingestion"
"description" = "This XML is generated by Intune Managemet tool"
"omaUri" = "./Device/Vendor/MSFT/Policy/ConfigOperations/ADMXInstall/IntuneManagementReg/$(($script:frmADMXRegProfile.DataContext.PolicyType))/$($rgPolicyFileName)"
"value" = (Format-XML $xml)
}
$intuneObj.omaSettings += $admxRegSettings
$json = $intuneObj | ConvertTo-Json -Depth 20
$obj = Invoke-GraphRequest -Url "/deviceManagement/deviceConfigurations" -Body $json -Method "POST"
if($obj)
{
Write-Log "Custom profile '$($intuneObj.displayName)' created with id $($obj.Id)"
}
else
{
$text = "Failed to create device configuration profile '$($intuneObj.displayName)'"
Write-Log $text 3
[System.Windows.MessageBox]::Show(($text + [Environment]::NewLine + [Environment]::NewLine + 'Check log for errors'), "Error!", "OK", "Error")
}
}
function Add-XMLAttribute
{
param($xmlNode, $attribute, $Value)
$xmlAttrib = $xmlNode.OwnerDocument.CreateAttribute($attribute)
$xmlAttrib.Value = $Value
$xmlNode.Attributes.Append($xmlAttrib)
}
function Add-ADMXRegClasses
{
if (("ADMXRegPolicyElement" -as [type]))
{
return
}
$classDef = @"
using System;
using System.ComponentModel;
using System.Collections.ObjectModel;
public class ADMXRegPolicyElement : System.ComponentModel.INotifyPropertyChanged
{
public string DataType { get { return _dataType; } set { _dataType = value; NotifyPropertyChanged("DataType"); NotifyPropertyChanged("DataTypeDisplayString"); } }
private string _dataType = null;
public string DataTypeDisplayString { get {
if(DataType == "text")
return "String";
else if(DataType == "multiText")
return "Multi-string";
else if(DataType == "list")
return "List";
else if(DataType == "decimal")
return "DWORD (32-bit)";
else if(DataType == "longDecimal")
return "QWORD (64-bit)";
else
return DataType;
} }
public string Key { get { return _key; } set { _key = value; NotifyPropertyChanged("Key"); } }
private string _key;
public string ValueName { get { return _valueName; } set { _valueName = value; NotifyPropertyChanged("ValueName"); } }
private string _valueName;
public string Value { get { return _value; } set { _value = value; NotifyPropertyChanged("Value"); } }
private string _value;
public string AttributePrefix { get { return _attributePrefix; } set { _attributePrefix = value; NotifyPropertyChanged("AttributePrefix"); } }
private string _attributePrefix;
public bool AttributeSoft { get { return _attributeSoft; } set { _attributeSoft = value; NotifyPropertyChanged("AttributeSoft"); } }
private bool _attributeSoft = false;
public bool AttributeExpandable { get { return _attributeExpandable; } set { _attributeExpandable = value; NotifyPropertyChanged("AttributeExpandable"); } }
private bool _attributeExpandable = false;
public bool AttributeAdditive { get { return _attributeAdditive; } set { _attributeAdditive = value; NotifyPropertyChanged("AttributeAdditive"); } }
private bool _attributeAdditive = false;
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged(string propertyName = "")
{
if(PropertyChanged != null) { PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
}
}
public class ADMXRegPolicy
{
public string PolicyName {get; set;}
public string PolicyStatus {get; set;}
public string Hive {get; set;}
public string Key {get; set;}
public bool StatusValueEnabled { get; set;}
public string StatusValueName { get; set;}
public System.Collections.ObjectModel.ObservableCollection PolicyElements {get; set;}
public ADMXRegPolicy()
{
PolicyElements = new System.Collections.ObjectModel.ObservableCollection();
Hive = "HKLM";
PolicyStatus = "Enabled";
StatusValueEnabled = true;
}
}
public class ADMXRegProfile
{
public string ProfileName {get; set;}
public string ProfileDescription {get; set;}
public string PolicyType {get; set;}
public System.Collections.ObjectModel.ObservableCollection ADMXPolicies {get; set;}
public string XmlString {get; set;}
public ADMXRegProfile()
{
ADMXPolicies = new System.Collections.ObjectModel.ObservableCollection();
PolicyType = "Policy";
}
}
"@
[Reflection.Assembly]::LoadWithPartialName("mscorlib") | Out-Null
[Reflection.Assembly]::LoadWithPartialName("System.ComponentModel") | Out-Null
Add-Type -TypeDefinition $classDef
}
#endregion