3.1 changes

This commit is contained in:
Mikael Karlsson
2021-06-08 19:02:25 +10:00
parent c7f8cbe760
commit 18533494b2
318 changed files with 163882 additions and 137 deletions

489
Extensions/Compare.psm1 Normal file
View 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
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"))
{

View File

@@ -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]{