3.1 changes
This commit is contained in:
489
Extensions/Compare.psm1
Normal file
489
Extensions/Compare.psm1
Normal file
@@ -0,0 +1,489 @@
|
||||
<#
|
||||
This moule extends the EnpointManager view with a Compare option.
|
||||
|
||||
This will compare an Intune object with an exported file.
|
||||
|
||||
The properties of the compared objects will be added to a DataGrid and the non-matching properties will be highlighted
|
||||
|
||||
Objects can be compared based on Properties or Documentatation info.
|
||||
|
||||
#>
|
||||
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'1.0.0'
|
||||
}
|
||||
|
||||
function Invoke-InitializeModule
|
||||
{
|
||||
# Make sure we add the default Output types
|
||||
Add-OutputType
|
||||
}
|
||||
|
||||
function Invoke-ShowMainWindow
|
||||
{
|
||||
$button = [System.Windows.Controls.Button]::new()
|
||||
$button.Content = "Compare"
|
||||
$button.Name = "btnCompare"
|
||||
$button.MinWidth = 100
|
||||
$button.Margin = "0,0,5,0"
|
||||
$button.IsEnabled = $false
|
||||
$button.ToolTip = "Compare object with exported file"
|
||||
$global:dgObjects.add_selectionChanged({
|
||||
Set-XamlProperty $global:dgObjects.Parent "btnCompare" "IsEnabled" (?: ($global:dgObjects.SelectedItem -eq $null) $false $true)
|
||||
})
|
||||
|
||||
$button.Add_Click({
|
||||
Show-CompareForm $global:dgObjects.SelectedItem
|
||||
})
|
||||
|
||||
$global:spSubMenu.RegisterName($button.Name, $button)
|
||||
|
||||
$global:spSubMenu.Children.Insert(0, $button)
|
||||
}
|
||||
|
||||
function Show-CompareForm
|
||||
{
|
||||
param($objInfo)
|
||||
|
||||
$script:cmpForm = Get-XamlObject ($global:AppRootFolder + "\Xaml\CompareForm.xaml") -AddVariables
|
||||
if(-not $script:cmpForm) { return }
|
||||
|
||||
$script:copareSource = $objInfo
|
||||
|
||||
$global:cbCompareType.ItemsSource = ("[ { Name: `"Property`",Value: `"property`" }, { Name: `"Documentation`",Value: `"doc`" }]" | ConvertFrom-Json)
|
||||
$global:cbCompareType.SelectedValue = (Get-Setting "Compare" "Type" "property")
|
||||
|
||||
$global:txtIntuneObject.Text = (Get-GraphObjectName $objInfo.Object $objInfo.ObjectType)
|
||||
|
||||
Add-XamlEvent $script:cmpForm "btnClose" "add_click" {
|
||||
$script:cmpForm = $null
|
||||
Show-ModalObject
|
||||
}
|
||||
|
||||
Add-XamlEvent $script:cmpForm "btnStartCompare" "add_click" {
|
||||
Write-Status "Compare objects"
|
||||
Save-Setting "Compare" "Type" $global:cbCompareType.SelectedValue
|
||||
$script:currentObjName = ""
|
||||
Invoke-CompareObjects
|
||||
Write-Status ""
|
||||
}
|
||||
|
||||
Add-XamlEvent $script:cmpForm "btnCompareSave" "add_click" {
|
||||
|
||||
if(($global:dgCompareInfo.ItemsSource | measure).Count -eq 0) { return }
|
||||
|
||||
$sf = [System.Windows.Forms.SaveFileDialog]::new()
|
||||
$sf.FileName = $script:currentObjName
|
||||
$sf.initialDirectory = (?: ($global:lastCompareFile -eq $null) (Get-Setting "" "LastUsedRoot") ([IO.FileInfo]$global:lastCompareFile).DirectoryName)
|
||||
$sf.DefaultExt = "*.csv"
|
||||
$sf.Filter = "CSV (*.csv)|*.csv|All files (*.*)| *.*"
|
||||
if($sf.ShowDialog() -eq "OK")
|
||||
{
|
||||
$csvInfo = $global:dgCompareInfo.ItemsSource | Select PropertyName,Object1Value,Object2Value,Category,SubCategory,Match | ConvertTo-Csv -NoTypeInformation
|
||||
$csvInfo | Out-File $sf.FileName -Force -Encoding UTF8
|
||||
}
|
||||
}
|
||||
|
||||
Add-XamlEvent $script:cmpForm "btnCompareCopy" "add_click" {
|
||||
|
||||
$global:dgCompareInfo.ItemsSource | Select PropertyName,Object1Value,Object2Value,Category,SubCategory,Match | ConvertTo-Csv -NoTypeInformation | Set-Clipboard
|
||||
}
|
||||
|
||||
Add-XamlEvent $script:cmpForm "browseCompareObject" "add_click" {
|
||||
$of = [System.Windows.Forms.OpenFileDialog]::new()
|
||||
$of.Multiselect = $false
|
||||
$of.Filter = "Json files (*.json)|*.json"
|
||||
$of.InitialDirectory = (?: ($global:lastCompareFile -eq $null) (Get-Setting "" "LastUsedRoot") ([IO.FileInfo]$global:lastCompareFile).DirectoryName)
|
||||
|
||||
if($of.ShowDialog())
|
||||
{
|
||||
Set-XamlProperty $script:cmpForm "txtCompareFile" "Text" $of.FileName
|
||||
$global:lastCompareFile = $of.FileName
|
||||
}
|
||||
}
|
||||
|
||||
#Add-XamlEvent $script:cmpForm "dgCompareInfo" "add_loaded" {
|
||||
|
||||
#}
|
||||
|
||||
Show-ModalForm "Compare Intune Objects" $script:cmpForm -HideButtons
|
||||
}
|
||||
|
||||
function Invoke-CompareObjects
|
||||
{
|
||||
if(-not $script:copareSource) { return }
|
||||
|
||||
if(-not $global:txtCompareFile.Text)
|
||||
{
|
||||
[System.Windows.MessageBox]::Show("No file selected", "Comapre", "OK", "Error")
|
||||
return
|
||||
}
|
||||
elseif([IO.File]::Exists($global:txtCompareFile.Text) -eq $false)
|
||||
{
|
||||
[System.Windows.MessageBox]::Show("File '$($global:txtCompareFile.Text)' not found", "Comapre", "OK", "Error")
|
||||
return
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if($script:copareSource.ObjectType.LoadObject)
|
||||
{
|
||||
$compareObj = & $script:copareSource.ObjectType.LoadObject $global:txtCompareFile.Text
|
||||
}
|
||||
else
|
||||
{
|
||||
$compareObj = Get-Content $global:txtCompareFile.Text | ConvertFrom-Json
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
[System.Windows.MessageBox]::Show("Failed to convert json file '$($global:txtCompareFile.Text)'", "Comapre", "OK", "Error")
|
||||
return
|
||||
}
|
||||
|
||||
$obj = Get-GraphObject $script:copareSource.Object $script:copareSource.ObjectType
|
||||
|
||||
$script:currentObjName = Get-GraphObjectName $script:copareSource.Object $script:copareSource.ObjectType
|
||||
|
||||
if($obj.Object."@OData.Type" -ne $compareObj."@OData.Type")
|
||||
{
|
||||
if(([System.Windows.MessageBox]::Show("The object types does not match.`n`nDo you to compare the objects?", "Comapre", "YesNo", "Warning")) -eq "No")
|
||||
{
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
$script:compareProperties = @()
|
||||
|
||||
if($global:cbCompareType.SelectedValue -eq "property")
|
||||
{
|
||||
Compare-ObjectsBasedonProperty $obj.Object $compareObj $obj.ObjectType
|
||||
}
|
||||
elseif($global:cbCompareType.SelectedValue -eq "doc")
|
||||
{
|
||||
Compare-ObjectsBasedonDocumentation $obj $compareObj
|
||||
}
|
||||
$global:dgCompareInfo.ItemsSource = $script:compareProperties
|
||||
}
|
||||
function Set-ColumnVisibility
|
||||
{
|
||||
param($show)
|
||||
|
||||
$colTmp = $global:dgCompareInfo.Columns | Where { $_.Binding.Path.Path -eq "Category" }
|
||||
if($colTmp)
|
||||
{
|
||||
$colTmp.Visibility = (?: ($show -eq $true) "Visible" "Collapsed")
|
||||
}
|
||||
|
||||
$colTmp = $global:dgCompareInfo.Columns | Where { $_.Binding.Path.Path -eq "SubCategory" }
|
||||
if($colTmp)
|
||||
{
|
||||
$colTmp.Visibility = (?: ($show -eq $true) "Visible" "Collapsed")
|
||||
}
|
||||
}
|
||||
|
||||
function Add-CompareProperty
|
||||
{
|
||||
param($name, $value1, $value2, $category, $subCategory, $match = $null)
|
||||
|
||||
$value1 = if($value1 -eq $null) { "" } else { $value1.ToString().Trim("`"") }
|
||||
$value2 = if($value2 -eq $null) { "" } else { $value2.ToString().Trim("`"") }
|
||||
if( ($value1 -eq $value2) -eq $false)
|
||||
{
|
||||
$dummy = 1
|
||||
}
|
||||
|
||||
$script:compareProperties += [PSCustomObject]@{
|
||||
PropertyName = $name
|
||||
Object1Value = $value1 #if($value1 -ne $null) { $value1.ToString().Trim("`"") } else { "" }
|
||||
Object2Value = $value2 #if($value2 -ne $null) { $value2.ToString().Trim("`"") } else { "" }
|
||||
Category = $category
|
||||
SubCategory = $subCategory
|
||||
Match = ?? $match ($value1 -eq $value2)
|
||||
}
|
||||
}
|
||||
|
||||
function Compare-ObjectsBasedonProperty
|
||||
{
|
||||
param($obj1, $obj2, $objectType)
|
||||
|
||||
Write-Status "Compare properties"
|
||||
|
||||
Set-ColumnVisibility $false
|
||||
|
||||
$coreProps = @((?? $objectType.NameProperty "displayName"), "Description", "Id", "createdDateTime", "lastModifiedDateTime", "version")
|
||||
$postProps = @("Advertisements")
|
||||
|
||||
foreach ($propName in $coreProps)
|
||||
{
|
||||
if(-not ($obj1.PSObject.Properties | Where Name -eq $propName))
|
||||
{
|
||||
continue
|
||||
}
|
||||
$val1 = ($obj1.$propName | ConvertTo-Json -Depth 10)
|
||||
$val2 = ($obj2.$propName | ConvertTo-Json -Depth 10)
|
||||
Add-CompareProperty $propName $val1 $val2
|
||||
}
|
||||
|
||||
$addedProps = @()
|
||||
foreach ($propName in ($obj1.PSObject.Properties | Select Name).Name)
|
||||
{
|
||||
if($propName -in $coreProps) { continue }
|
||||
if($propName -in $postProps) { continue }
|
||||
|
||||
if($propName -like "*@OData*" -or $propName -like "#microsoft.graph*") { continue }
|
||||
|
||||
$addedProps += $propName
|
||||
$val1 = ($obj1.$propName | ConvertTo-Json -Depth 10)
|
||||
$val2 = ($obj2.$propName | ConvertTo-Json -Depth 10)
|
||||
Add-CompareProperty $propName $val1 $val2
|
||||
}
|
||||
|
||||
foreach ($propName in ($obj2.PSObject.Properties | Select Name).Name)
|
||||
{
|
||||
if($propName -in $coreProps) { continue }
|
||||
if($propName -in $postProps) { continue }
|
||||
if($propName -in $addedProps) { continue }
|
||||
|
||||
if($propName -like "*@OData*" -or $propName -like "#microsoft.graph*") { continue }
|
||||
|
||||
$val1 = ($obj1.$propName | ConvertTo-Json -Depth 10)
|
||||
$val2 = ($obj2.$propName | ConvertTo-Json -Depth 10)
|
||||
Add-CompareProperty $propName $val1 $val2
|
||||
}
|
||||
|
||||
foreach ($propName in $postProps)
|
||||
{
|
||||
if(-not ($obj1.PSObject.Properties | Where Name -eq $propName))
|
||||
{
|
||||
continue
|
||||
}
|
||||
$val1 = ($obj1.$propName | ConvertTo-Json -Depth 10)
|
||||
$val2 = ($obj2.$propName | ConvertTo-Json -Depth 10)
|
||||
Add-CompareProperty $propName $val1 $val2
|
||||
}
|
||||
}
|
||||
|
||||
function Compare-ObjectsBasedonDocumentation
|
||||
{
|
||||
param($obj1, $obj2)
|
||||
|
||||
Set-ColumnVisibility $true
|
||||
|
||||
# ToDo: set this based on configuration value
|
||||
$script:assignmentOutput = "simpleFullCompare"
|
||||
|
||||
$docObj1 = Invoke-ObjectDocumentation $obj1
|
||||
|
||||
$obj2 | Add-Member Noteproperty -Name "@CompareObject" -Value $true -Force
|
||||
|
||||
$docObj2 = Invoke-ObjectDocumentation ([PSCustomObject]@{
|
||||
Object = $obj2
|
||||
ObjectType = $obj1.ObjectType
|
||||
})
|
||||
|
||||
$settingsValue = ?? $obj1.ObjectType.CompareValue "Value"
|
||||
|
||||
foreach ($prop in $docObj1.BasicInfo)
|
||||
{
|
||||
$val1 = $prop.Value
|
||||
$prop2 = $docObj2.BasicInfo | Where Name -eq $prop.Name
|
||||
$val2 = $prop2.Value
|
||||
Add-CompareProperty $prop.Name $val1 $val2 $prop.Category
|
||||
}
|
||||
|
||||
$addedProperties = @()
|
||||
|
||||
if($docObj1.InputType -eq "Settings")
|
||||
{
|
||||
foreach ($prop in $docObj1.Settings)
|
||||
{
|
||||
if(($prop.SettingId + $prop.ParentSettingId) -in $addedProperties) { continue }
|
||||
|
||||
$addedProperties += ($prop.SettingId + $prop.ParentSettingId)
|
||||
$val1 = $prop.Value
|
||||
$prop2 = $docObj2.Settings | Where { $_.SettingId -eq $prop.SettingId -and $_.ParentSettingId -eq $prop.ParentSettingId }
|
||||
$val2 = $prop2.Value
|
||||
Add-CompareProperty $prop.Name $val1 $val2 $prop.Category
|
||||
|
||||
# ToDo: fix lazy copy/past coding
|
||||
$children1 = $docObj1.Settings | Where ParentId -eq $prop.Id
|
||||
$children2 = $docObj2.Settings | Where ParentId -eq $prop2.Id
|
||||
|
||||
# Add children defined on Object 1 property
|
||||
foreach ($childProp in $children1)
|
||||
{
|
||||
if(($childProp.SettingId + $childProp.ParentSettingId) -in $addedProperties) { continue }
|
||||
|
||||
$addedProperties += ($childProp.SettingId + $childProp.ParentSettingId)
|
||||
$val1 = $childProp.Value
|
||||
$prop2 = $docObj2.Settings | Where { $_.SettingId -eq $childProp.SettingId -and $_.ParentSettingId -eq $childProp.ParentSettingId }
|
||||
$val2 = $prop2.Value
|
||||
Add-CompareProperty $childProp.Name $val1 $val2 $prop.Category
|
||||
}
|
||||
|
||||
# Add children defined only on Object 2 property e.g. Baseline Firewall profile was disable AFTER export.
|
||||
# This is to make sure all children are added under its parent and not last in the table
|
||||
foreach ($childProp in $children2)
|
||||
{
|
||||
if(($childProp.SettingId + $childProp.ParentSettingId) -in $addedProperties) { continue }
|
||||
|
||||
$addedProperties += ($childProp.SettingId + $childProp.ParentSettingId)
|
||||
$val2 = $childProp.Value
|
||||
$prop2 = $docObj1.Settings | Where { $_.SettingId -eq $childProp.SettingId -and $_.ParentSettingId -eq $childProp.ParentSettingId }
|
||||
$val1 = $prop2.Value
|
||||
Add-CompareProperty $childProp.Name $val1 $val2 $prop.Category
|
||||
}
|
||||
}
|
||||
|
||||
# These objects are defined only on Object 2. They will be last in the table
|
||||
foreach ($prop in $docObj2.Settings)
|
||||
{
|
||||
if(($prop.SettingId + $prop.ParentSettingId) -in $addedProperties) { continue }
|
||||
|
||||
$addedProperties += ($prop.SettingId + $prop.ParentSettingId)
|
||||
$val2 = $prop.Value
|
||||
$prop2 = $docObj1.Settings | Where { $_.SettingId -eq $prop.SettingId -and $_.ParentSettingId -eq $prop.ParentSettingId }
|
||||
$val1 = $prop2.Value
|
||||
Add-CompareProperty $prop.Name $val1 $val2 $prop.Category
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($prop in $docObj1.Settings)
|
||||
{
|
||||
if(($prop.EntityKey) -in $addedProperties) { continue }
|
||||
|
||||
$addedProperties += $prop.EntityKey
|
||||
$val1 = $prop.$settingsValue
|
||||
$prop2 = $docObj2.Settings | Where { $_.EntityKey -eq $prop.EntityKey }
|
||||
$val2 = $prop2.$settingsValue
|
||||
Add-CompareProperty $prop.Name $val1 $val2 $prop.Category
|
||||
}
|
||||
|
||||
# These objects are defined only on Object 2. They will be last in the table
|
||||
foreach ($prop in $docObj2.Settings)
|
||||
{
|
||||
if(($prop.EntityKey) -in $addedProperties) { continue }
|
||||
|
||||
$addedProperties += $prop.EntityKey
|
||||
$val2 = $prop.$settingsValue
|
||||
$prop2 = $docObj1.Settings | Where { $_.EntityKey -eq $prop.EntityKey }
|
||||
$val1 = $prop2.$settingsValue
|
||||
Add-CompareProperty $prop.Name $val1 $val2 $prop.Category
|
||||
}
|
||||
}
|
||||
|
||||
$applicabilityRulesAdded = @()
|
||||
#$properties = @("Rule","Property","Value")
|
||||
foreach($applicabilityRule in $docObj1.ApplicabilityRules)
|
||||
{
|
||||
$applicabilityRule2 = $docObj2.ApplicabilityRules | Where { $_.Id -eq $applicabilityRule.Id }
|
||||
$applicabilityRulesAdded += $applicabilityRule.Id
|
||||
$val1 = ($applicabilityRule.Rule + [environment]::NewLine + $applicabilityRule.Value)
|
||||
$val2 = ($applicabilityRule2.Rule + [environment]::NewLine + $applicabilityRule2.Value)
|
||||
|
||||
Add-CompareProperty $applicabilityRule.Property $val1 $val2 $applicabilityRule.Category
|
||||
}
|
||||
|
||||
foreach($applicabilityRule in $docObj2.ApplicabilityRules)
|
||||
{
|
||||
if(($applicabilityRule.Id) -in $applicabilityRulesAdded) { continue }
|
||||
$applicabilityRule2 = $docObj1.ApplicabilityRules | Where { $_.Id -eq $applicabilityRule.Id }
|
||||
$script:applicabilityRulesAdded += $applicabilityRule.Id
|
||||
$val2 = ($applicabilityRule.Rule + [environment]::NewLine + $applicabilityRule.Value)
|
||||
$val1 = ($applicabilityRule2.Rule + [environment]::NewLine + $applicabilityRule2.Value)
|
||||
|
||||
Add-CompareProperty $applicabilityRule.Property $val1 $val2 $applicabilityRule.Category
|
||||
}
|
||||
|
||||
$script:assignmentStr = Get-LanguageString "TableHeaders.assignment"
|
||||
$script:groupsAdded = @()
|
||||
|
||||
$assignmentType = $null
|
||||
$curType = $null
|
||||
|
||||
foreach ($assignment in $docObj1.Assignments)
|
||||
{
|
||||
#if(-not $assignmentType)
|
||||
#{
|
||||
# $assignmentType = (?: ($assignment.RawIntent -eq $null) "generic" "app")
|
||||
#}
|
||||
|
||||
$prevType = $null
|
||||
|
||||
if($curType -ne $assignment.Category)
|
||||
{
|
||||
if($curType) { $prevType = $curType}
|
||||
$curType = $assignment.Category
|
||||
}
|
||||
|
||||
if($prevType)
|
||||
{
|
||||
# Add any additional missing intent in the same intent group
|
||||
foreach($tmpAssignment in $docObj2.Assignments | Where { $_.Category -eq $prevType })
|
||||
{
|
||||
Add-AssignmentInfo $docObj2 $docObj1 $tmpAssignment -ReversedValue
|
||||
}
|
||||
}
|
||||
Add-AssignmentInfo $docObj1 $docObj2 $assignment
|
||||
}
|
||||
|
||||
# Add any missing assignments from Object 2
|
||||
foreach ($assignment in $docObj2.Assignments)
|
||||
{
|
||||
Add-AssignmentInfo $docObj2 $docObj1 $assignment -ReversedValue
|
||||
}
|
||||
}
|
||||
|
||||
function Add-AssignmentInfo
|
||||
{
|
||||
param($srcObj, $cmpObj, $assignment, [switch]$ReversedValue)
|
||||
|
||||
if(($assignment.Group + $assignment.GroupMode + $assignment.RawIntent) -in $script:groupsAdded) { continue }
|
||||
|
||||
$assignment2 = $cmpObj.Assignments | Where { $_.GroupMode -eq $assignment.GroupMode -and $_.Group -eq $assignment.Group -and $_.RawIntent -eq $assignment.RawIntent }
|
||||
$script:groupsAdded += ($assignment.Group + $assignment.GroupMode + $assignment.RawIntent)
|
||||
|
||||
$match = $null
|
||||
|
||||
# To only show the group name
|
||||
if($script:assignmentOutput -eq "simple")
|
||||
{
|
||||
$val1 = $assignment.Group
|
||||
$val2 = $assignment2.Group
|
||||
}
|
||||
else
|
||||
{
|
||||
# Show full Assignment info
|
||||
# -Property @("Group","*") will generete error but will put the Group first and the rest of the properties after it. ErrorAction SilentlyContinue will ignore the error
|
||||
# Should be another way of doing this without generating an error.
|
||||
$val1 = $assignment | Select -Property @("Group","*") -ExcludeProperty @("RawJsonValue","RawIntent","GroupMode","Category") -ErrorAction SilentlyContinue | ConvertTo-Json -Compress #$assignment.Group
|
||||
$val2 = $assignment2 | Select -Property @("Group","*") -ExcludeProperty @("RawJsonValue","RawIntent","GroupMode","Category") -ErrorAction SilentlyContinue | ConvertTo-Json -Compress #$assignment2.Group
|
||||
|
||||
if($script:assignmentOutput -eq "simpleFullCompare")
|
||||
{
|
||||
# Full compare but show only the Group name. This could cause red for not matching even though the same group is used e.g. Filter is changed
|
||||
$match = ($val1 -eq $val2)
|
||||
$val1 = $assignment.Group
|
||||
$val2 = $assignment2.Group
|
||||
}
|
||||
}
|
||||
|
||||
if($ReversedValue -eq $true)
|
||||
{
|
||||
$tmpVal = $val1
|
||||
$val1 = $val2
|
||||
$val2 = $tmpVal
|
||||
}
|
||||
|
||||
if($assignment.RawIntent)
|
||||
{
|
||||
Add-CompareProperty $assignment.Category $val1 $val2 -Category $assignment.GroupMode -match $match
|
||||
}
|
||||
else
|
||||
{
|
||||
Add-CompareProperty $assignmentStr $val1 $val2 -Category $assignment.GroupMode -match $match
|
||||
}
|
||||
}
|
||||
3653
Extensions/Documentation.psm1
Normal file
3653
Extensions/Documentation.psm1
Normal file
File diff suppressed because it is too large
Load Diff
2043
Extensions/DocumentationCustom.psm1
Normal file
2043
Extensions/DocumentationCustom.psm1
Normal file
File diff suppressed because it is too large
Load Diff
619
Extensions/DocumentationWord.psm1
Normal file
619
Extensions/DocumentationWord.psm1
Normal file
@@ -0,0 +1,619 @@
|
||||
# Documentation Output Provider for Word
|
||||
|
||||
#https://docs.microsoft.com/en-us/office/vba/api/overview/word
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'1.0.0'
|
||||
}
|
||||
|
||||
function Invoke-InitializeModule
|
||||
{
|
||||
if(!("Microsoft.Office.Interop.Word.Application" -as [Type]))
|
||||
{
|
||||
try
|
||||
{
|
||||
Add-Type -AssemblyName Microsoft.Office.Interop.Word
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-LogError "Failed to add Word Interop type. Cannot create word documents. Verify that Word is installed properly." $_.Exception
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Add-OutputType ([PSCustomObject]@{
|
||||
Name="Word"
|
||||
Value="word"
|
||||
OutputOptions = (Add-WordOptionsControl)
|
||||
Activate = { Invoke-WordActivate @args }
|
||||
PreProcess = { Invoke-WordPreProcessItems @args }
|
||||
NewObjectType = { Invoke-WordNewObjectType @args }
|
||||
Process = { Invoke-WordProcessItem @args }
|
||||
PostProcess = { Invoke-WordPostProcessItems @args }
|
||||
})
|
||||
|
||||
$script:columnHeaders = @{
|
||||
Name="Inputs.displayNameLabel"
|
||||
Value="TableHeaders.value"
|
||||
Description="TableHeaders.description"
|
||||
GroupMode="SettingDetails.modeTableHeader" #assignmentTypeSelectionLabel?
|
||||
Group="TableHeaders.assignedGroups"
|
||||
Groups="TableHeaders.groups"
|
||||
useDeviceContext="SettingDetails.installContextLabel"
|
||||
uninstallOnDeviceRemoval="SettingDetails.UninstallOnRemoval"
|
||||
isRemovable="SettingDetails.installAsRemovable"
|
||||
vpnConfigurationId="PolicyType.vpn"
|
||||
Action="SettingDetails.actionColumnName"
|
||||
Schedule="ScheduledAction.List.schedule"
|
||||
MessageTemplate="ScheduledAction.Notification.messageTemplate"
|
||||
EmailCC="ScheduledAction.Notification.additionalRecipients"
|
||||
Filter="AssignmentFilters.assignmentFilterColumnHeader"
|
||||
Rule="ApplicabilityRules.GridLabel.Rule"
|
||||
ValueWithLabel="TableHeaders.value"
|
||||
Status="TableHeaders.status"
|
||||
CombinedValueWithLabel="TableHeaders.value"
|
||||
CombinedValue="TableHeaders.value"
|
||||
}
|
||||
}
|
||||
|
||||
function Add-WordOptionsControl
|
||||
{
|
||||
$script:wordForm = Get-XamlObject ($global:AppRootFolder + "\Xaml\DocumentationWordOptions.xaml") -AddVariables
|
||||
|
||||
$global:cbWordDocumentationProperties.ItemsSource = ("[ { Name: `"Simple (Name and Value)`",Value: `"simple`" }, { Name: `"Extended`",Value: `"extended`" }, { Name: `"Custom`",Value: `"custom`" }]" | ConvertFrom-Json)
|
||||
$global:cbWordDocumentationProperties.SelectedValue = (Get-Setting "Documentation" "WordExportProperties" "simple")
|
||||
$global:txtWordCustomProperties.Text = (Get-Setting "Documentation" "WordCustomDisplayProperties" "Name,Value,Category,SubCategory")
|
||||
|
||||
$global:spWordCustomProperties.Visibility = (?: ($global:cbWordDocumentationProperties.SelectedValue -ne "custom") "Collapsed" "Visible")
|
||||
$global:txtWordCustomProperties.Visibility = (?: ($global:cbWordDocumentationProperties.SelectedValue -ne "custom") "Collapsed" "Visible")
|
||||
|
||||
$global:txtWordDocumentTemplate.Text = Get-Setting "Documentation" "WordDocumentTemplate" ""
|
||||
$global:txtWordDocumentName.Text = (Get-Setting "Documentation" "WordDocumentName" "%MyDocuments%\%Organization%-%Date%.docx")
|
||||
|
||||
$global:chkWordAddCategories.IsChecked = ((Get-Setting "Documentation" "WordAddCategories" "true") -ne "false")
|
||||
$global:chkWordAddSubCategories.IsChecked = ((Get-Setting "Documentation" "WordAddSubCategories" "true") -ne "false")
|
||||
|
||||
$global:txtWordHeader1Style.Text = Get-Setting "Documentation" "WordHeader1Style" "Heading 1"
|
||||
$global:txtWordHeader2Style.Text = Get-Setting "Documentation" "WordHeader2Style" "Heading 2"
|
||||
$global:txtWordTableStyle.Text = Get-Setting "Documentation" "WordTableStyle" "Grid table 4 - Accent 3"
|
||||
$global:txtWordTableHeaderStyle.Text = Get-Setting "Documentation" "WordTableHeaderStyle" ""
|
||||
$global:txtWordCategoryHeaderStyle.Text = Get-Setting "Documentation" "WordCategoryHeaderStyle" ""
|
||||
$global:txtWordSubCategoryHeaderStyle.Text = Get-Setting "Documentation" "WordSubCategoryHeaderStyle" ""
|
||||
|
||||
$global:chkWordOpenDocument.IsChecked = ((Get-Setting "Documentation" "WordOpenDocument" "true") -ne "false")
|
||||
|
||||
Add-XamlEvent $script:wordForm "browseWordDocumentTemplate" "add_click" {
|
||||
$of = [System.Windows.Forms.OpenFileDialog]::new()
|
||||
$of.Multiselect = $false
|
||||
$of.Filter = "Word Templates (*.dotx)|*.dotx"
|
||||
if($of.ShowDialog())
|
||||
{
|
||||
Set-XamlProperty $script:wordForm "txtWordDocumentTemplate" "Text" $of.FileName
|
||||
Save-Setting "Documentation" "WordDocumentTemplate" $of.FileName
|
||||
}
|
||||
}
|
||||
|
||||
Add-XamlEvent $script:wordForm "cbWordDocumentationProperties" "add_selectionChanged" {
|
||||
$global:spWordCustomProperties.Visibility = (?: ($this.SelectedValue -ne "custom") "Collapsed" "Visible")
|
||||
$global:txtWordCustomProperties.Visibility = (?: ($this.SelectedValue -ne "custom") "Collapsed" "Visible")
|
||||
}
|
||||
|
||||
$script:wordForm
|
||||
}
|
||||
function Invoke-WordActivate
|
||||
{
|
||||
#$global:chkWordAddCompanyName.IsChecked = (Get-SettingValue "AddCompanyName")
|
||||
}
|
||||
|
||||
function Invoke-WordPreProcessItems
|
||||
{
|
||||
Save-Setting "Documentation" "WordExportProperties" $global:cbWordDocumentationProperties.SelectedValue
|
||||
Save-Setting "Documentation" "WordCustomDisplayProperties" $global:txtWordCustomProperties.Text
|
||||
Save-Setting "Documentation" "WordDocumentTemplate" $global:txtWordDocumentTemplate.Text
|
||||
|
||||
Save-Setting "Documentation" "WordAddCategories" $global:chkWordAddCategories.IsChecked
|
||||
Save-Setting "Documentation" "WordAddSubCategories" $global:chkWordAddSubCategories.IsChecked
|
||||
Save-Setting "Documentation" "WordOpenDocument" $global:chkWordOpenDocument.IsChecked
|
||||
|
||||
Save-Setting "Documentation" "WordHeader1Style" $global:txtWordHeader1Style.Text
|
||||
Save-Setting "Documentation" "WordHeader2Style" $global:txtWordHeader2Style.Text
|
||||
Save-Setting "Documentation" "WordTableStyle" $global:txtWordTableStyle.Text
|
||||
Save-Setting "Documentation" "WordTableHeaderStyle" $global:txtWordTableHeaderStyle.Text
|
||||
Save-Setting "Documentation" "WordCategoryHeaderStyle" $global:txtWordCategoryHeaderStyle.Text
|
||||
Save-Setting "Documentation" "WordSubCategoryHeaderStyle" $global:txtWordSubCategoryHeaderStyle.Text
|
||||
|
||||
try
|
||||
{
|
||||
$script:wordApp = New-Object -ComObject Word.Application
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-LogError "Failed to create Word App object. Word documentation aborted..." $_.Exception
|
||||
return $false
|
||||
}
|
||||
|
||||
|
||||
#$wordApp.Visible = $true
|
||||
|
||||
if($global:txtWordDocumentTemplate.Text)
|
||||
{
|
||||
try
|
||||
{
|
||||
$script:doc = $wordApp.Documents.Add($global:txtWordDocumentTemplate.Text)
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-LogError "Failed to create document based on tmeplate: $($global:txtWordDocumentTemplate.Text)" $_.Exception
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$script:doc = $wordApp.Documents.Add()
|
||||
}
|
||||
|
||||
#Get BuiltIn properties
|
||||
$script:builtInProps = @()
|
||||
$script:doc.BuiltInDocumentProperties | foreach-object {
|
||||
|
||||
$name = [System.__ComObject].invokemember("name",[System.Reflection.BindingFlags]::GetProperty,$null,$_,$null)
|
||||
try
|
||||
{
|
||||
$value = [System.__ComObject].invokemember("value",[System.Reflection.BindingFlags]::GetProperty,$null,$_,$null)
|
||||
}
|
||||
catch{}
|
||||
|
||||
if($name)
|
||||
{
|
||||
$script:builtInProps += [PSCustomObject]@{
|
||||
Name = $name
|
||||
Value = $value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Get Custom properties
|
||||
$script:customProps = @()
|
||||
$script:doc.CustomDocumentProperties | foreach-object {
|
||||
|
||||
$name = [System.__ComObject].invokemember("name",[System.Reflection.BindingFlags]::GetProperty,$null,$_,$null)
|
||||
try
|
||||
{
|
||||
$value = [System.__ComObject].invokemember("value",[System.Reflection.BindingFlags]::GetProperty,$null,$_,$null)
|
||||
}
|
||||
catch{}
|
||||
|
||||
if($name)
|
||||
{
|
||||
$script:customProps += [PSCustomObject]@{
|
||||
Name = $name
|
||||
Value = $value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$script:wordStyles = @()
|
||||
$script:doc.Styles | foreach {
|
||||
$script:wordStyles += [PSCUstomObject]@{
|
||||
Name=$_.NameLocal
|
||||
Type=$_.Type
|
||||
Style=$_
|
||||
}
|
||||
}
|
||||
|
||||
$script:builtinStyles = [Enum]::GetNames([Microsoft.Office.Interop.Word.wdBuiltinStyle])
|
||||
}
|
||||
|
||||
function Invoke-WordPostProcessItems
|
||||
{
|
||||
$userName = $global:me.displayName
|
||||
if($global:me.givenName -and $global:me.surname)
|
||||
{
|
||||
$userName = ($global:me.givenName + " " + $global:me.surname)
|
||||
}
|
||||
|
||||
#Add properties - ToDo: This is static...
|
||||
Set-WordDocBuiltInProperty "wdPropertyTitle" "Intune documentation"
|
||||
Set-WordDocBuiltInProperty "wdPropertySubject" "Intune documentation"
|
||||
Set-WordDocBuiltInProperty "wdPropertyAuthor" $userName
|
||||
Set-WordDocBuiltInProperty "wdPropertyCompany" $global:Organization.displayName
|
||||
Set-WordDocBuiltInProperty "wdPropertyKeywords" "Intune,Endpoint Manager,MEM"
|
||||
|
||||
#update fields, ToC etc.
|
||||
$script:doc.Fields | ForEach-Object -Process { $_.Update() | Out-Null }
|
||||
$script:doc.TablesOfContents | ForEach-Object -Process { $_.Update() | Out-Null }
|
||||
$script:doc.TablesOfFigures | ForEach-Object -Process { $_.Update() | Out-Null }
|
||||
$script:doc.TablesOfFigures | ForEach-Object -Process { $_.Update() | Out-Null }
|
||||
|
||||
$fileName = $global:txtWordDocumentName.Text
|
||||
|
||||
[Environment]::SetEnvironmentVariable("Date",(Get-Date).ToString("yyyy-MM-dd"),[System.EnvironmentVariableTarget]::Process)
|
||||
[Environment]::SetEnvironmentVariable("Organization",$global:Organization.displayName,[System.EnvironmentVariableTarget]::Process)
|
||||
|
||||
if(-not $fileName)
|
||||
{
|
||||
$fileName = "%MyDocuments%\%Organization%-%Date%.docx"
|
||||
}
|
||||
$fileName = [Environment]::ExpandEnvironmentVariables($fileName)
|
||||
|
||||
foreach($tmpFolder in ([System.Enum]::GetNames([System.Environment+SpecialFolder])))
|
||||
{
|
||||
$fileName = $fileName -replace "%$($tmpFolder)%",([Environment]::GetFolderPath($tmpFolder))
|
||||
}
|
||||
|
||||
[Environment]::SetEnvironmentVariable("Date",$null,[System.EnvironmentVariableTarget]::Process)
|
||||
[Environment]::SetEnvironmentVariable("Organization",$null,[System.EnvironmentVariableTarget]::Process)
|
||||
$fileName
|
||||
|
||||
$format = [Microsoft.Office.Interop.Word.WdSaveFormat]::wdFormatDocumentDefault
|
||||
|
||||
try
|
||||
{
|
||||
$script:doc.SaveAs2([ref]$fileName,[ref]$format)
|
||||
Write-Log "Document $fileName saved successfully"
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-LogError "Failed to save file $fileName" $_.Excption
|
||||
}
|
||||
|
||||
if($global:chkWordOpenDocument.IsChecked -eq $true)
|
||||
{
|
||||
$wordApp.Visible = $true
|
||||
$wordApp.WindowState = [Microsoft.Office.Interop.Word.WdWindowState]::wdWindowStateMaximize
|
||||
$wordApp.Activate()
|
||||
[Console.Window]::SetForegroundWindow($wordApp.ActiveWindow.Hwnd) | Out-Null
|
||||
}
|
||||
else
|
||||
{
|
||||
$script:doc.Close([Microsoft.Office.Interop.Word.WdSaveOptions]::wdDoNotSaveChanges)
|
||||
$wordApp.Quit()
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-WordNewObjectType
|
||||
{
|
||||
param($obj, $documentedObj)
|
||||
|
||||
$objectTypeString = Get-ObjectTypeString $obj.Object $obj.ObjectType
|
||||
|
||||
Add-DocText (?? $objectTypeString $obj.ObjectType.Title) $global:txtWordHeader1Style.Text
|
||||
}
|
||||
|
||||
function Invoke-WordProcessItem
|
||||
{
|
||||
param($obj, $objectType, $documentedObj)
|
||||
|
||||
if(!$documentedObj -or !$obj -or !$objectType) { return }
|
||||
|
||||
$objName = Get-GraphObjectName $obj $objectType
|
||||
|
||||
Add-DocText $objName $global:txtWordHeader2Style.Text
|
||||
|
||||
$script:doc.Application.Selection.TypeParagraph()
|
||||
|
||||
try
|
||||
{
|
||||
#TableHeaders.value
|
||||
#Inputs.displayNameLabel
|
||||
|
||||
foreach($tableType in @("BasicInfo","FilteredSettings"))
|
||||
{
|
||||
if($tableType -eq "BasicInfo")
|
||||
{
|
||||
$properties = @("Name","Value")
|
||||
}
|
||||
elseif($global:cbWordDocumentationProperties.SelectedValue -eq 'extended' -and $documentedObj.DisplayProperties)
|
||||
{
|
||||
$properties = @("Name","Value","Description")
|
||||
}
|
||||
elseif($global:cbWordDocumentationProperties.SelectedValue -eq 'custom' -and $global:txtWordCustomProperties.Text)
|
||||
{
|
||||
$properties = @()
|
||||
|
||||
foreach($prop in $global:txtWordCustomProperties.Text.Split(","))
|
||||
{
|
||||
# This will add language support for custom colument (or replacing existing header)
|
||||
$propInfo = $prop.Split('=')
|
||||
if(($propInfo | measure).Count -gt 1)
|
||||
{
|
||||
$properties += $propInfo[0]
|
||||
Set-WordColumnHeaderLanguageId $propInfo[0] $propInfo[1]
|
||||
}
|
||||
else
|
||||
{
|
||||
$properties += $prop
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$properties = (?? $documentedObj.DefaultDocumentationProperties (@("Name","Value")))
|
||||
}
|
||||
|
||||
$lngId = ?: ($tableType -eq "BasicInfo") "SettingDetails.basics" "TableHeaders.settings" -AddCategories
|
||||
|
||||
Add-DocTableItems $obj $objectType ($documentedObj.$tableType) $properties $lngId `
|
||||
-AddCategories:($global:chkWordAddCategories.IsChecked -eq $true) `
|
||||
-AddSubcategories:($global:chkWordAddSubCategories.IsChecked -eq $true)
|
||||
}
|
||||
|
||||
if(($documentedObj.ComplianceActions | measure).Count -gt 0)
|
||||
{
|
||||
$properties = @("Action","Schedule","MessageTemplate","EmailCC")
|
||||
|
||||
Add-DocTableItems $obj $objectType $documentedObj.ComplianceActions $properties "Category.complianceActionsLabel"
|
||||
}
|
||||
|
||||
if(($documentedObj.ApplicabilityRules | measure).Count -gt 0)
|
||||
{
|
||||
$properties = @("Rule","Property","Value")
|
||||
|
||||
Add-DocTableItems $obj $objectType $documentedObj.ApplicabilityRules $properties "SettingDetails.applicabilityRules"
|
||||
}
|
||||
|
||||
if(($documentedObj.Assignments | measure).Count -gt 0)
|
||||
{
|
||||
$params = @{}
|
||||
if($documentedObj.Assignments[0].RawIntent)
|
||||
{
|
||||
$properties = @("GroupMode","Group","Filter","FilterMode")
|
||||
|
||||
$settingsObj = $documentedObj.Assignments | Where { $_.Settings -ne $null } | Select -First 1
|
||||
|
||||
if($settingsObj)
|
||||
{
|
||||
foreach($objProp in $settingsObj.Settings.Keys)
|
||||
{
|
||||
if($objProp -in $properties) { continue }
|
||||
if($objProp -in @("Category","RawIntent")) { continue }
|
||||
$properties += ("Settings." + $objProp)
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$isFilterAssignment = $false
|
||||
foreach($assignment in $documentedObj.Assignments)
|
||||
{
|
||||
if(($assignment.target.PSObject.Properties | Where Name -eq "deviceAndAppManagementAssignmentFilterType"))
|
||||
{
|
||||
$isFilterAssignment = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
$properties = @("Group")
|
||||
if($isFilterAssignment)
|
||||
{
|
||||
$properties += @("Filter","FilterMode")
|
||||
}
|
||||
$params.Add("AddCategories", $true)
|
||||
}
|
||||
|
||||
Add-DocTableItems $obj $objectType $documentedObj.Assignments $properties "TableHeaders.assignments" @params
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-LogError "Failed to process object $objName" $_.Exception
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-WordTranslateColumnHeader
|
||||
{
|
||||
param($columnName)
|
||||
|
||||
$lngText = ""
|
||||
if($script:columnHeaders.ContainsKey($columnName))
|
||||
{
|
||||
$lngText = Get-LanguageString $script:columnHeaders[$columnName]
|
||||
}
|
||||
|
||||
(?? $lngText $columnName)
|
||||
}
|
||||
function Set-WordColumnHeaderLanguageId
|
||||
{
|
||||
param($columnName, $lngId)
|
||||
|
||||
if(-not $script:columnHeaders -or -not $lngId) { return }
|
||||
|
||||
if($script:columnHeaders.ContainsKey($columnName))
|
||||
{
|
||||
$script:columnHeaders[$columnName] = $lngId
|
||||
}
|
||||
else
|
||||
{
|
||||
$script:columnHeaders.Add($columnName, $lngId)
|
||||
}
|
||||
}
|
||||
|
||||
function Add-DocTableItems
|
||||
{
|
||||
param($obj, $objectType, $items, $properties, $lngId, [switch]$AddCategories, [switch]$AddSubcategories)
|
||||
|
||||
$tblHeaderStyle = $global:txtWordTableHeaderStyle.Text
|
||||
$tblCategoryStyle = $global:txtWordCategoryHeaderStyle.Text
|
||||
$tblSubCategoryStyle = $global:txtWordSubCategoryHeaderStyle.Text
|
||||
|
||||
$range = $script:doc.application.selection.range
|
||||
|
||||
$script:docTable = $script:doc.Tables.Add($range, ($items.Count + 1), $properties.Count, [Microsoft.Office.Interop.Word.WdDefaultTableBehavior]::wdWord9TableBehavior, [Microsoft.Office.Interop.Word.WdAutoFitBehavior]::wdAutoFitWindow)
|
||||
$script:docTable.ApplyStyleHeadingRows = $true
|
||||
Set-DocObjectStyle $script:docTable $global:txtWordTableStyle.Text
|
||||
|
||||
if($lngId)
|
||||
{
|
||||
$caption = "$((Get-LanguageString $lngId)) - $((Get-GraphObjectName $obj $objectType))"
|
||||
}
|
||||
else
|
||||
{
|
||||
$caption = "$((Get-GraphObjectName $obj $objectType)) ($($objectType.Title))"
|
||||
}
|
||||
|
||||
$i = 1
|
||||
foreach($prop in $properties)
|
||||
{
|
||||
$script:docTable.Cell(1, $i).Range.Text = (Invoke-WordTranslateColumnHeader ($prop.Split(".")[-1]))
|
||||
$i++
|
||||
}
|
||||
|
||||
if(!(Set-DocObjectStyle $script:docTable.Rows(1).Range $tblHeaderStyle))
|
||||
{
|
||||
$script:docTable.Rows(1).Range.Font.Size += 2
|
||||
$script:docTable.Rows(1).Range.Font.Bold = $true
|
||||
}
|
||||
|
||||
$curCategory = ""
|
||||
$curSubCategory = ""
|
||||
|
||||
$row = 2
|
||||
foreach($itemObj in $items)
|
||||
{
|
||||
try
|
||||
{
|
||||
$i = 1
|
||||
foreach($prop in $properties)
|
||||
{
|
||||
try
|
||||
{
|
||||
# This adds support for properties like Settings.PropName
|
||||
$propArr = $prop.Split('.')
|
||||
$tmpObj = $itemObj
|
||||
$propName = $propArr[-1]
|
||||
for($x = 0; $x -lt ($propArr.Count - 1);$x++)
|
||||
{
|
||||
$tmpObj = $tmpObj."$($propArr[$x])"
|
||||
}
|
||||
$script:docTable.Cell($row, $i).Range.Text = "$($tmpObj.$propName)"
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-LogError "Failed to add property value for $prop" $_.Exception
|
||||
}
|
||||
$i++
|
||||
}
|
||||
|
||||
if($itemObj.Category -and $curCategory -ne $itemObj.Category -and $AddCategories -eq $true)
|
||||
{
|
||||
# Insert row for the Category above the new row
|
||||
$script:docTable.Rows.Add($script:docTable.Rows.Item($row)) | Out-Null
|
||||
$script:docTable.Rows.Item($row).Cells.Merge()
|
||||
$script:docTable.Cell($row, 1).Range.Text = $itemObj.Category
|
||||
|
||||
if(!(Set-DocObjectStyle $script:docTable.Rows($row).Range $tblCategoryStyle))
|
||||
{
|
||||
$script:docTable.Rows($row).Range.Font.Size += 2
|
||||
$script:docTable.Rows($row).Range.Font.Italic = $true
|
||||
}
|
||||
$row++
|
||||
$curCategory = $itemObj.Category
|
||||
$curSubCategory = ""
|
||||
}
|
||||
|
||||
if($itemObj.SubCategory -and $curSubCategory -ne $itemObj.SubCategory -and $AddSubcategories -eq $true)
|
||||
{
|
||||
# Insert row for the SubCategory above the new row
|
||||
$script:docTable.Rows.Add($script:docTable.Rows.Item($row)) | Out-Null
|
||||
$script:docTable.Rows.Item($row).Cells.Merge()
|
||||
$script:docTable.Cell($row, 1).Range.Text = $itemObj.SubCategory
|
||||
|
||||
if(!(Set-DocObjectStyle $script:docTable.Rows($row).Range $tblSubCategoryStyle))
|
||||
{
|
||||
$script:docTable.Rows($row).Range.Font.Italic = $true
|
||||
}
|
||||
$row++
|
||||
$curSubCategory = $itemObj.SubCategory
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Log "Failed to process property" 2
|
||||
}
|
||||
|
||||
$row++
|
||||
}
|
||||
|
||||
# -2 = Table, 1 = Below
|
||||
$script:docTable.Application.Selection.InsertCaption(-2, ". $caption", $null, 1)
|
||||
|
||||
# Add new row after the table
|
||||
#$script:doc.Application.Selection.InsertParagraphAfter()
|
||||
$script:doc.Application.Selection.TypeParagraph()
|
||||
#$script:doc.Application.Selection.TypeParagraph()
|
||||
}
|
||||
|
||||
function Get-DocStyle
|
||||
{
|
||||
param($styleName)
|
||||
|
||||
$tmpStyle = ($script:wordStyles | Where Name -like $styleName).Style
|
||||
|
||||
# BuiltIn Styles
|
||||
#[Enum]::GetNames([Microsoft.Office.Interop.Word.wdBuiltinStyle])
|
||||
|
||||
if(!$tmpStyle)
|
||||
{
|
||||
Write-Log "Style $styleName not found"
|
||||
}
|
||||
$tmpStyle
|
||||
}
|
||||
|
||||
function Add-DocText
|
||||
{
|
||||
param($text, $style, [switch]$SkipAddParagraph)
|
||||
|
||||
Set-DocObjectStyle $script:doc.application.selection $style | Out-Null
|
||||
|
||||
$script:doc.Application.Selection.TypeText($text)
|
||||
|
||||
if($SkipAddParagraph -ne $true)
|
||||
{
|
||||
# Add new paragraph by default
|
||||
$script:doc.Application.Selection.TypeParagraph()
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-DocGoToEnd
|
||||
{
|
||||
$script:doc.Application.Selection.goto([Microsoft.Office.Interop.Word.WdGoToItem]::wdGoToBookmark, $null, $null, '\EndOfDoc') | Out-Null
|
||||
}
|
||||
|
||||
function Set-WordDocBuiltInProperty
|
||||
{
|
||||
param($propertyName, $value)
|
||||
|
||||
try
|
||||
{
|
||||
$script:doc.BuiltInDocumentProperties([Microsoft.Office.Interop.Word.WdBuiltInProperty]$propertyName) = $value
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-LogError "Failed to set built in property $propertyName to $value" $_.Exception
|
||||
}
|
||||
}
|
||||
|
||||
function Set-DocObjectStyle
|
||||
{
|
||||
param($docObj, $objStyle)
|
||||
|
||||
$styleSet = $false
|
||||
if($docObj -and $objStyle)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(($script:builtinStyles | Where { $_ -eq $objStyle }))
|
||||
{
|
||||
$docObj.style = [Microsoft.Office.Interop.Word.wdBuiltinStyle]$objStyle
|
||||
}
|
||||
else
|
||||
{
|
||||
$docObj.style = $objStyle
|
||||
}
|
||||
$styleSet = $true
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Log "Failed to set style: $objStyle" 3
|
||||
}
|
||||
}
|
||||
$styleSet
|
||||
}
|
||||
@@ -10,7 +10,7 @@ This module is for the Endpoint Manager/Intune View. It manages Export/Import/Co
|
||||
#>
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'3.0.0'
|
||||
'3.1.0'
|
||||
}
|
||||
|
||||
function Invoke-InitializeModule
|
||||
@@ -100,7 +100,11 @@ function Invoke-InitializeModule
|
||||
API = "/deviceManagement/deviceConfigurations"
|
||||
QUERYLIST = "`$filter=not%20isof(%27microsoft.graph.windowsUpdateForBusinessConfiguration%27)%20and%20not%20isof(%27microsoft.graph.iosUpdateConfiguration%27)"
|
||||
#ExportFullObject = $false
|
||||
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
|
||||
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
|
||||
PropertiesToRemove = @("privacyAccessControls")
|
||||
PostFileImportCommand = { Start-PostFileImportDeviceConfiguration @args }
|
||||
PostCopyCommand = { Start-PostCopyDeviceConfiguration @args }
|
||||
GroupId = "DeviceConfiguration"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -110,6 +114,7 @@ function Invoke-InitializeModule
|
||||
API = "/identity/conditionalAccess/policies"
|
||||
Permissons=@("Policy.Read.All","Policy.ReadWrite.ConditionalAccess","Application.Read.All")
|
||||
Dependencies = @("NamedLocations","Applications")
|
||||
GroupId = "ConditionalAccess"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -119,6 +124,7 @@ function Invoke-InitializeModule
|
||||
API = "/identity/conditionalAccess/namedLocations"
|
||||
Permissons=@("Policy.ReadWrite.ConditionalAccess")
|
||||
ImportOrder = 50
|
||||
GroupId = "ConditionalAccess"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -134,6 +140,7 @@ function Invoke-InitializeModule
|
||||
#PreCopyCommand = { Start-PreCopyEndpointSecurity @args }
|
||||
PostCopyCommand = { Start-PostCopyEndpointSecurity @args }
|
||||
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
|
||||
GroupId = "EndpointSecurity"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -145,6 +152,7 @@ function Invoke-InitializeModule
|
||||
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
|
||||
Dependencies = @("Locations","Notifications")
|
||||
PostExportCommand = { Start-PostExportCompliancePolicies @args }
|
||||
GroupId = "CompliancePolicies"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -161,6 +169,7 @@ function Invoke-InitializeModule
|
||||
Permissons=@("DeviceManagementApps.ReadWrite.All")
|
||||
Icon = "Branding"
|
||||
SkipRemoveProperties = @('Id') # Id is removed by PreImport. Required for default profile
|
||||
GroupId = "TenantAdmin"
|
||||
})
|
||||
|
||||
<#
|
||||
@@ -197,6 +206,7 @@ function Invoke-InitializeModule
|
||||
Permissons=@("DeviceManagementServiceConfig.ReadWrite.All")
|
||||
SkipRemoveProperties = @('Id')
|
||||
AssignmentsType = "enrollmentConfigurationAssignments"
|
||||
GroupId = "WinEnrollment"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -210,6 +220,7 @@ function Invoke-InitializeModule
|
||||
Permissons=@("DeviceManagementServiceConfig.ReadWrite.All")
|
||||
SkipRemoveProperties = @('Id')
|
||||
AssignmentsType = "enrollmentConfigurationAssignments"
|
||||
GroupId = "EnrollmentRestrictions"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -220,13 +231,16 @@ function Invoke-InitializeModule
|
||||
PostExportCommand = { Start-PostExportAdministrativeTemplate @args }
|
||||
PostCopyCommand = { Start-PostCopyAdministrativeTemplate @args }
|
||||
PostFileImportCommand = { Start-PostFileImportAdministrativeTemplate @args }
|
||||
LoadObject = { Start-LoadAdministrativeTemplate @args }
|
||||
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
|
||||
Icon="DeviceConfiguration"
|
||||
GroupId = "DeviceConfiguration"
|
||||
CompareValue = "CombinedValueWithLabel"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
Title = "Scripts"
|
||||
Id = "Scripts"
|
||||
Title = "Scripts (PowerShell)"
|
||||
Id = "PowerShellScripts"
|
||||
API = "/deviceManagement/deviceManagementScripts"
|
||||
ViewID = "IntuneGraphAPI"
|
||||
DetailExtension = { Add-ScriptExtensions @args }
|
||||
@@ -234,8 +248,35 @@ function Invoke-InitializeModule
|
||||
PostExportCommand = { Start-PostExportScripts @args }
|
||||
Permissons=@("DeviceManagementManagedDevices.ReadWrite.All")
|
||||
AssignmentsType = "deviceManagementScriptAssignments"
|
||||
Icon="Scripts"
|
||||
GroupId = "Scripts"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
Title = "Scripts (Shell)"
|
||||
Id = "MacScripts"
|
||||
API = "/deviceManagement/deviceShellScripts"
|
||||
ViewID = "IntuneGraphAPI"
|
||||
DetailExtension = { Add-ScriptExtensions @args }
|
||||
ExportExtension = { Add-ScriptExportExtensions @args }
|
||||
PostExportCommand = { Start-PostExportScripts @args }
|
||||
Permissons=@("DeviceManagementManagedDevices.ReadWrite.All")
|
||||
AssignmentsType = "deviceManagementScriptAssignments"
|
||||
Icon="Scripts"
|
||||
GroupId = "Scripts"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
Title = "Custom Attributes"
|
||||
Id = "MacCustomAttributes"
|
||||
API = "/deviceManagement/deviceCustomAttributeShellScripts"
|
||||
ViewID = "IntuneGraphAPI"
|
||||
Permissons=@("DeviceManagementManagedDevices.ReadWrite.All")
|
||||
AssignmentsType = "deviceManagementScriptAssignments"
|
||||
Icon="CustomAttributes"
|
||||
GroupId = "CustomAttributes" # MacOS Settings
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
Title = "Terms and Conditions"
|
||||
Id = "TermsAndConditions"
|
||||
@@ -245,6 +286,7 @@ function Invoke-InitializeModule
|
||||
ExpandAssignments = $false # Not supported for this object type
|
||||
PostExportCommand = { Start-PostExportTermsAndConditions @args }
|
||||
PreImportAssignmentsCommand = { Start-PreImportAssignmentsTermsAndConditions @args }
|
||||
GroupId = "TenantAdmin"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -258,8 +300,10 @@ function Invoke-InitializeModule
|
||||
PostImportCommand = { Start-PostImportAppProtection @args }
|
||||
PreImportAssignmentsCommand = { Start-PreImportAssignmentsAppProtection @args }
|
||||
ExportFullObject = $true
|
||||
PropertiesToRemove = @('exemptAppLockerFiles')
|
||||
Permissons=@("DeviceManagementApps.ReadWrite.All")
|
||||
Dependencies = @("Applications")
|
||||
GroupId = "AppProtection"
|
||||
})
|
||||
|
||||
# These are also included in the managedAppPolicies API
|
||||
@@ -276,6 +320,7 @@ function Invoke-InitializeModule
|
||||
Permissons=@("DeviceManagementApps.ReadWrite.All")
|
||||
Dependencies = @("Applications")
|
||||
Icon = "AppConfiguration"
|
||||
GroupId = "AppConfiguration"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -289,6 +334,7 @@ function Invoke-InitializeModule
|
||||
PreImportAssignmentsCommand = { Start-PreImportAssignmentsAppConfiguration @args }
|
||||
#PostExportCommand = { Start-PostExportAppConfiguration @args }
|
||||
Icon = "AppConfiguration"
|
||||
GroupId = "AppConfiguration"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -296,14 +342,17 @@ function Invoke-InitializeModule
|
||||
Id = "Applications"
|
||||
API = "/deviceAppManagement/mobileApps"
|
||||
ViewID = "IntuneGraphAPI"
|
||||
PropertiesToRemove = @('uploadState','publishingState','isAssigned','dependentAppCount','supersedingAppCount','supersededAppCount','committedContentVersion','isFeatured','size')
|
||||
PropertiesToRemove = @('uploadState','publishingState','isAssigned','dependentAppCount','supersedingAppCount','supersededAppCount','committedContentVersion','isFeatured','size','categories')
|
||||
QUERYLIST = "`$filter=(microsoft.graph.managedApp/appAvailability%20eq%20null%20or%20microsoft.graph.managedApp/appAvailability%20eq%20%27lineOfBusiness%27%20or%20isAssigned%20eq%20true)&`$orderby=displayName"
|
||||
Permissons=@("DeviceManagementApps.ReadWrite.All")
|
||||
AssignmentsType="mobileAppAssignments"
|
||||
AssignmentProperties = @("@odata.type","target","settings","intent")
|
||||
AssignmentTargetProperties = @("@odata.type","groupId","deviceAndAppManagementAssignmentFilterId","deviceAndAppManagementAssignmentFilterType")
|
||||
ImportOrder = 60
|
||||
Expand="categories,assignments" # ODataMetadata is set to minimal so assignments can't be autodetected
|
||||
ODataMetadata="minimal" # categories property not supported with ODataMetadata full
|
||||
PostFileImportCommand = { Start-PostFileImportApplications @args }
|
||||
GroupId = "Apps"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -314,6 +363,7 @@ function Invoke-InitializeModule
|
||||
CopyDefaultName = "%displayName% Copy" # '-' is not allowed in the name
|
||||
Permissons=@("DeviceManagementServiceConfig.ReadWrite.All")
|
||||
PreImportAssignmentsCommand = { Start-PreImportAssignmentsAutoPilot @args }
|
||||
GroupId = "WinEnrollment"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -326,7 +376,8 @@ function Invoke-InitializeModule
|
||||
PreImportCommand = { Start-PreImportPolicySets @args }
|
||||
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
|
||||
ImportOrder = 2000 # Policy Sets reference other objects so make sure it is imported last
|
||||
Dependencies = @("Applications","AppConfiguration","AppProtection","AutoPilot","EnrollmentRestrictions","EnrollmentStatusPage","DeviceConfiguration","AdministrativeTemplates","SettingsCatalog","CompliancePolicies")
|
||||
Dependencies = @("Applications","AppConfiguration","AppProtection","AutoPilot","EnrollmentRestrictions","EnrollmentStatusPage","DeviceConfiguration","AdministrativeTemplates","SettingsCatalog","CompliancePolicies")
|
||||
GroupId = "PolicySets"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -337,6 +388,7 @@ function Invoke-InitializeModule
|
||||
QUERYLIST = "`$filter=isof(%27microsoft.graph.windowsUpdateForBusinessConfiguration%27)%20or%20isof(%27microsoft.graph.iosUpdateConfiguration%27)"
|
||||
#ExportFullObject = $false
|
||||
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
|
||||
GroupId = "WinUpdatePolicies"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -344,9 +396,20 @@ function Invoke-InitializeModule
|
||||
Id = "FeatureUpdates"
|
||||
ViewID = "IntuneGraphAPI"
|
||||
API = "/deviceManagement/windowsFeatureUpdateProfiles"
|
||||
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
|
||||
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
|
||||
GroupId = "WinFeatureUpdates"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
Title = "Quality Updates"
|
||||
Id = "QualityUpdates"
|
||||
ViewID = "IntuneGraphAPI"
|
||||
API = "/deviceManagement/windowsQualityUpdateProfiles"
|
||||
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
|
||||
Icon = "UpdatePolicies"
|
||||
GroupId = "WinQualityUpdates"
|
||||
})
|
||||
|
||||
# Locations are not FULLY supported
|
||||
# They will be imported but Compliance Policies will not be updated with new Location object after import
|
||||
# ToDo: Add support Export/Import Location Settings
|
||||
@@ -363,6 +426,7 @@ function Invoke-InitializeModule
|
||||
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
|
||||
PreImportCommand = { Start-PreImportLocations @args }
|
||||
ImportOrder = 30
|
||||
GroupId = "CompliancePolicies"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -377,6 +441,7 @@ function Invoke-InitializeModule
|
||||
Expand="Settings"
|
||||
Icon="DeviceConfiguration"
|
||||
PostExportCommand = { Start-PostExportSettingsCatalog @args }
|
||||
GroupId = "DeviceConfiguration"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -391,6 +456,7 @@ function Invoke-InitializeModule
|
||||
Permissons=@("DeviceManagementRBAC.ReadWrite.All")
|
||||
ImportOrder = 20
|
||||
#expand=roleassignments
|
||||
GroupId = "TenantAdmin"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -402,6 +468,7 @@ function Invoke-InitializeModule
|
||||
Permissons=@("DeviceManagementRBAC.ReadWrite.All")
|
||||
PostExportCommand = { Start-PostExportScopeTags @args }
|
||||
ImportOrder = 10
|
||||
GroupId = "TenantAdmin"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
@@ -415,7 +482,7 @@ function Invoke-InitializeModule
|
||||
PreImportCommand = { Start-PreImportNotifications @args }
|
||||
PostFileImportCommand = { Start-PostFileImportNotifications @args }
|
||||
PostCopyCommand = { Start-PostCopyNotifications @args }
|
||||
|
||||
GroupId = "CompliancePolicies"
|
||||
})
|
||||
|
||||
# This has some pre-reqs for working!
|
||||
@@ -432,6 +499,7 @@ function Invoke-InitializeModule
|
||||
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
|
||||
Icon="DeviceConfiguration"
|
||||
Dependencies = @("Applications")
|
||||
GroupId = "DeviceConfiguration"
|
||||
})
|
||||
|
||||
# Copy/Export/Import not verified!
|
||||
@@ -441,7 +509,18 @@ function Invoke-InitializeModule
|
||||
ViewID = "IntuneGraphAPI"
|
||||
API = "/deviceManagement/appleUserInitiatedEnrollmentProfiles"
|
||||
Permissons=@("DeviceManagementServiceConfig.ReadWrite.All")
|
||||
})
|
||||
GroupId = "AppleEnrollment"
|
||||
})
|
||||
|
||||
Add-ViewItem (New-Object PSObject -Property @{
|
||||
Title = "Filters"
|
||||
Id = "AssignmentFilters"
|
||||
ViewID = "IntuneGraphAPI"
|
||||
API = "/deviceManagement/assignmentFilters"
|
||||
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
|
||||
ImportOrder = 15
|
||||
GroupId = "TenantAdmin"
|
||||
})
|
||||
}
|
||||
|
||||
function Invoke-EMAuthenticateToMSAL
|
||||
@@ -507,15 +586,6 @@ function Set-EMViewPanel
|
||||
Add-XamlEvent $panel "btnImport" "Add_Click" -scriptBlock ([scriptblock]{
|
||||
Show-GraphImportForm
|
||||
})
|
||||
|
||||
Add-XamlEvent $panel "chkSelectAll" "Add_Click" -scriptBlock ([scriptblock]{
|
||||
foreach($item in $global:dgObjects.ItemsSource)
|
||||
{
|
||||
$item.IsSelected = $this.IsChecked
|
||||
}
|
||||
$global:dgObjects.Items.Refresh()
|
||||
})
|
||||
|
||||
|
||||
Add-XamlEvent $panel "txtFilter" "Add_LostFocus" ({ #param($obj, $e)
|
||||
Invoke-FiterBoxChanged $this
|
||||
@@ -548,8 +618,6 @@ function Set-EMViewPanel
|
||||
$enabled = (?: ($this.ItemsSource -eq $null -or ($this.ItemsSource | measure).Count -eq 0) $false $true)
|
||||
Set-XamlProperty $global:dgObjects.Parent "btnImport" "IsEnabled" $true # Always all Import if ObjectType allows it
|
||||
Set-XamlProperty $global:dgObjects.Parent "btnExport" "IsEnabled" $enabled
|
||||
Set-XamlProperty $global:dgObjects.Parent "chkSelectAll" "IsEnabled" $enabled
|
||||
Set-XamlProperty $global:dgObjects.Parent "chkSelectAll" "IsChecked" $false
|
||||
})
|
||||
}
|
||||
|
||||
@@ -641,14 +709,14 @@ function Start-PostListEndpointSecurity
|
||||
foreach($obj in $objList)
|
||||
{
|
||||
if(-not $obj.Object.templateId) { continue }
|
||||
if($obj.Object.templateId -ne $baseLineTepmlate.Id)
|
||||
if($obj.Object.templateId -ne $baseLineTemplate.Id)
|
||||
{
|
||||
$baseLineTepmlate = $script:baseLineTemplates | Where Id -eq $obj.Object.templateId
|
||||
$baseLineTemplate = $script:baseLineTemplates | Where Id -eq $obj.Object.templateId
|
||||
}
|
||||
if($baseLineTepmlate)
|
||||
if($baseLineTemplate)
|
||||
{
|
||||
$obj | Add-Member -MemberType NoteProperty -Name "Type" -Value $baseLineTepmlate.displayName
|
||||
$obj | Add-Member -MemberType NoteProperty -Name "Category" -Value (?: ($baseLineTepmlate.templateSubtype -eq "none") $baseLineTepmlate.templateType $baseLineTepmlate.templateSubtype)
|
||||
$obj | Add-Member -MemberType NoteProperty -Name "Type" -Value $baseLineTemplate.displayName
|
||||
$obj | Add-Member -MemberType NoteProperty -Name "Category" -Value (?: ($baseLineTemplate.templateSubtype -eq "none") $baseLineTemplate.templateType $baseLineTemplate.templateSubtype)
|
||||
}
|
||||
}
|
||||
$objList
|
||||
@@ -705,6 +773,46 @@ function Start-PostCopyEndpointSecurity
|
||||
|
||||
#endregion
|
||||
|
||||
#region
|
||||
|
||||
function Start-PostFileImportDeviceConfiguration
|
||||
{
|
||||
param($obj, $objectType, $importFile)
|
||||
|
||||
if($obj.'@OData.Type' -like "#microsoft.graph.windows10GeneralConfiguration")
|
||||
{
|
||||
$tmpObj = Get-Content $importFile | ConvertFrom-Json
|
||||
|
||||
if(($tmpObj.privacyAccessControls | measure).Count -gt 0)
|
||||
{
|
||||
$privacyObj = [PSCustomObject]@{
|
||||
windowsPrivacyAccessControls = $tmpObj.privacyAccessControls
|
||||
}
|
||||
$json = $privacyObj | ConvertTo-Json -Depth 10
|
||||
$ret = Invoke-GraphRequest -Url "deviceManagement/deviceConfigurations('$($obj.Id)')/windowsPrivacyAccessControls" -Body $json -Method "POST"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Start-PostCopyDeviceConfiguration
|
||||
{
|
||||
param($objCopyFrom, $objNew, $objectType)
|
||||
|
||||
if($objCopyFrom.'@OData.Type' -like "#microsoft.graph.windows10GeneralConfiguration")
|
||||
{
|
||||
if(($objCopyFrom.privacyAccessControls | measure).Count -gt 0)
|
||||
{
|
||||
$privacyObj = [PSCustomObject]@{
|
||||
windowsPrivacyAccessControls = $objCopyFrom.privacyAccessControls
|
||||
}
|
||||
$json = $privacyObj | ConvertTo-Json -Depth 10
|
||||
$ret = Invoke-GraphRequest -Url "deviceManagement/deviceConfigurations('$($objNew.Id)')/windowsPrivacyAccessControls" -Body $json -Method "POST"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Compliance Policy
|
||||
function Start-PostExportCompliancePolicies
|
||||
{
|
||||
@@ -982,10 +1090,15 @@ function Start-GetAppProtection
|
||||
{
|
||||
|
||||
}
|
||||
$expand = $null
|
||||
if($objectClass -eq "windowsInformationProtectionPolicies")
|
||||
{
|
||||
$expand = "?`$expand=protectedAppLockerFiles,exemptAppLockerFiles"
|
||||
}
|
||||
|
||||
if($objectClass)
|
||||
{
|
||||
@{"API"="/deviceAppManagement/$objectClass/$($obj.Id)"}
|
||||
@{"API"="/deviceAppManagement/$objectClass/$($obj.Id)$expand"}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1150,7 +1263,7 @@ function Start-PostFileImportApplications
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Group Policy/Administrative Template functions
|
||||
#region Group Policy/Administrative Templates functions
|
||||
function Get-GPOObjectSettings
|
||||
{
|
||||
param($GPOObj)
|
||||
@@ -1246,6 +1359,31 @@ function Start-PostFileImportAdministrativeTemplate
|
||||
}
|
||||
}
|
||||
|
||||
function Start-LoadAdministrativeTemplate
|
||||
{
|
||||
param($fileName)
|
||||
|
||||
if(-not $fileName) { return $null }
|
||||
|
||||
$fi = [IO.FileInfo]$fileName
|
||||
$obj = Get-Content $global:txtCompareFile.Text | ConvertFrom-Json
|
||||
|
||||
if($obj.definitionValues)
|
||||
{
|
||||
return $obj
|
||||
}
|
||||
|
||||
$settingsFile = $fi.DirectoryName + "\" + $fi.BaseName + "_Settings.json"
|
||||
|
||||
if([IO.File]::Exists($settingsFile))
|
||||
{
|
||||
$definitionValues = Get-Content $settingsFile | ConvertFrom-Json
|
||||
|
||||
$obj | Add-Member Noteproperty -Name "definitionValues" -Value $definitionValues -Force
|
||||
}
|
||||
$obj
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Policy Sets function
|
||||
@@ -1520,16 +1658,26 @@ function Get-EMSettingsObject
|
||||
{
|
||||
param($obj, $objectType, $file)
|
||||
|
||||
$fi = [IO.FileInfo]$file
|
||||
$settingsFile = $fi.DirectoryName + "\" + $fi.BaseName + "_Settings.json"
|
||||
$fiSettings = [IO.FileInfo]$settingsFile
|
||||
if($fiSettings.Exists -eq $false)
|
||||
{
|
||||
Write-Log "Settings file '$($fiSettings.FullName)' was not found" 2
|
||||
return
|
||||
}
|
||||
if($obj.Settings) { $obj.Settings }
|
||||
|
||||
(Get-Content $fiSettings.FullName) | ConvertFrom-Json
|
||||
$fi = [IO.FileInfo]$file
|
||||
if($fi.Exists)
|
||||
{
|
||||
Write-Log "Settings not included in export file. Try import from _Settings.json file" 2
|
||||
$settingsFile = $fi.DirectoryName + "\" + $fi.BaseName + "_Settings.json"
|
||||
$fiSettings = [IO.FileInfo]$settingsFile
|
||||
if($fiSettings.Exists -eq $false)
|
||||
{
|
||||
Write-Log "Settings file '$($fiSettings.FullName)' was not found" 2
|
||||
return
|
||||
}
|
||||
|
||||
(Get-Content $fiSettings.FullName) | ConvertFrom-Json
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Log "Settings not included in export file and _Settings.json file is missing." 3
|
||||
}
|
||||
}
|
||||
|
||||
function Add-EMAssignmentsToExportFile
|
||||
|
||||
@@ -10,7 +10,7 @@ This module is for the Endpoint Info View. It shows read-only objects in Intune
|
||||
#>
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'3.0.0'
|
||||
'3.1.0'
|
||||
}
|
||||
|
||||
function Invoke-InitializeModule
|
||||
|
||||
@@ -10,7 +10,7 @@ This module manages Application objects in Intune e.g. uploading application fil
|
||||
#>
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'3.0.0'
|
||||
'3.1.1'
|
||||
}
|
||||
|
||||
#########################################################################################
|
||||
@@ -431,6 +431,8 @@ function Write-AzureStorageChunk
|
||||
"x-ms-blob-type" = "BlockBlob"
|
||||
}
|
||||
|
||||
$curProgressPreference = $ProgressPreference
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
try
|
||||
{
|
||||
$response = Invoke-WebRequest $uri -Method Put -Headers $headers -Body $encodedBody
|
||||
@@ -439,6 +441,7 @@ function Write-AzureStorageChunk
|
||||
{
|
||||
Write-Log "Failed to upload file chunk. $($_.Exception.Message)" 3
|
||||
}
|
||||
$ProgressPreference = $curProgressPreference
|
||||
}
|
||||
|
||||
function Get-IntuneKey
|
||||
|
||||
@@ -1338,7 +1338,23 @@ function Show-MSALDecodedToken {
|
||||
}
|
||||
elseif($prop.Name -in @("wids"))
|
||||
{
|
||||
$value = $tokenData.Payload."$($prop.Name)" -join "`n"
|
||||
if(-not $script:aadRoles)
|
||||
{
|
||||
$script:aadRoles =(Invoke-GraphRequest -url "/directoryRoles?`$select=roleTemplateId,displayName" -ODataMetadata "minimal").value
|
||||
}
|
||||
$wids = @()
|
||||
foreach($wid in $tokenData.Payload."$($prop.Name)")
|
||||
{
|
||||
$text = $wid
|
||||
$role = ($script:aadRoles | where roleTemplateId -eq $wid)
|
||||
if($role)
|
||||
{
|
||||
$text = ($text + " ($($role.displayName))")
|
||||
}
|
||||
$wids += $text
|
||||
}
|
||||
$value = $wids -join "`n"
|
||||
#$value = $tokenData.Payload."$($prop.Name)" -join "`n"
|
||||
}
|
||||
elseif($prop.Name -in @("scp"))
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ This module manages Microsoft Grap fuctions like calling APIs, managing graph ob
|
||||
#>
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'3.0.0'
|
||||
'3.1.0'
|
||||
}
|
||||
|
||||
$global:MSGraphGlobalApps = @(
|
||||
@@ -145,7 +145,10 @@ function Invoke-GraphRequest
|
||||
|
||||
[Switch]$SkipAuthentication,
|
||||
|
||||
$ODataMetadata = "full" # full, minimal, none or skip
|
||||
$ODataMetadata = "full", # full, minimal, none or skip
|
||||
|
||||
[switch]
|
||||
$NoError
|
||||
)
|
||||
|
||||
if($SkipAuthentication -ne $true)
|
||||
@@ -165,11 +168,6 @@ function Invoke-GraphRequest
|
||||
'ExpiresOn' = $global:MSALToken.ExpiresOn
|
||||
'x-ms-client-request-id' = $requestId
|
||||
}
|
||||
|
||||
if($ContentLanguage)
|
||||
{
|
||||
$Headers.Add("Content-Language",$ContentLanguage)
|
||||
}
|
||||
}
|
||||
|
||||
if($HttpMethod -eq "GET" -and $ODataMetadata -ne "Skip")
|
||||
@@ -188,7 +186,7 @@ function Invoke-GraphRequest
|
||||
|
||||
if($AdditionalHeaders -is [HashTable])
|
||||
{
|
||||
foreach($key in $AdditionalHeaders)
|
||||
foreach($key in $AdditionalHeaders.Keys)
|
||||
{
|
||||
if($Headers.ContainsKey($key)) { continue }
|
||||
|
||||
@@ -238,6 +236,7 @@ function Invoke-GraphRequest
|
||||
}
|
||||
catch
|
||||
{
|
||||
if($NoError -eq $true) { return }
|
||||
Write-LogError "Failed to invoke MS Graph with URL $Url (Request ID: $requestId). Status code: $($_.Exception.Response.StatusCode)" $_.Excption
|
||||
}
|
||||
|
||||
@@ -255,13 +254,20 @@ function Get-GraphObjects
|
||||
$property = $null,
|
||||
[Array]
|
||||
$exclude,
|
||||
$SortProperty = "displayName")
|
||||
$SortProperty = "displayName",
|
||||
$objectType)
|
||||
|
||||
$objects = @()
|
||||
|
||||
if($property -isnot [Object[]]) { $property = @('displayName', 'description', 'id')}
|
||||
|
||||
$graphObjects = Invoke-GraphRequest -Url $url
|
||||
$params = @{}
|
||||
if($objectType.ODataMetadata)
|
||||
{
|
||||
$params.Add('ODataMetadata',$objectType.ODataMetadata)
|
||||
}
|
||||
|
||||
$graphObjects = Invoke-GraphRequest -Url $url @params
|
||||
|
||||
if($graphObjects -and ($graphObjects | GM -Name Value -MemberType NoteProperty))
|
||||
{
|
||||
@@ -281,6 +287,7 @@ function Get-GraphObjects
|
||||
{
|
||||
$objTmp | Add-Member -NotePropertyName "IsSelected" -NotePropertyValue $false
|
||||
$objTmp | Add-Member -NotePropertyName "Object" -NotePropertyValue $graphObject
|
||||
$objTmp | Add-Member -NotePropertyName "ObjectType" -NotePropertyValue $objectType
|
||||
$objects += $objTmp
|
||||
}
|
||||
}
|
||||
@@ -333,7 +340,7 @@ function Show-GraphObjects
|
||||
$url = "$($url.Trim())?$($global:curObjectType.QUERYLIST.Trim())"
|
||||
}
|
||||
|
||||
$graphObjects = @(Get-GraphObjects -Url $url -property $global:curObjectType.ViewProperties)
|
||||
$graphObjects = @(Get-GraphObjects -Url $url -property $global:curObjectType.ViewProperties -objectType $global:curObjectType)
|
||||
|
||||
if($global:curObjectType.PostListCommand)
|
||||
{
|
||||
@@ -348,25 +355,22 @@ function Show-GraphObjects
|
||||
|
||||
$prop = $tmpObj.PSObject.Properties | Where Name -eq "IsSelected"
|
||||
if($prop)
|
||||
{
|
||||
# Build the CheckBox column for IsSelected
|
||||
$binding = [System.Windows.Data.Binding]::new($prop.Name)
|
||||
$binding.UpdateSourceTrigger = [System.Windows.Data.UpdateSourceTrigger]::PropertyChanged
|
||||
$column = [System.Windows.Controls.DataGridTemplateColumn]::new()
|
||||
$fef = [System.Windows.FrameworkElementFactory]::new([System.Windows.Controls.CheckBox])
|
||||
$binding.Mode = [System.Windows.Data.BindingMode]::TwoWay
|
||||
$fef.SetValue([System.Windows.Controls.CheckBox]::IsCheckedProperty,$binding)
|
||||
$dt = [System.Windows.DataTemplate]::new()
|
||||
$dt.VisualTree = $fef
|
||||
$column.CellTemplate = $dt
|
||||
#$header = [System.Windows.Controls.CheckBox]::new()
|
||||
#$column.Header = $header
|
||||
{
|
||||
$column = Get-GridCheckboxColumn "IsSelected"
|
||||
$dgObjects.Columns.Add($column)
|
||||
|
||||
$column.Header.add_Click({
|
||||
foreach($item in $global:dgObjects.ItemsSource)
|
||||
{
|
||||
$item.IsSelected = $this.IsChecked
|
||||
}
|
||||
$global:dgObjects.Items.Refresh()
|
||||
})
|
||||
}
|
||||
|
||||
$tableColumns = @()
|
||||
# Add other columns
|
||||
foreach($prop in ($tmpObj.PSObject.Properties | Where {$_.Name -notin @("IsSelected","Object")}))
|
||||
foreach($prop in ($tmpObj.PSObject.Properties | Where {$_.Name -notin @("IsSelected","Object","ObjectType")}))
|
||||
{
|
||||
$binding = [System.Windows.Data.Binding]::new($prop.Name)
|
||||
$column = [System.Windows.Controls.DataGridTextColumn]::new()
|
||||
@@ -379,21 +383,6 @@ function Show-GraphObjects
|
||||
}
|
||||
$ocList = [System.Collections.ObjectModel.ObservableCollection[object]]::new($graphObjects)
|
||||
$dgObjects.ItemsSource = [System.Windows.Data.CollectionViewSource]::GetDefaultView($ocList)
|
||||
|
||||
<#
|
||||
$dt = New-Object System.Data.DataTable
|
||||
[void]$dt.Columns.AddRange($tableColumns)
|
||||
foreach ($graphObject in $graphObjects)
|
||||
{
|
||||
$rowValues = @()
|
||||
Foreach ($prop in $tableColumns)
|
||||
{
|
||||
$rowValues += $graphObject.$prop
|
||||
}
|
||||
$dt.Rows.Add($rowValues) | Out-Null
|
||||
}
|
||||
$dgObjects.ItemsSource = $dt.DefaultView
|
||||
#>
|
||||
|
||||
# Show/Hide buttons based on object type
|
||||
foreach($ctrl in $spSubMenu.Children)
|
||||
@@ -408,7 +397,7 @@ function Show-GraphObjects
|
||||
Write-LogDebug "Hide $($ctrl.Name)"
|
||||
$ctrl.Visibility = "Collapsed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Clear-GraphObjects
|
||||
@@ -467,11 +456,16 @@ function Get-GraphObject
|
||||
if($obj.'roleAssignments@odata.navigationLink')
|
||||
{
|
||||
$expand += "roleAssignments"
|
||||
}
|
||||
|
||||
if($obj.'privacyAccessControls@odata.associationLink')
|
||||
{
|
||||
$expand += "microsoft.graph.windows10GeneralConfiguration/privacyAccessControls"
|
||||
}
|
||||
|
||||
if($objectType.Expand)
|
||||
{
|
||||
foreach($objExpand in $objectType.Expand)
|
||||
foreach($objExpand in $objectType.Expand.Split(","))
|
||||
{
|
||||
if($objExpand -notin $expand) { $expand += $objExpand}
|
||||
}
|
||||
@@ -479,12 +473,23 @@ function Get-GraphObject
|
||||
|
||||
if($expand.Count -gt 0)
|
||||
{
|
||||
if($api.IndexOf('?') -eq -1) { $api = ($api + "?")}
|
||||
else { $api = ($api + "&")}
|
||||
$api = ($api + ("expand=" + ($expand -join ",")))
|
||||
if($api.IndexOf('?') -eq -1)
|
||||
{
|
||||
$api = ($api + "?`$expand=")
|
||||
}
|
||||
elseif($api.IndexOf("`$expand") -gt 1)
|
||||
{
|
||||
$api = ($api + ",")
|
||||
}
|
||||
else
|
||||
{
|
||||
$api = ($api + "&`$expand=")
|
||||
}
|
||||
|
||||
$api = ($api + ($expand -join ","))
|
||||
}
|
||||
|
||||
$objInfo = Get-GraphObjects -Url $api -property $objectType.ViewProperties
|
||||
$objInfo = Get-GraphObjects -Url $api -property $objectType.ViewProperties -objectType $objectType
|
||||
|
||||
if($objInfo -and $objectType.PostGetCommand)
|
||||
{
|
||||
@@ -545,48 +550,40 @@ function Get-GraphMetaData
|
||||
{
|
||||
if(-not $global:metaDataXML)
|
||||
{
|
||||
$downloadSize = 0
|
||||
$url = "https://graph.microsoft.com/beta/`$metadata#deviceAppManagement"
|
||||
try
|
||||
{
|
||||
$wr = [net.WebRequest]::Create($url)
|
||||
try
|
||||
{
|
||||
$wrResponse = $wr.GetResponse()
|
||||
$downloadSize = $wrResponse.ContentLength
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
$wrResponse.Close()
|
||||
$wrResponse.Dispose()
|
||||
}
|
||||
$wr.Abort()
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
#ToDo: When do we update/re-download it?
|
||||
$fileName = [Environment]::ExpandEnvironmentVariables("%LOCALAPPDATA%\GraphPowerShellManager\GraphMetaData.xml")
|
||||
$fi = [IO.FileInfo]$fileName
|
||||
if($fi.Exists -and $fi.Length -ne $downloadSize)
|
||||
# Graph metadata does not support Content-Length in response so size can not be used to check if it is updated
|
||||
# There also no other version information in response headers. Use file date to update every week
|
||||
$url = "https://graph.microsoft.com/beta/`$metadata"
|
||||
$fileFullPath = [Environment]::ExpandEnvironmentVariables("%LOCALAPPDATA%\CloudAPIPowerShellManagement\GraphMetaData.xml")
|
||||
$fi = [IO.FileInfo]$fileFullPath
|
||||
$maxAge = (Get-Date).AddDays(-7)
|
||||
if($fi.Exists -and ($fi.LastWriteTime -gt $maxAge -or $fi.CreationTime -gt $maxAge))
|
||||
{
|
||||
try
|
||||
{
|
||||
[xml]$global:metaDataXML = Get-Content $fileName
|
||||
[xml]$global:metaDataXML = Get-Content $fi.FullName
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
|
||||
if(-not $global:metaDataXML)
|
||||
{
|
||||
$ret = Invoke-WebRequest $url -UseBasicParsing
|
||||
[xml]$global:metaDataXML = $ret.Content
|
||||
try { $global:metaDataXML.Save($fileName) } catch {}
|
||||
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")
|
||||
$wc = New-Object System.Net.WebClient
|
||||
$wc.Encoding = [System.Text.Encoding]::UTF8
|
||||
try
|
||||
{
|
||||
[xml]$global:metaDataXML = $wc.DownloadString($url)
|
||||
# Download to string and then use Save to format the XML output
|
||||
$global:metaDataXML.Save($fi.FullName)
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-LogError "Failed to download Graph MetaData file" $_.Exception
|
||||
}
|
||||
finally
|
||||
{
|
||||
$wc.Dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -627,9 +624,9 @@ function Show-GraphExportForm
|
||||
Set-XamlProperty $script:exportForm "chkAddCompanyName" "IsChecked" (Get-SettingValue "AddCompanyName")
|
||||
|
||||
Set-XamlProperty $script:exportForm "btnExportSelected" "IsEnabled" ($global:dgObjects.SelectedItem -ne $null)
|
||||
if(($global:dgObjects.ItemsSource | Where IsSelected).Count -gt 0)
|
||||
if(($global:dgObjects.ItemsSource | Where IsSelected -eq $true).Count -gt 0)
|
||||
{
|
||||
Set-XamlProperty $script:exportForm "lblSelectedObject" "Content" "$(($global:dgObjects.ItemsSource | Where IsSelected).Count) selected object(s)"
|
||||
Set-XamlProperty $script:exportForm "lblSelectedObject" "Content" "$(($global:dgObjects.ItemsSource | Where IsSelected -eq $true).Count) selected object(s)"
|
||||
}
|
||||
elseif($global:dgObjects.SelectedItem)
|
||||
{
|
||||
@@ -742,7 +739,7 @@ function Show-GraphBulkExportForm
|
||||
{
|
||||
$folder = Get-GraphObjectFolder $item.ObjectType (Get-XamlProperty $script:exportForm "txtExportPath" "Text") (Get-XamlProperty $script:exportForm "chkAddObjectType" "IsChecked") (Get-XamlProperty $script:exportForm "chkAddCompanyName" "IsChecked")
|
||||
|
||||
$objects = @(Get-GraphObjects -Url $url -property $objectType.ViewProperties)
|
||||
$objects = @(Get-GraphObjects -Url $url -property $objectType.ViewProperties -objectType $objectType)
|
||||
foreach($obj in $objects)
|
||||
{
|
||||
Write-Status "Export $($item.Title): $((Get-GraphObjectName $obj))" -Force
|
||||
@@ -908,6 +905,7 @@ function Show-GraphBulkImportForm
|
||||
|
||||
foreach($item in ($script:importObjects | where Selected -eq $true | sort-object -property @{e={$_.ObjectType.ImportOrder}}))
|
||||
{
|
||||
Write-Status "Import $($item.ObjectType.Title) objects" -Force
|
||||
Write-Log "----------------------------------------------------------------"
|
||||
Write-Log "Import $($item.ObjectType.Title) objects"
|
||||
Write-Log "----------------------------------------------------------------"
|
||||
@@ -1085,7 +1083,7 @@ function Import-GraphFile
|
||||
|
||||
$keepProperties = ?? $file.ObjectType.AssignmentProperties @("target")
|
||||
$keepTargetProperties = ?? $file.ObjectType.AssignmentTargetProperties @("@odata.type","groupId")
|
||||
$ObjctAssignments = @()
|
||||
$ObjectAssignments = @()
|
||||
foreach($assignment in $objClone.Assignments)
|
||||
{
|
||||
if($assignment.target.UserId -or ($assignment.Source -and $assignment.Source -ne "direct"))
|
||||
@@ -1106,10 +1104,10 @@ function Import-GraphFile
|
||||
if($prop.Name -in $keepTargetProperties) { continue }
|
||||
Remove-Property $assignment.target $prop.Name
|
||||
}
|
||||
$ObjctAssignments += $assignment
|
||||
$ObjectAssignments += $assignment
|
||||
}
|
||||
|
||||
$objClone.Assignments = $ObjctAssignments
|
||||
$objClone.Assignments = $ObjectAssignments
|
||||
|
||||
if(($objClone.Assignments | measure).Count -gt 0)
|
||||
{
|
||||
@@ -1117,7 +1115,10 @@ function Import-GraphFile
|
||||
$strAssign = "$((Update-JsonForEnvironment ($objClone.Assignments | ConvertTo-Json -Depth 10)))"
|
||||
# Array characters [ ] is not included if there is only one assignment
|
||||
# Added them if they are missing
|
||||
if($strAssign.Trim().StartsWith("[") -eq $false) { $strAssign = (" [ " + $strAssign + " ] ") }
|
||||
if($strAssign.Trim().StartsWith("[") -eq $false)
|
||||
{
|
||||
$strAssign = (" [ " + $strAssign + " ] ")
|
||||
}
|
||||
$json = ($json + $strAssign + "}")
|
||||
|
||||
if($json)
|
||||
@@ -1560,7 +1561,7 @@ function Add-GraphDependencyObjects
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Log "Export folder for depndency $dep not found" 2
|
||||
Write-Log "Export folder for dependency $dep not found" 2
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1626,10 +1627,10 @@ function Export-GraphObjects
|
||||
# Export all
|
||||
$objectsToExport = $global:dgObjects.ItemsSource
|
||||
}
|
||||
elseif(($global:dgObjects.ItemsSource | Where IsSelected).Count -gt 0)
|
||||
elseif(($global:dgObjects.ItemsSource | Where IsSelected -eq $true).Count -gt 0)
|
||||
{
|
||||
# Export checked items
|
||||
$objectsToExport += ($global:dgObjects.ItemsSource | Where IsSelected)
|
||||
$objectsToExport += ($global:dgObjects.ItemsSource | Where IsSelected -eq $true)
|
||||
}
|
||||
elseif($global:dgObjects.SelectedItem)
|
||||
{
|
||||
@@ -1880,7 +1881,7 @@ function Show-GraphObjectInfo
|
||||
|
||||
Add-XamlEvent $script:detailsForm "btnCopy" "Add_Click" -scriptBlock ([scriptblock]{
|
||||
$tmp = $script:detailsForm.FindName("txtValue")
|
||||
if($tmp.Text) { $tmp.Text | Clip }
|
||||
if($tmp.Text) { $tmp.Text | Set-Clipboard }
|
||||
})
|
||||
|
||||
Add-XamlEvent $script:detailsForm "btnFull" "Add_Click" -scriptBlock ([scriptblock]{
|
||||
|
||||
Reference in New Issue
Block a user