This commit is contained in:
Mikael Karlsson
2021-09-04 10:19:42 +10:00
parent 95bd8fc07c
commit 58abeeb9bd
37 changed files with 882 additions and 136 deletions

5
.gitignore vendored
View File

@@ -1,8 +1,7 @@
.vs/ .vs/
.vscode/ .vscode/
.git/ .git/
/CloudAPIPowerShellManagement.Log /*.Log
/CloudAPIPowerShellManagement.Lo_ /*.Lo_
/Documentation/Get-LanguageStrings.ps1
/*.csv /*.csv

View File

@@ -12,7 +12,7 @@ This module handles the WPF UI
function Get-ModuleVersion function Get-ModuleVersion
{ {
'3.1.5' '3.1.6'
} }
function Start-CoreApp function Start-CoreApp
@@ -484,7 +484,7 @@ function Show-UpdatesDialog
$content = Invoke-RestMethod "https://api.github.com/repos/Micke-K/IntuneManagement/contents/ReleaseNotes.md" $content = Invoke-RestMethod "https://api.github.com/repos/Micke-K/IntuneManagement/contents/ReleaseNotes.md"
if($content) if($content)
{ {
$txt = [System.Text.Encoding]::ASCII.GetString(([System.Convert]::FromBase64String($content.content))) $txt = [System.Text.Encoding]::UTF8.GetString(([System.Convert]::FromBase64String($content.content)))
Set-XamlProperty $script:dlgUpdates "txtReleaseNotes" "Text" $txt Set-XamlProperty $script:dlgUpdates "txtReleaseNotes" "Text" $txt
if($content.sha -ne $curHash.Hash) if($content.sha -ne $curHash.Hash)
@@ -1528,8 +1528,8 @@ function Get-JWTtoken
while ($payload.Length % 4) { $payload += "=" } # Add padding to match required length while ($payload.Length % 4) { $payload += "=" } # Add padding to match required length
return (New-Object PSObject -Property @{ return (New-Object PSObject -Property @{
Header=(([System.Text.Encoding]::ASCII.GetString(([System.Convert]::FromBase64String($header)))) | ConvertFrom-Json) Header=(([System.Text.Encoding]::UTF8.GetString(([System.Convert]::FromBase64String($header)))) | ConvertFrom-Json)
Payload=(([System.Text.Encoding]::ASCII.GetString(([System.Convert]::FromBase64String($payload)))) | ConvertFrom-Json) Payload=(([System.Text.Encoding]::UTF8.GetString(([System.Convert]::FromBase64String($payload)))) | ConvertFrom-Json)
}) })
} }
#endregion #endregion

View File

@@ -109,7 +109,7 @@ These files will have to be re-generated when new functionality is released in I
**Scripts for Generated Files** **Scripts for Generated Files**
The scripts that automatically generates language files, translation files, object info etc. are not included in the release. These scripts are currently not in a state that they can be released. The best would be if Microsoft released all the required information in Graph. A deep dive into graph suggests that it might be possible in the future since some information about the generated files are there but with some properties missing or missing. The information can't be accessed unless an API is called that gets the definition for all the profiles at the same time. The scripts that automatically generates language files, translation files, object info etc. are not included in the release. These scripts are currently not in a state that they can be released. The best would be if Microsoft released all the required information in Graph. A deep dive into graph suggests that it might be possible in the future since some information about the generated files are there but with some properties missing or language text missing. The information can't be accessed unless an API is called that gets the definition for all the profiles at the same time (the file is over 100MB).
## Extending The Documentation ## Extending The Documentation
@@ -130,7 +130,7 @@ The priority order for object documentation is:
**Documentation Provider** **Documentation Provider**
The documentation provider takes care of the collection information about the object. The `DocumentationCustom.psm1`file is an example of this. This file has examples of custom translation of properties for json files and examples of custom translation of objects via a PowerShell functions. The documentation provider takes care of collecting all the information about the object. The `DocumentationCustom.psm1`file is an example of this. This file has examples of custom translation of properties for json files and examples of custom translation of objects via a PowerShell functions.
Documentation providers has a Priority property. This defines in what order the providers will be triggered. The provider with the lowest priority number will be executed first. The included custom documentation provider has a priority number of 1000. The information gathering of the provider can be overridden by creating a custom documentation provider with a lower priority number. Documentation providers has a Priority property. This defines in what order the providers will be triggered. The provider with the lowest priority number will be executed first. The included custom documentation provider has a priority number of 1000. The information gathering of the provider can be overridden by creating a custom documentation provider with a lower priority number.

View File

@@ -351,6 +351,22 @@
] ]
} }
}, },
{
"nameResourceKey": "OfficeSuiteAppsTab.xmlConfigurationLabel",
"descriptionResourceKey": "",
"entityKey": "MSAppsConfigXml",
"dataType": 20,
"booleanActions": 0,
"category": "OfficeSuiteAppsTab.appSuiteConfigurationLabel",
"Condition": {
"Expressions": [
{
"property": "@OData.Type",
"value": "#microsoft.graph.officeSuiteApp"
}
]
}
},
{ {
"nameResourceKey": "OfficeSuiteAppsTab.appsToBeInstalledLabel", "nameResourceKey": "OfficeSuiteAppsTab.appsToBeInstalledLabel",
"descriptionResourceKey": "OfficeSuiteAppsTab.selectOfficeAppsTooltip", "descriptionResourceKey": "OfficeSuiteAppsTab.selectOfficeAppsTooltip",
@@ -393,7 +409,12 @@
} }
], ],
"Condition": { "Condition": {
"type": "and",
"Expressions": [ "Expressions": [
{
"property": "MSAppsConfigXml",
"operator": "null"
},
{ {
"property": "@OData.Type", "property": "@OData.Type",
"value": "#microsoft.graph.officeSuiteApp" "value": "#microsoft.graph.officeSuiteApp"
@@ -419,7 +440,12 @@
} }
], ],
"Condition": { "Condition": {
"type": "and",
"Expressions": [ "Expressions": [
{
"property": "MSAppsConfigXml",
"operator": "null"
},
{ {
"property": "@OData.Type", "property": "@OData.Type",
"value": "#microsoft.graph.officeSuiteApp" "value": "#microsoft.graph.officeSuiteApp"
@@ -433,7 +459,12 @@
"dataType": 8, "dataType": 8,
"booleanActions": 0, "booleanActions": 0,
"Condition": { "Condition": {
"type": "and",
"Expressions": [ "Expressions": [
{
"property": "MSAppsConfigXml",
"operator": "null"
},
{ {
"property": "@OData.Type", "property": "@OData.Type",
"value": "#microsoft.graph.officeSuiteApp" "value": "#microsoft.graph.officeSuiteApp"
@@ -459,7 +490,12 @@
} }
], ],
"Condition": { "Condition": {
"type": "and",
"Expressions": [ "Expressions": [
{
"property": "MSAppsConfigXml",
"operator": "null"
},
{ {
"property": "@OData.Type", "property": "@OData.Type",
"value": "#microsoft.graph.officeSuiteApp" "value": "#microsoft.graph.officeSuiteApp"
@@ -497,7 +533,12 @@
} }
], ],
"Condition": { "Condition": {
"type": "and",
"Expressions": [ "Expressions": [
{
"property": "MSAppsConfigXml",
"operator": "null"
},
{ {
"property": "@OData.Type", "property": "@OData.Type",
"value": "#microsoft.graph.officeSuiteApp" "value": "#microsoft.graph.officeSuiteApp"
@@ -513,7 +554,12 @@
"booleanActions": 109, "booleanActions": 109,
"category": "OfficeSuiteAppsTab.appSuiteConfigurationLabel", "category": "OfficeSuiteAppsTab.appSuiteConfigurationLabel",
"Condition": { "Condition": {
"type": "and",
"Expressions": [ "Expressions": [
{
"property": "MSAppsConfigXml",
"operator": "null"
},
{ {
"property": "@OData.Type", "property": "@OData.Type",
"value": "#microsoft.graph.officeSuiteApp" "value": "#microsoft.graph.officeSuiteApp"
@@ -529,7 +575,12 @@
"booleanActions": 0, "booleanActions": 0,
"category": "OfficeSuiteAppsTab.appSuiteConfigurationLabel", "category": "OfficeSuiteAppsTab.appSuiteConfigurationLabel",
"Condition": { "Condition": {
"type": "and",
"Expressions": [ "Expressions": [
{
"property": "MSAppsConfigXml",
"operator": "null"
},
{ {
"property": "@OData.Type", "property": "@OData.Type",
"value": "#microsoft.graph.officeSuiteApp" "value": "#microsoft.graph.officeSuiteApp"
@@ -543,7 +594,12 @@
"dataType": 8, "dataType": 8,
"booleanActions": 0, "booleanActions": 0,
"Condition": { "Condition": {
"type": "and",
"Expressions": [ "Expressions": [
{
"property": "MSAppsConfigXml",
"operator": "null"
},
{ {
"property": "@OData.Type", "property": "@OData.Type",
"value": "#microsoft.graph.officeSuiteApp" "value": "#microsoft.graph.officeSuiteApp"
@@ -559,7 +615,12 @@
"booleanActions": 109, "booleanActions": 109,
"category": "OfficeSuiteAppsTab.appSuiteConfigurationLabel", "category": "OfficeSuiteAppsTab.appSuiteConfigurationLabel",
"Condition": { "Condition": {
"type": "and",
"Expressions": [ "Expressions": [
{
"property": "MSAppsConfigXml",
"operator": "null"
},
{ {
"property": "@OData.Type", "property": "@OData.Type",
"value": "#microsoft.graph.officeSuiteApp" "value": "#microsoft.graph.officeSuiteApp"
@@ -575,7 +636,12 @@
"booleanActions": 109, "booleanActions": 109,
"category": "OfficeSuiteAppsTab.appSuiteConfigurationLabel", "category": "OfficeSuiteAppsTab.appSuiteConfigurationLabel",
"Condition": { "Condition": {
"type": "and",
"Expressions": [ "Expressions": [
{
"property": "MSAppsConfigXml",
"operator": "null"
},
{ {
"property": "@OData.Type", "property": "@OData.Type",
"value": "#microsoft.graph.officeSuiteApp" "value": "#microsoft.graph.officeSuiteApp"
@@ -591,7 +657,12 @@
"booleanActions": 109, "booleanActions": 109,
"category": "OfficeSuiteAppsTab.appSuiteConfigurationLabel", "category": "OfficeSuiteAppsTab.appSuiteConfigurationLabel",
"Condition": { "Condition": {
"type": "and",
"Expressions": [ "Expressions": [
{
"property": "MSAppsConfigXml",
"operator": "null"
},
{ {
"property": "@OData.Type", "property": "@OData.Type",
"value": "#microsoft.graph.officeSuiteApp" "value": "#microsoft.graph.officeSuiteApp"
@@ -607,7 +678,12 @@
"booleanActions": 0, "booleanActions": 0,
"category": "OfficeSuiteAppsTab.appSuiteConfigurationLabel", "category": "OfficeSuiteAppsTab.appSuiteConfigurationLabel",
"Condition": { "Condition": {
"type": "and",
"Expressions": [ "Expressions": [
{
"property": "MSAppsConfigXml",
"operator": "null"
},
{ {
"property": "@OData.Type", "property": "@OData.Type",
"value": "#microsoft.graph.officeSuiteApp" "value": "#microsoft.graph.officeSuiteApp"
@@ -660,7 +736,12 @@
"category": "OfficeSuiteAppsTab.appSuiteConfigurationLabel", "category": "OfficeSuiteAppsTab.appSuiteConfigurationLabel",
"unconfiguredValue": "os-default", "unconfiguredValue": "os-default",
"Condition": { "Condition": {
"type": "and",
"Expressions": [ "Expressions": [
{
"property": "MSAppsConfigXml",
"operator": "null"
},
{ {
"property": "@OData.Type", "property": "@OData.Type",
"value": "#microsoft.graph.officeSuiteApp" "value": "#microsoft.graph.officeSuiteApp"

View File

@@ -0,0 +1,41 @@
[
{
"nameResourceKey": "ProactiveRemediations.Create.Settings.DetectionScriptMultiLineTextBox.label",
"descriptionResourceKey": "",
"entityKey": "detectionScriptAdded",
"dataType": 0,
"booleanActions": 109,
"category": "WindowsManagement.scriptsettingsTabHeader"
},
{
"nameResourceKey": "ProactiveRemediations.Create.Settings.RemediationScriptMultiLineTextBox.label",
"descriptionResourceKey": "",
"entityKey": "remediationScriptAdded",
"dataType": 0,
"booleanActions": 109,
"category": "WindowsManagement.scriptsettingsTabHeader"
},
{
"nameResourceKey": "ProactiveRemediations.Create.Settings.LoggedOnCredentialsOptionPicker.label",
"descriptionResourceKey": "",
"entityKey": "useLoggedOnCredentials",
"dataType": 0,
"booleanActions": 109
},
{
"nameResourceKey": "ProactiveRemediations.Create.Settings.EnforceScriptSignatureCheckOptionPicker.label",
"descriptionResourceKey": "",
"entityKey": "enforceSignatureCheck",
"dataType": 0,
"booleanActions": 109
},
{
"nameResourceKey": "ProactiveRemediations.Create.Settings.RunIn64BitPSOptionPicker.label",
"descriptionResourceKey": "",
"entityKey": "runAs32Bit",
"dataType": 0,
"booleanActions": 110
}
]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -11,7 +11,7 @@ Objects can be compared based on Properties or Documentatation info.
function Get-ModuleVersion function Get-ModuleVersion
{ {
'1.0.6' '1.0.7'
} }
function Invoke-InitializeModule function Invoke-InitializeModule
@@ -399,8 +399,9 @@ function Invoke-BulkCompareNamedObjects
} }
else else
{ {
# Add objects that are exported but deleted $sourceObj = Get-GraphObject $graphObj.Object $graphObj.ObjectType
Write-Log "Object '$((Get-GraphObjectName $graphObj.Object $graphObj.ObjectType))' with id $($graphObj.Object.Id) has no matching object with the compate pattern" 2 # Add objects that are exported but deleted/not imported etc.
Write-Log "Object '$((Get-GraphObjectName $graphObj.Object $graphObj.ObjectType))' with id $($graphObj.Object.Id) has no matching object with the compare pattern" 2
$compareProperties = @([PSCustomObject]@{ $compareProperties = @([PSCustomObject]@{
Object1Value = (Get-GraphObjectName $graphObj.Object $graphObj.ObjectType) Object1Value = (Get-GraphObjectName $graphObj.Object $graphObj.ObjectType)
Object2Value = $null Object2Value = $null
@@ -828,7 +829,15 @@ function Compare-Objects
$script:compareProperties = @() $script:compareProperties = @()
if($global:cbCompareType.SelectedItem.Compare) if($obj1.'@OData.Type' -eq "#microsoft.graph.deviceManagementConfigurationPolicy" -or
$obj1.'@OData.Type' -eq "#microsoft.graph.deviceManagementIntent" -or
$obj1.'@OData.Type' -eq "#microsoft.graph.groupPolicyConfiguration")
{
# Always use documentation for Settings Catalog, Endpoint Security and Administrative Template policies
# These use Graph API for docummentation and all properties will be documented
$compareResult = Compare-ObjectsBasedonDocumentation $obj1 $obj2 $objectType
}
elseif($global:cbCompareType.SelectedItem.Compare)
{ {
$compareResult = & $global:cbCompareType.SelectedItem.Compare $obj1 $obj2 $objectType $compareResult = & $global:cbCompareType.SelectedItem.Compare $obj1 $obj2 $objectType
} }

View File

@@ -20,7 +20,7 @@ $global:documentationProviders = @()
function Get-ModuleVersion function Get-ModuleVersion
{ {
'1.0.6' '1.0.7'
} }
function Invoke-InitializeModule function Invoke-InitializeModule
@@ -443,6 +443,10 @@ function Get-ObjectTypeString
elseif($objTypeId -eq "TenantAdmin") elseif($objTypeId -eq "TenantAdmin")
{ {
return (Get-LanguageString "Titles.tenantAdmin") return (Get-LanguageString "Titles.tenantAdmin")
}
elseif($objTypeId -eq "Azure")
{
return "Azure"
} }
} }
@@ -1600,10 +1604,14 @@ function Invoke-VerifyCondition
return $false return $false
} }
if($expression.value -eq $null) if($expression.operator -eq "null")
{
$tmpRet = $null -eq $tmpProp.Value
}
elseif($null -eq $expression.value)
{ {
# Value not specified. Check if the property is set # Value not specified. Check if the property is set
$tmpRet = $tmpProp.Value -ne $null $tmpRet = $null -ne $tmpProp.Value
} }
elseif($expression.operator -eq "ne") elseif($expression.operator -eq "ne")
{ {
@@ -1737,7 +1745,7 @@ function Invoke-TranslateSection
{ {
$value = Get-LanguageString $prop.value $value = Get-LanguageString $prop.value
Add-PropertyInfo $prop $value $rawValue Add-PropertyInfo $prop $value $rawValue $rawValue
} }
} }
elseif([String]::IsNullOrEmpty($prop.entityKey) -eq $false) elseif([String]::IsNullOrEmpty($prop.entityKey) -eq $false)
@@ -1791,6 +1799,7 @@ function Invoke-TranslateSection
{ {
$value = $cert.displayName $value = $cert.displayName
} }
$rawValue = $value
} }
} }
elseif($prop.dataType -eq 200) # Multi option based on boolean value elseif($prop.dataType -eq 200) # Multi option based on boolean value
@@ -2063,7 +2072,7 @@ function Get-PropertyInfo
$categoryStr = Get-Category $prop.category $categoryStr = Get-Category $prop.category
} }
if(!$jsonValue -and $rawValue -ne $null -and "$($rawValue)" -ne "") if(!$jsonValue -and $null -ne $rawValue -and "$($rawValue)" -ne "")
{ {
$jsonValue = $rawValue | ConvertTo-Json -Depth 10 -Compress $jsonValue = $rawValue | ConvertTo-Json -Depth 10 -Compress
} }
@@ -2436,7 +2445,7 @@ function Invoke-TranslateOption
Value=$optionValue Value=$optionValue
} }
Add-PropertyInfo $prop $optionValue -originalValue $propValue Add-PropertyInfo $prop $optionValue $propValue
if($SkipOptionChildren -ne $true) if($SkipOptionChildren -ne $true)
{ {
Invoke-ChildSections (Get-CustomChildObject $obj $prop) $option Invoke-ChildSections (Get-CustomChildObject $obj $prop) $option
@@ -3138,6 +3147,8 @@ function Show-DocumentationForm
{ {
foreach($groupId in ($objectTypes | Select GroupId -Unique).GroupId) foreach($groupId in ($objectTypes | Select GroupId -Unique).GroupId)
{ {
if(-not $groupId) { continue }
#$script:DocumentationLanguage = ?? $global:cbDocumentationLanguage.SelectedValue "en" #$script:DocumentationLanguage = ?? $global:cbDocumentationLanguage.SelectedValue "en"
$script:DocumentationLanguage = "en" $script:DocumentationLanguage = "en"
$groupName = Get-ObjectTypeString -ObjectType $groupId $groupName = Get-ObjectTypeString -ObjectType $groupId
@@ -3375,9 +3386,24 @@ function Show-DocumentationForm
} }
$tmpCurObjectType = $null $tmpCurObjectType = $null
$tmpCurObjectGroup = $null
$allObjectTypeObjects = @()
foreach($tmpObj in ($sourceList)) foreach($tmpObj in ($sourceList))
{ {
$obj = Get-GraphObject $tmpObj.Object $tmpObj.ObjectType if($allObjectTypeObjects.Count -gt 0 -and $tmpCurObjectGroup -ne $tmpObj.ObjectType.GroupId -and $tmpCurObjectType -ne $tmpObj.ObjectType.Id)
{
if($global:cbDocumentationType.SelectedItem.ProcessAllObjects)
{
& $global:cbDocumentationType.SelectedItem.ProcessAllObjects $allObjectTypeObjects
$allObjectTypeObjects = @()
}
else
{
Write-Log "ProcessAllObjects not defined. $tmpCurObjectType will not be documented" 3
}
}
$obj = Get-GraphObject $tmpObj.Object $tmpObj.ObjectType
if($obj) if($obj)
{ {
@@ -3398,19 +3424,35 @@ function Show-DocumentationForm
{ {
# The provider takes care of all the processing # The provider takes care of all the processing
Write-Status "Run CustomProcess for $($global:cbDocumentationType.SelectedItem.Name)" Write-Status "Run CustomProcess for $($global:cbDocumentationType.SelectedItem.Name)"
& $global:cbDocumentationType.SelectedItem.CustomProcess $obj $documentedObj $ret = & $global:cbDocumentationType.SelectedItem.CustomProcess $obj $documentedObj
continue if($ret -is [boolean] -and $ret -eq $true) { continue }
} }
if($tmpCurObjectType -ne $obj.ObjectType.GroupId) if($tmpCurObjectGroup -ne $obj.ObjectType.GroupId)
{ {
# A group matches a menu item in the protal but can contain multiple object types
# New object group e.g. Script, Tennant, Device Configuration
if($global:cbDocumentationType.SelectedItem.NewObjectGroup)
{
Write-Status "Run NewObjectGroup for $($global:cbDocumentationType.SelectedItem.Name)"
$ret = & $global:cbDocumentationType.SelectedItem.NewObjectGroup $obj $documentedObj
if($ret -is [boolean] -and $ret -eq $true) { continue }
}
$tmpCurObjectGroup = $obj.ObjectType.GroupId
}
if($tmpCurObjectType -ne $obj.ObjectType.Id)
{
# New object type e.g Administrative Template, VPN profile etc.
if($global:cbDocumentationType.SelectedItem.NewObjectType) if($global:cbDocumentationType.SelectedItem.NewObjectType)
{ {
Write-Status "Run NewObjectType for $($global:cbDocumentationType.SelectedItem.Name)" Write-Status "Run NewObjectType for $($global:cbDocumentationType.SelectedItem.Name)"
& $global:cbDocumentationType.SelectedItem.NewObjectType $obj $documentedObj $ret = & $global:cbDocumentationType.SelectedItem.NewObjectType $obj $documentedObj
if($ret -is [boolean] -and $ret -eq $true) { continue }
} }
$tmpCurObjectType = $obj.ObjectType.GroupId $tmpCurObjectType = $obj.ObjectType.Id
} $allObjectTypeObjects = @()
}
if($documentedObj) if($documentedObj)
{ {
@@ -3435,15 +3477,16 @@ function Show-DocumentationForm
$filteredSettings = @() $filteredSettings = @()
foreach($item in $documentedObj.Settings) foreach($item in $documentedObj.Settings)
{ {
if(-not ($item.PSObject.Properties | Where Name -eq RawValue) -or $documentedObj.UpdateFilteredObject -eq $false) if(-not ($item.PSObject.Properties | Where Name -eq "RawValue") -or $documentedObj.UpdateFilteredObject -eq $false)
{ {
$filteredSettings = $documentedObj.Settings $filteredSettings = $documentedObj.Settings
break break
} }
if($global:chkSkipNotConfigured.IsChecked -and (([String]::IsNullOrEmpty($item.RawValue) -or ("$($item.RawValue)" -eq "notConfigured")))) if($global:chkSkipNotConfigured.IsChecked -and (($item.RawValue -isnot [array] -and ([String]::IsNullOrEmpty($item.RawValue) -or ("$($item.RawValue)" -eq "notConfigured"))) -or ($item.RawValue -is [array] -and ($item.RawValue | measure).Count -eq 0)))
{ {
# Skip unconfigured items e.g. properties with null values # Skip unconfigured items e.g. properties with null values
# Note: This could removed configured properties if RawValue is not specified
continue continue
} }
elseif($global:chkSkipNotConfigured.IsChecked -and $documentedObj.UnconfiguredProperties -and ($documentedObj.UnconfiguredProperties | Where EntityKey -eq $item.EntityKey)) elseif($global:chkSkipNotConfigured.IsChecked -and $documentedObj.UnconfiguredProperties -and ($documentedObj.UnconfiguredProperties | Where EntityKey -eq $item.EntityKey))
@@ -3472,7 +3515,7 @@ function Show-DocumentationForm
} }
} }
if($updateNotConfigured -and ($item.RawValue -eq $null -or "$($item.RawValue)" -eq "" -or "$($item.RawValue)" -eq "notConfigured") -and [String]::IsNullOrEmpty($item.Value)) if($updateNotConfigured -and (($item.RawValue -isnot [array] -and ($null -eq $item.RawValue -or "$($item.RawValue)" -eq "" -or "$($item.RawValue)" -eq "notConfigured") -and [String]::IsNullOrEmpty($item.Value)) -or ($item.RawValue -is [array] -and ($item.RawValue | measure).Count -eq 0)))
{ {
$item.Value = $notConfiguredText $item.Value = $notConfiguredText
} }
@@ -3489,12 +3532,36 @@ function Show-DocumentationForm
$documentedObj | Add-Member Noteproperty -Name "FilteredSettings" -Value $filteredSettings -Force $documentedObj | Add-Member Noteproperty -Name "FilteredSettings" -Value $filteredSettings -Force
& $global:cbDocumentationType.SelectedItem.Process $obj.Object $obj.ObjectType $documentedObj if($obj.ObjectType.DocumentAll -eq $true)
{
$allObjectTypeObjects += [PSCustomObject]@{
Object = $obj
DocumentationObject = $documentedObj
}
}
else
{
& $global:cbDocumentationType.SelectedItem.Process $obj.Object $obj.ObjectType $documentedObj
}
} }
} }
} }
} }
if($allObjectTypeObjects.Count -gt 0)
{
if($global:cbDocumentationType.SelectedItem.ProcessAllObjects)
{
& $global:cbDocumentationType.SelectedItem.ProcessAllObjects $allObjectTypeObjects
$allObjectTypeObjects = @()
}
else
{
Write-Log "ProcessAllObjects not defined. $tmpCurObjectType will not be documented" 3
}
}
if($global:cbDocumentationType.SelectedItem.PostProcess) if($global:cbDocumentationType.SelectedItem.PostProcess)
{ {
Write-Status "Run PostProcess for $($global:cbDocumentationType.SelectedItem.Name)" Write-Status "Run PostProcess for $($global:cbDocumentationType.SelectedItem.Name)"

View File

@@ -10,7 +10,7 @@ This module will also document some objects based on PowerShell functions
function Get-ModuleVersion function Get-ModuleVersion
{ {
'1.0.4' '1.0.5'
} }
function Invoke-InitializeModule function Invoke-InitializeModule
@@ -306,6 +306,20 @@ function Add-CDDocumentCustomProfileValue
} }
} }
} }
elseif($obj.'@OData.Type' -like "#microsoft.graph.windows10VpnConfiguration")
{
if($prop.EntityKey -eq "enableSplitTunneling" -and $prop.enabled -eq $false)
{
# SplitTunneling settings are moved to another file
return $false
}
elseif($prop.EntityKey -eq "eapXml" -and $obj.eapXml)
{
$propValue = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($obj.eapXml))
Add-PropertyInfo $prop $propValue -originalValue $propValue
return $false
}
}
} }
<# <#
@@ -638,6 +652,11 @@ function Add-CDDocumentCustomProfileProperty
$obj | Add-Member Noteproperty -Name "useMicrosoftSearchAsDefault" -Value ($obj.excludedApps.bing -eq $false) $obj | Add-Member Noteproperty -Name "useMicrosoftSearchAsDefault" -Value ($obj.excludedApps.bing -eq $false)
if($obj.officeConfigurationXml)
{
$xmlConfig = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($obj.officeConfigurationXml))
$obj | Add-Member Noteproperty -Name "MSAppsConfigXml" -Value $xmlConfig
}
$retValue = $true $retValue = $true
} }
elseif($obj.'@OData.Type' -like "#microsoft.graph.windowsWifiEnterpriseEAPConfiguration") elseif($obj.'@OData.Type' -like "#microsoft.graph.windowsWifiEnterpriseEAPConfiguration")
@@ -1037,6 +1056,12 @@ function Add-CDDocumentCustomProfileProperty
$obj | Add-Member Noteproperty -Name "returnCodes" -Value ($returnCodes -join $objSeparator) -Force $obj | Add-Member Noteproperty -Name "returnCodes" -Value ($returnCodes -join $objSeparator) -Force
$obj | Add-Member Noteproperty -Name "win10Release" -Value (Get-LanguageString "MinimumOperatingSystem.Windows.V10Release.release$($obj.minimumSupportedWindowsRelease)") -Force $obj | Add-Member Noteproperty -Name "win10Release" -Value (Get-LanguageString "MinimumOperatingSystem.Windows.V10Release.release$($obj.minimumSupportedWindowsRelease)") -Force
} }
elseif($obj.'@OData.Type' -eq "#microsoft.graph.deviceHealthScript")
{
$obj | Add-Member Noteproperty -Name "detectionScriptAdded" -Value (-not [String]::IsNullOrEmpty($obj.detectionScriptContent))
$obj | Add-Member Noteproperty -Name "remediationScriptAdded" -Value (-not [String]::IsNullOrEmpty($obj.remediationScriptContent))
$obj | Add-Member Noteproperty -Name "useLoggedOnCredentials" -Value ($obj.runAsAccount -ne "system")
}
if(($obj.PSObject.Properties | where Name -eq "securityRequireSafetyNetAttestationBasicIntegrity") -and if(($obj.PSObject.Properties | where Name -eq "securityRequireSafetyNetAttestationBasicIntegrity") -and
($obj.PSObject.Properties | where Name -eq "securityRequireSafetyNetAttestationCertifiedDevice")) ($obj.PSObject.Properties | where Name -eq "securityRequireSafetyNetAttestationCertifiedDevice"))

View File

@@ -3,7 +3,7 @@
#https://docs.microsoft.com/en-us/office/vba/api/overview/word #https://docs.microsoft.com/en-us/office/vba/api/overview/word
function Get-ModuleVersion function Get-ModuleVersion
{ {
'1.0.3' '1.0.4'
} }
function Invoke-InitializeModule function Invoke-InitializeModule
@@ -27,9 +27,10 @@ function Invoke-InitializeModule
OutputOptions = (Add-WordOptionsControl) OutputOptions = (Add-WordOptionsControl)
Activate = { Invoke-WordActivate @args } Activate = { Invoke-WordActivate @args }
PreProcess = { Invoke-WordPreProcessItems @args } PreProcess = { Invoke-WordPreProcessItems @args }
NewObjectType = { Invoke-WordNewObjectType @args } NewObjectGroup = { Invoke-WordNewObjectGroup @args }
Process = { Invoke-WordProcessItem @args } Process = { Invoke-WordProcessItem @args }
PostProcess = { Invoke-WordPostProcessItems @args } PostProcess = { Invoke-WordPostProcessItems @args }
ProcessAllObjects = { Invoke-WordProcessAllObjects @args }
}) })
$script:columnHeaders = @{ $script:columnHeaders = @{
@@ -389,7 +390,7 @@ function Set-WordContentControlText
} }
} }
function Invoke-WordNewObjectType function Invoke-WordNewObjectGroup
{ {
param($obj, $documentedObj) param($obj, $documentedObj)
@@ -408,7 +409,7 @@ function Invoke-WordProcessItem
Add-DocText $objName $global:txtWordHeader2Style.Text Add-DocText $objName $global:txtWordHeader2Style.Text
$script:doc.Application.Selection.TypeParagraph() $script:doc.Application.Selection.TypeParagraph()
try try
{ {
@@ -517,6 +518,45 @@ function Invoke-WordProcessItem
} }
} }
function Invoke-WordProcessAllObjects
{
param($allObjectTypeObjects)
if(($allObjectTypeObjects | measure).Count -eq 0) { return }
$tmpObj = $allObjectTypeObjects | Select -First 1
if(-not $tmpObj) { return }
$objectType = $tmpObj.Object.ObjectType
if($objectType.Id -eq "ScopeTags")
{
$objTypeName = Get-LanguageString "SettingDetails.scopeTags"
Add-DocText $objTypeName $global:txtWordHeader2Style.Text
$script:doc.Application.Selection.TypeParagraph()
$items = @()
$nameLabel = Get-LanguageString "Inputs.displayNameLabel"
$descriptionLable = Get-LanguageString "TableHeaders.description"
foreach($obj in $allObjectTypeObjects.Object.Object)
{
$items += [PSCustomObject]@{
$nameLabel = $obj.displayName
ID = $obj.Id
$descriptionLable = $obj.Description
}
}
$items = $items | Sort -Property $nameLabel
$properties = @($nameLabel,"id",$descriptionLable)
Add-DocTableItems $tmpObj.Object.Object $tmpObj.Object.ObjectType $items $properties -captionOverride (Get-LanguageString "SettingDetails.scopeTags")
}
}
function Invoke-WordTranslateColumnHeader function Invoke-WordTranslateColumnHeader
{ {
param($columnName) param($columnName)
@@ -529,6 +569,13 @@ function Invoke-WordTranslateColumnHeader
(?? $lngText $columnName) (?? $lngText $columnName)
} }
function Invoke-WordCustomProcessItems
{
param($obj, $documentedObj)
}
function Set-WordColumnHeaderLanguageId function Set-WordColumnHeaderLanguageId
{ {
param($columnName, $lngId) param($columnName, $lngId)
@@ -547,7 +594,7 @@ function Set-WordColumnHeaderLanguageId
function Add-DocTableItems function Add-DocTableItems
{ {
param($obj, $objectType, $items, $properties, $lngId, [switch]$AddCategories, [switch]$AddSubcategories) param($obj, $objectType, $items, $properties, $lngId, [switch]$AddCategories, [switch]$AddSubcategories, $captionOverride)
$tblHeaderStyle = $global:txtWordTableHeaderStyle.Text $tblHeaderStyle = $global:txtWordTableHeaderStyle.Text
$tblCategoryStyle = $global:txtWordCategoryHeaderStyle.Text $tblCategoryStyle = $global:txtWordCategoryHeaderStyle.Text
@@ -559,7 +606,11 @@ function Add-DocTableItems
$script:docTable.ApplyStyleHeadingRows = $true $script:docTable.ApplyStyleHeadingRows = $true
Set-DocObjectStyle $script:docTable $global:txtWordTableStyle.Text Set-DocObjectStyle $script:docTable $global:txtWordTableStyle.Text
if($lngId) if($captionOverride)
{
$caption = $captionOverride
}
elseif($lngId)
{ {
$caption = "$((Get-LanguageString $lngId)) - $((Get-GraphObjectName $obj $objectType))" $caption = "$((Get-LanguageString $lngId)) - $((Get-GraphObjectName $obj $objectType))"
} }

View File

@@ -10,7 +10,7 @@ This module is for the Endpoint Manager/Intune View. It manages Export/Import/Co
#> #>
function Get-ModuleVersion function Get-ModuleVersion
{ {
'3.1.9' '3.1.10'
} }
function Invoke-InitializeModule function Invoke-InitializeModule
@@ -106,9 +106,8 @@ function Invoke-InitializeModule
Permissons=@("DeviceManagementConfiguration.ReadWrite.All") Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
PropertiesToRemove = @("privacyAccessControls") PropertiesToRemove = @("privacyAccessControls")
PostFileImportCommand = { Start-PostFileImportDeviceConfiguration @args } PostFileImportCommand = { Start-PostFileImportDeviceConfiguration @args }
PreCopyCommand = { Start-PreCopyDeviceConfiguration @args }
PostCopyCommand = { Start-PostCopyDeviceConfiguration @args } PostCopyCommand = { Start-PostCopyDeviceConfiguration @args }
PostExportCommand = { Start-PostExportDeviceConfiguration @args } PostGetCommand = { Start-PostGetDeviceConfiguration @args }
GroupId = "DeviceConfiguration" GroupId = "DeviceConfiguration"
}) })
@@ -204,6 +203,7 @@ function Invoke-InitializeModule
Permissons=@("Organization.ReadWrite.All") Permissons=@("Organization.ReadWrite.All")
Icon = "Branding" Icon = "Branding"
SkipRemoveProperties = @('Id') SkipRemoveProperties = @('Id')
GroupId = "Azure"
}) })
Add-ViewItem (New-Object PSObject -Property @{ Add-ViewItem (New-Object PSObject -Property @{
@@ -510,6 +510,7 @@ function Invoke-InitializeModule
Permissons=@("DeviceManagementRBAC.ReadWrite.All") Permissons=@("DeviceManagementRBAC.ReadWrite.All")
PostExportCommand = { Start-PostExportScopeTags @args } PostExportCommand = { Start-PostExportScopeTags @args }
ImportOrder = 10 ImportOrder = 10
DocumentAll = $true
GroupId = "TenantAdmin" GroupId = "TenantAdmin"
}) })
@@ -954,17 +955,17 @@ function Start-PostCopyDeviceConfiguration
} }
} }
function Start-PreCopyDeviceConfiguration function Start-PostGetDeviceConfiguration
{ {
param($obj, $objectType, $newName) param($obj, $objectType)
if(($obj.omaSettings | measure).Count -gt 0) if(($obj.Object.omaSettings | measure).Count -gt 0)
{ {
foreach($omaSetting in ($obj.omaSettings | Where isEncrypted -eq $true)) foreach($omaSetting in ($obj.Object.omaSettings | Where isEncrypted -eq $true))
{ {
if($omaSetting.isEncrypted -eq $false) { continue } if($omaSetting.isEncrypted -eq $false) { continue }
$xmlValue = Invoke-GraphRequest -Url "/deviceManagement/deviceConfigurations/$($obj.Id)/getOmaSettingPlainTextValue(secretReferenceValueId='$($omaSetting.secretReferenceValueId)')" $xmlValue = Invoke-GraphRequest -Url "/deviceManagement/deviceConfigurations/$($obj.Object.Id)/getOmaSettingPlainTextValue(secretReferenceValueId='$($omaSetting.secretReferenceValueId)')"
if($xmlValue.Value) if($xmlValue.Value)
{ {
$omaSetting.isEncrypted = $false $omaSetting.isEncrypted = $false
@@ -982,49 +983,7 @@ function Start-PreCopyDeviceConfiguration
} }
} }
} }
} }
$false
}
function Start-PostExportDeviceConfiguration
{
param($obj, $objectType, $path)
$fileName = "$path\$((Remove-InvalidFileNameChars (Get-GraphObjectName $obj $objectType))).json"
if(($obj.omaSettings | measure).Count -gt 0)
{
$updated = $false
foreach($omaSetting in @(($obj.omaSettings | Where isEncrypted -eq $true)))
{
if($omaSetting.isEncrypted -eq $false) { continue }
# Get decrypted value and mark OMA-URI setting as not encrypted
$xmlValue = Invoke-GraphRequest -Url "/deviceManagement/deviceConfigurations/$($obj.Id)/getOmaSettingPlainTextValue(secretReferenceValueId='$($omaSetting.secretReferenceValueId)')"
if($xmlValue.Value)
{
$omaSetting.isEncrypted = $false
$omaSetting.secretReferenceValueId = $null
if($omaSetting.'@odata.type' -eq "#microsoft.graph.omaSettingStringXml" -or
$omaSetting.'value@odata.type' -eq "#Binary")
{
$Bytes = [System.Text.Encoding]::UTF8.GetBytes($xmlValue.Value)
$omaSetting.value = [Convert]::ToBase64String($bytes)
}
else
{
$omaSetting.value = $xmlValue.Value
}
$updated = $true
}
}
if($updated)
{
$obj | ConvertTo-Json -Depth 20 | Out-File -LiteralPath $fileName -Force
}
}
} }
#endregion #endregion
@@ -1251,6 +1210,22 @@ function Add-ScriptExtensions
{ {
$tmp.Children.Insert($index, $btnDownload) $tmp.Children.Insert($index, $btnDownload)
} }
$btnDownload = New-Object System.Windows.Controls.Button
$btnDownload.Content = 'Edit'
$btnDownload.Name = 'btnEdit'
$btnDownload.Margin = "0,0,5,0"
$btnDownload.Width = "100"
$btnDownload.Add_Click({
Invoke-EditScript
})
$tmp = $form.FindName($buttonPanel)
if($tmp)
{
$tmp.Children.Insert($index, $btnDownload)
}
} }
function Add-ScriptExportExtensions function Add-ScriptExportExtensions
@@ -1280,7 +1255,7 @@ function Start-PostExportScripts
{ {
Write-Log "Export script $($obj.FileName)" Write-Log "Export script $($obj.FileName)"
$fileName = [IO.Path]::Combine($exportPath, $obj.FileName) $fileName = [IO.Path]::Combine($exportPath, $obj.FileName)
[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($obj.scriptContent)) | Out-File -LiteralPath $fileName -Force [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($obj.scriptContent)) | Out-File -LiteralPath $fileName -Force
} }
} }
@@ -1306,6 +1281,87 @@ function Invoke-DownloadScript
} }
} }
function Invoke-EditScript
{
if(-not $global:dgObjects.SelectedItem.Object.id) { return }
$obj = (Get-GraphObject $global:dgObjects.SelectedItem $global:curObjectType)
Write-Status ""
if(-not $obj.Object.scriptContent) { return }
$script:currentScriptObject = $obj
$script:editForm = Get-XamlObject ($global:AppRootFolder + "\Xaml\EditScriptDialog.xaml")
if(-not $script:editForm) { return }
Set-XamlProperty $script:editForm "txtEditScriptTitle" "Text" "Edit: $($obj.Object.displayName)"
$scriptText = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($obj.Object.scriptContent))
Set-XamlProperty $script:editForm "txtScriptText" "Text" $scriptText
$script:currentModal = $null
if($global:grdModal.Children.Count -gt 0)
{
$script:currentModal = $global:grdModal.Children[0]
}
Add-XamlEvent $script:editForm "btnSaveScriptEdit" "add_click" ({
$scriptText = Get-XamlProperty $script:editForm "txtScriptText" "Text"
$bytes = [System.Text.Encoding]::UTF8.GetBytes($scriptText)
$encodedText = [Convert]::ToBase64String($bytes)
if($script:currentScriptObject.Object.scriptContent -ne $encodedText)
{
# Save script
if(([System.Windows.MessageBox]::Show("Are you sure you want to update the script?`n`nObject:`n$($script:currentScriptObject.displayName)", "Update script?", "YesNo", "Warning")) -eq "Yes")
{
Write-Status "Update $($script:currentScriptObject.displayName)"
$obj = $script:currentScriptObject.Object | ConvertTo-Json -Depth 20 | ConvertFrom-Json
$obj.scriptContent = $encodedText
Start-GraphPreImport $obj $script:currentScriptObject.ObjectType
foreach($prop in $script:currentScriptObject.ObjectType.PropertiesToRemoveForUpdate)
{
Remove-Property $obj $prop
}
Remove-Property $obj "Assignments"
Remove-Property $obj "isAssigned"
$json = ConvertTo-Json $obj -Depth 15
$objectUpdated = (Invoke-GraphRequest -Url "$($script:currentScriptObject.ObjectType.API)/$($script:currentScriptObject.Object.Id)" -Content $json -HttpMethod "PATCH")
if(-not $objectUpdated)
{
Write-Log "Failed to update script" 3
[System.Windows.MessageBox]::Show("Failed to save the script object. See log for more information","Update failed!", "OK", "Error")
}
Write-Status ""
}
}
$global:grdModal.Children.Clear()
if($script:currentModal)
{
$global:grdModal.Children.Add($script:currentModal)
}
[System.Windows.Forms.Application]::DoEvents()
})
Add-XamlEvent $script:editForm "btnCancelScriptEdit" "add_click" ({
$global:grdModal.Children.Clear()
if($script:currentModal)
{
$global:grdModal.Children.Add($script:currentModal)
}
[System.Windows.Forms.Application]::DoEvents()
})
$global:grdModal.Children.Clear()
$script:editForm.SetValue([System.Windows.Controls.Grid]::RowProperty,1)
$script:editForm.SetValue([System.Windows.Controls.Grid]::ColumnProperty,1)
$global:grdModal.Children.Add($script:editForm) | Out-Null
[System.Windows.Forms.Application]::DoEvents()
}
#endregion #endregion
#region Terms and Conditions #region Terms and Conditions

View File

@@ -0,0 +1,249 @@
<#
.SYNOPSIS
Module for listing Intune assignments
.DESCRIPTION
.NOTES
Author: Mikael Karlsson
#>
function Get-ModuleVersion
{
'1.0.0'
}
function Invoke-InitializeModule
{
Add-EMToolsViewItem (New-Object PSObject -Property @{
Title = "Intune Assignments"
Id = "IntuneAssignments"
ViewID = "EMTools"
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
Icon="DeviceConfiguration"
ShowViewItem = { Show-EMToolsIntuneAssignments }
})
}
function Show-EMToolsIntuneAssignments
{
if(-not $script:frmIntuneAssignments)
{
$script:frmIntuneAssignments = Get-XamlObject ($global:AppRootFolder + "\Xaml\EndpointManagerToolsIntuneAssignments.xaml") #-AddVariables
if(-not $script:frmIntuneAssignments) { return }
Add-XamlEvent $script:frmIntuneAssignments "btnBrowseIntuneAssignmentsExportPath" "add_click" ({
$folder = Get-Folder (Get-XamlProperty $script:frmIntuneAssignments "txtIntuneAssignmentsExportPath" "Text") "Select root folder for exported files"
if($folder)
{
Set-XamlProperty $script:frmIntuneAssignments "txtIntuneAssignmentsExportPath" "Text" $folder
}
})
Add-XamlEvent $script:frmIntuneAssignments "btnGetIntuneAssignments" "add_click" ({
$folder = Get-XamlProperty $script:frmIntuneAssignments "txtIntuneAssignmentsExportPath" "Text"
if($folder)
{
Write-Status "Get Intune Assignments"
Get-EMIntuneAssignments $folder
Write-Status ""
}
})
Add-XamlEvent $script:frmIntuneAssignments "btnIntuneAssignmentsCopy" "add_click" ({
$script:objAssignments | Select Name, Type, IncludedString, ExcludedString | ConvertTo-Csv -NoTypeInformation | Set-Clipboard
})
Add-XamlEvent $script:frmIntuneAssignments "btnIntuneAssignmentsSave" "add_click" ({
$dlgSave = New-Object -Typename System.Windows.Forms.SaveFileDialog
#$dlgSave.InitialDirectory = Get-SettingValue "IntuneRootFolder" $env:Temp
$dlgSave.FileName = $obj.FileName
$sf.DefaultExt = "*.csv"
$sf.Filter = "CSV (*.csv)|*.csv|All files (*.*)| *.*"
if($dlgSave.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK -and $dlgSave.Filename)
{
$script:objAssignments | Select Name, Type, IncludedString, ExcludedString | ConvertTo-Csv -NoTypeInformation | Out-File -LiteralPath $dlgSave.Filename -Encoding UTF8 -Force
}
})
}
$global:grdToolsMain.Children.Clear()
$global:grdToolsMain.Children.Add($frmIntuneAssignments)
}
function Get-EMIntuneAssignmentInfo
{
param($rootDir)
Write-Status "Gather Export Information"
$path = "$rootDir\Groups"
$script:htGroups = @{}
foreach($file in (Get-Item -path "$path\*.json"))
{
$graphObj = (ConvertFrom-Json (Get-Content -LiteralPath $file.FullName -Raw))
$htGroups.Add($graphObj.Id, $graphObj)
}
$script:fileArr = @()
foreach($path in [IO.Directory]::EnumerateDirectories($rootDir))
{
if($path -eq "$rootDir\Groups") { continue }
foreach($file in (Get-Item -path "$path\*.json" -Exclude @("*_settings.json","*_assignments.json")))
{
$graphObj = (ConvertFrom-Json (Get-Content -LiteralPath $file.FullName -Raw))
$obj = New-Object PSObject -Property @{
FileName = $file.Name
FileInfo = $file
Selected = $SelectedStatus
Object = $graphObj
}
$script:fileArr += $obj
}
}
}
function Get-EMIntuneAssignments
{
param($folder)
Set-XamlProperty $script:frmIntuneAssignments "dgIntuneAssignments" "ItemsSource" $null
$folderDI = [IO.DirectoryInfo]$folder
if(-not $folderDI.Exists) { return }
Get-EMIntuneAssignmentInfo $folder
Write-Status "Collect exported assignments"
$script:objAssignments = @()
foreach($fileObj in $script:fileArr)
{
$obj = New-Object PSObject -Property @{
Object = $fileObj.Object
Name = $fileObj.Object.DisplayName
Type = $null
Included = $null
Excluded = $null
IncludedString = ""
ExcludedString = ""
}
$obj.Included = @()
$obj.Excluded = @()
if($fileObj.Object.'@OData.Type')
{
$obj.Type = $fileObj.Object.'@OData.Type'.Split('.')[-1]
}
else
{
$obj.Type = $file.Directory.Parent.Name
}
foreach($assignment in $fileObj.Object.assignments)
{
$assignmentObj = $null
$included = $true
if($assignment.target.'@odata.type' -eq "#microsoft.graph.groupAssignmentTarget" -or
$assignment.target.'@odata.type' -eq "#microsoft.graph.exclusionGroupAssignmentTarget")
{
if($script:htGroups.ContainsKey($assignment.target.groupId))
{
$assignmentObj = $script:htGroups[$assignment.target.groupId].displayName
}
else
{
Write-Warning "Could not find a group with ID $($assignment.target.groupId)"
}
$included = $assignment.target.'@odata.type' -eq "#microsoft.graph.groupAssignmentTarget"
}
elseif($assignment.target.'@odata.type' -eq "#microsoft.graph.allDevicesAssignmentTarget")
{
$assignmentObj = "All Devices"
}
elseif($assignment.target.'@odata.type' -eq "#microsoft.graph.allLicensedUsersAssignmentTarget")
{
$assignmentObj = "All Users"
}
if($included)
{
$obj.Included += $assignmentObj
}
else
{
$obj.Excluded += $assignmentObj
}
}
$obj.IncludedString = $obj.Included -join ";"
$obj.ExcludedString = $obj.Excluded -join ";"
$script:objAssignments += $obj
}
Add-XamlEvent $script:frmIntuneAssignments "txtIntuneAssignmentsFilter" "Add_LostFocus" ({
Invoke-IntueAssignmentFilterBoxChanged $this
})
Add-XamlEvent $script:frmIntuneAssignments "txtIntuneAssignmentsFilter" "Add_GotFocus" ({
if($this.Tag -eq "1" -and $this.Text -eq "Filter") { $this.Text = "" }
Invoke-IntueAssignmentFilterBoxChanged $this ($script:frmIntuneAssignments.FindName("dgIntuneAssignments"))
})
Add-XamlEvent $script:frmIntuneAssignments "txtIntuneAssignmentsFilter" "Add_TextChanged" ({
Invoke-IntueAssignmentFilterBoxChanged $this ($script:frmIntuneAssignments.FindName("dgIntuneAssignments"))
})
Invoke-IntueAssignmentFilterBoxChanged ($script:frmIntuneAssignments.FindName("txtIntuneAssignmentsFilter")) ($script:frmIntuneAssignments.FindName("dgIntuneAssignments"))
$ocList = [System.Collections.ObjectModel.ObservableCollection[object]]::new(@($script:objAssignments))
Set-XamlProperty $script:frmIntuneAssignments "dgIntuneAssignments" "ItemsSource" ([System.Windows.Data.CollectionViewSource]::GetDefaultView($ocList))
}
function Invoke-IntueAssignmentFilterBoxChanged
{
param($txtBox, $dgObject)
$filter = $null
if($txtBox.Text.Trim() -eq "" -and $txtBox.IsFocused -eq $false)
{
$txtBox.FontStyle = "Italic"
$txtBox.Tag = 1
$txtBox.Text = "Filter"
$txtBox.Foreground="Lightgray"
}
else
{
if($txtBox.Tag -eq "1" -and $txtBox.Text -eq "Filter" -and $txtBox.IsFocused -eq $false) { return }
$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) -or $item.IncludedString -match [regex]::Escape($txtBox.Text) -or $item.ExcludedString -match [regex]::Escape($txtBox.Text) )
}
}
}
if($dgObject.ItemsSource -is [System.Windows.Data.ListCollectionView])
{
# This causes odd behaviour with focus e.g. and item has to be clicked twice to be selected
$dgObject.ItemsSource.Filter = $filter
$dgObject.ItemsSource.Refresh()
}
}

View File

@@ -17,53 +17,19 @@ This module is for the Intune Tools View.
.NOTES .NOTES
Author: Mikael Karlsson Author: Mikael Karlsson
#> #>
$global:EMToolsViewObject = $null
function Get-ModuleVersion function Get-ModuleVersion
{ {
'1.0.0' '1.0.1'
} }
function Invoke-InitializeModule function Invoke-InitializeModule
{ {
$viewPanel = Get-XamlObject ($global:AppRootFolder + "\Xaml\EndpointManagerTools.xaml") -AddVariables Add-ADMXRegClasses
if(-not $viewPanel) { return }
Add-ADMXRegClasses Add-EMToolsViewItem
#Add menu group and items
$global:EMToolsViewObject = (New-Object PSObject -Property @{
Title = "Intune Tools"
Description = "Additional tools for managing Intune"
ID = "EMTools"
ViewPanel = $viewPanel
ItemChanged = { Show-EMTool }
Activating = { Invoke-EMToolsActivatingView }
Authentication = (Get-MSALAuthenticationObject)
Authenticate = { Invoke-EMToolsAuthenticateToMSAL }
AppInfo = (Get-GraphAppInfo "EM" "d1ddf0e4-d672-4dae-b554-9d5bdfd93547")
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 }
})
# https://docs.microsoft.com/en-us/windows/client-management/mdm/win32-and-centennial-app-policy-configuration # https://docs.microsoft.com/en-us/windows/client-management/mdm/win32-and-centennial-app-policy-configuration
# ADMX ingestion cannot write to these paths: # ADMX ingestion cannot write to these paths:
@@ -95,6 +61,58 @@ function Invoke-InitializeModule
"@ "@
} }
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"
ViewPanel = $viewPanel
ItemChanged = { Show-EMTool }
Activating = { Invoke-EMToolsActivatingView }
Authentication = (Get-MSALAuthenticationObject)
Authenticate = { Invoke-EMToolsAuthenticateToMSAL }
AppInfo = (Get-GraphAppInfo "EM" "d1ddf0e4-d672-4dae-b554-9d5bdfd93547")
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-EMToolsActivatingView
{ {

View File

@@ -42,7 +42,7 @@ The script can import the exported json files in multiple ways.
* **Skip if object exists:** The script will look if there is an existing object with the same name and type. It will not import the file if existing object is detected * **Skip if object exists:** The script will look if there is an existing object with the same name and type. It will not import the file if existing object is detected
* **Replace (Preview):** If a existing object is detected, the script will * **Replace (Preview):** If an existing object is detected, the script will
* Import the file without assignments * Import the file without assignments
* Copy assignments from the existing object * Copy assignments from the existing object
@@ -52,7 +52,7 @@ The script can import the exported json files in multiple ways.
* **Update (Preview):** This will update the existing object. * **Update (Preview):** This will update the existing object.
Update does not support all the properties that import does and object types behaves differently during update e.g. Settings for Endpoint Security objects will not be cleared. There is no API for removing settings only adding. If a settings does not in the import file, the existing setting will be set to Not Configured. Settings Catalog replaces the whole settings property during update. The update APIs does not support all the properties that the import API sdoes and object types behaves differently during update e.g. Settings for **Endpoint Security** objects will not be cleared. There is no API for removing settings, only adding. If a setting does not exist in the import file, the existing setting will be set to *Not Configured*. **Settings Catalog** replaces the whole settings property during update.
This has been tested with all supported object types *except* Import Scripts (Shell), Android OEM Config and Apple Enrollment Types. This has been tested with all supported object types *except* Import Scripts (Shell), Android OEM Config and Apple Enrollment Types.
@@ -115,6 +115,11 @@ Additional Intune Tools is included in the script.
* List (Key/Value pair) * List (Key/Value pair)
* This tool creates a custom ADMX file based on the specified registry keys. * This tool creates a custom ADMX file based on the specified registry keys.
* Intune Assignments
* Quickly gather all assignments from an export
* Easily identify all profiles/polices a group is assigned to
* Export as CSV to analyse in Excel
See [ADMX Import](ADMXImport.md) for more information about the ADMX tools See [ADMX Import](ADMXImport.md) for more information about the ADMX tools
## Change log ## Change log
@@ -187,16 +192,18 @@ Android Store Apps are **not** imported. The Create API is documented in Microso
Using multiple tenants support causes multiple logins/consent prompts the first time if 'Microsoft Graph PowerShell' is used. Querying the API for tenant list uses a different scope that is not included by default in the 'Microsoft Graph PowerShell' app. Using multiple tenants support causes multiple logins/consent prompts the first time if 'Microsoft Graph PowerShell' is used. Querying the API for tenant list uses a different scope that is not included by default in the 'Microsoft Graph PowerShell' app.
Using multiple tenants support *might* cause and endless loop in the login screen and cause duplicate accounts in token cache. Actual cause is not found yet but it happens on rare occasions and it looks like it happens when a guest account is used. Workaround: Cancel the login, restart the script, logout and restart the script again. ~~Using multiple tenants support *might* cause and endless loop in the login screen and cause duplicate accounts in token cache. Actual cause is not found yet but it happens on rare occasions and it looks like it happens when a guest account is used. Workaround: Cancel the login, restart the script, logout and restart the script again.~~ - Not seen this in a long time. Please create issue if this happens
When multi tenant settings is Enabled/Disabled, the Profile Info is not updated until the account is changed or app is restarted. Profile Info popup is built after logon. When multi tenant settings is Enabled/Disabled, the Profile Info is not updated until the account is changed or app is restarted. Profile Info popup is built after logon.
The list applications API might not list an imported app immediately after the import. Click Refresh to reload the application objects. The *List Applications* API might not list an imported app immediately after the import. Click *Refresh* to reload the application objects.
When using the filter box to search for items, the checkbox must be clicked twice to select an item. When using the filter box to search for items, the checkbox must be clicked twice to select an item.
Logout will only clear the token from cache and not from the browser e.g. if login is triggered after a logout, the user will still be listed in the 'Select user' dialog. Logout will only clear the token from cache and not from the browser e.g. if login is triggered after a logout, the user will still be listed in the 'Select user' dialog.
Referenced settings will NOT be imported/copied. There is no value stored in a property on the object for these settings. Example: A VPN profile has certificates as referenced properties. The certificates must be added manually after import/copy.
See [Documentation](Documentation.md) for issues regarding the documentation process. See [Documentation](Documentation.md) for issues regarding the documentation process.
## TIP ## TIP

View File

@@ -1,5 +1,41 @@
# Release Notes # Release Notes
## 3.2.1 - 2021-09-04
**New features**
- PowerShell Scripts can now be viewed and edited in the tool
- Intune Tools
- Added Intune Assignment - Simple tool to quickly gather all assignments from exported objects
- Documentation
- Added documentation support for
- Scope (Tags)
Note: This will generate one section for all Scopes in the word document
- Health Scripts (Remediation Scripts)
**Fixes**
* General
* Custom Device Configuration profiles will convert encrypted OMA URI values when the full object is loaded instead during Copy and Export.
* All file exports are now saved in UTF8
* Compare
* Fixed issue where the wrong name was specified if the compare object was missing
* Administrative Templates, Settings Catalog and Endpoint Security will always compare based on documentation.
* Encrypted OMA URI values are now supported
* Documentation
* Minor updates to support documenting all objects of a specific object type in one section instead of one section per object
* Fixed "Not Configured" value issues for empty arrays
* Fixed documentation of Microsoft 365 Apps when XML is used
* Minor updates on VPN profile documentation. EAP XML will be in XML format and removed duplicate SplitTunneling values.
Note: The EAP XML will require manual update of the column sizes in Word
## 3.2.0 - 2021-08-15 ## 3.2.0 - 2021-08-15
**New features** **New features**

View File

@@ -0,0 +1,27 @@
<Border xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
BorderBrush="Black" BorderThickness="1" Padding="5" Background="White">
<Grid HorizontalAlignment="Stretch" Name="grdModalContainer" VerticalAlignment="Stretch" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Background="{DynamicResource TitleBackgroundColor}" BorderThickness="0">
<TextBlock Name="txtEditScriptTitle" Margin="5" FontWeight="Bold" />
</Border>
<TextBox Name="txtScriptText"
Grid.Row="1"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.CanContentScroll="True"
MinWidth="250" Margin="0,5,0,0" AcceptsReturn="True" />
<WrapPanel Name="pnlButtons" Grid.Row="2" HorizontalAlignment="Right" Margin="0,5,0,0">
<Button Name="btnSaveScriptEdit" MinWidth="100" Margin="0,0,5,0" ToolTip="Save updated script">Save</Button>
<Button Name="btnCancelScriptEdit" MinWidth="100" Margin="0,0,0,0" ToolTip="Cancel any changes">Cancel</Button>
</WrapPanel>
</Grid>
</Border>

View File

@@ -0,0 +1,80 @@
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Grid.IsSharedSizeScope='True'>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="TitleColumn" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Margin="0,0,5,2" >
<Label Content="Export root" />
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="Select the root folder for exported files" />
</StackPanel>
<Grid Grid.Column='1' Grid.Row='0'>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Text="" Name="txtIntuneAssignmentsExportPath" />
<Button Grid.Column="2" Name="btnBrowseIntuneAssignmentsExportPath" Padding="5,2,5,2" Width="50" ToolTip="Select folder with exported objects">...</Button>
<Button Grid.Column="4" Name="btnGetIntuneAssignments" Padding="5,2,5,2" Content="Get Assignments" ToolTip="Get assignments from the selected exported folder" />
</Grid>
<StackPanel Grid.Row='1' Orientation="Horizontal" Margin="0,0,5,5" >
<Label Content="Filter" />
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="Filter rows" />
</StackPanel>
<Grid Grid.Column='1' Grid.Row='1'>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Text="" Name="txtIntuneAssignmentsFilter" ToolTip="Filter items" />
</Grid>
</Grid>
<DataGrid Name="dgIntuneAssignments" Margin="0,5,0,0" Grid.Row="1"
AutoGenerateColumns="False"
SelectionMode="Single"
SelectionUnit="FullRow"
CanUserAddRows="False"
ItemsSource="">
<DataGrid.Columns>
<DataGridTextColumn Header="Object Name" Binding="{Binding Name}" IsReadOnly="True" />
<DataGridTextColumn Header="Object Type" Binding="{Binding Type, Mode=OneWay}" IsReadOnly="True" />
<DataGridTextColumn Header="Included" Binding="{Binding IncludedString}" IsReadOnly="True" />
<DataGridTextColumn Header="Excluded" Binding="{Binding ExcludedString}" IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,5,0,0" >
<Button Name="btnIntuneAssignmentsCopy" Content="Copy" MinWidth="100" Margin="0,0,5,0" ToolTip="Copy the assignments as a CSV to the clipboard" />
<Button Name="btnIntuneAssignmentsSave" Content="Save" MinWidth="100" />
</StackPanel>
</Grid>

View File

@@ -1,4 +1,4 @@
<Grid Margin="0,0,0,5" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Grid Margin="0,5,0,5" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
@@ -17,7 +17,7 @@
IsReadOnly="True" IsReadOnly="True"
MinWidth="250" Margin="0" AcceptsReturn="True" /> MinWidth="250" Margin="0" AcceptsReturn="True" />
<WrapPanel Name="pnlButtons" Grid.Row="2" Grid.ColumnSpan="2" HorizontalAlignment="Right" Margin="0,10,0,0"> <WrapPanel Name="pnlButtons" Grid.Row="2" Grid.ColumnSpan="2" HorizontalAlignment="Right" Margin="0,5,0,0">
<Button Name="btnFull" MinWidth="100" Margin="0,0,5,0" ToolTip="Load full info of the object">Load full</Button> <Button Name="btnFull" MinWidth="100" Margin="0,0,5,0" ToolTip="Load full info of the object">Load full</Button>
<Button Name="btnCopy" MinWidth="100" Margin="0,0,0,0" ToolTip="Copy text to clipboard">Copy</Button> <Button Name="btnCopy" MinWidth="100" Margin="0,0,0,0" ToolTip="Copy text to clipboard">Copy</Button>
</WrapPanel> </WrapPanel>