3.1.3
Bulk Compare, Bulk Copy + bug fixes
This commit is contained in:
@@ -11,13 +11,102 @@ Objects can be compared based on Properties or Documentatation info.
|
||||
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'1.0.1'
|
||||
'1.0.2'
|
||||
}
|
||||
|
||||
function Invoke-InitializeModule
|
||||
{
|
||||
# Make sure we add the default Output types
|
||||
Add-OutputType
|
||||
$global:comparisonTypes = $null
|
||||
$global:compareProviders = $null
|
||||
$script:CompareProviderOptionsCache = $null
|
||||
|
||||
$script:defaultCompareProps = [Collections.Generic.List[String]]@('ObjectName', 'Id', 'Type', 'Category', 'SubCategory', 'Property', 'Value1', 'Value2', 'Match')
|
||||
|
||||
# Make sure we add the default providers
|
||||
Add-CompareProvider
|
||||
Add-ComparisonTypes
|
||||
|
||||
$script:saveType = @(
|
||||
[PSCustomObject]@{
|
||||
Name="One file for each object type"
|
||||
Value="objectType"
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Name="One file for all objects"
|
||||
Value="all"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function Add-CompareProvider
|
||||
{
|
||||
param($compareProvider)
|
||||
|
||||
if(-not $global:compareProviders)
|
||||
{
|
||||
$global:compareProviders = @()
|
||||
}
|
||||
|
||||
if($global:compareProviders.Count -eq 0)
|
||||
{
|
||||
$global:compareProviders += [PSCustomObject]@{
|
||||
Name = "Exported File"
|
||||
Value = "export"
|
||||
ObjectCompare = { Compare-ObjectsBasedonProperty @args }
|
||||
BulkCompare = { Start-BulkCompareExportObjects @args }
|
||||
ProviderOptions = "CompareExportOptions"
|
||||
Activate = { Invoke-ActivateCompareExportObjects @args }
|
||||
}
|
||||
|
||||
$global:compareProviders += [PSCustomObject]@{
|
||||
Name = "Named Objects"
|
||||
Value = "name"
|
||||
BulkCompare = { Start-BulkCompareNamedObjects @args }
|
||||
ProviderOptions = "CompareNamedOptions"
|
||||
Activate = { Invoke-ActivateCompareNamesObjects @args }
|
||||
RemoveProperties = @("Id")
|
||||
}
|
||||
|
||||
$global:compareProviders += [PSCustomObject]@{
|
||||
Name = "Existing objects"
|
||||
Value = "existing"
|
||||
Compare = { Compare-ObjectsBasedonDocumentation @args }
|
||||
}
|
||||
}
|
||||
|
||||
if(!$compareProvider) { return }
|
||||
|
||||
$global:compareProviders += $compareProvider
|
||||
}
|
||||
|
||||
function Add-ComparisonTypes
|
||||
{
|
||||
param($comparisonType)
|
||||
|
||||
if(-not $global:comparisonTypes)
|
||||
{
|
||||
$global:comparisonTypes = @()
|
||||
}
|
||||
|
||||
if($global:comparisonTypes.Count -eq 0)
|
||||
{
|
||||
$global:comparisonTypes += [PSCustomObject]@{
|
||||
Name = "Property"
|
||||
Value = "property"
|
||||
Compare = { Compare-ObjectsBasedonProperty @args }
|
||||
RemoveProperties = @('Category','SubCategory')
|
||||
}
|
||||
|
||||
$global:comparisonTypes += [PSCustomObject]@{
|
||||
Name = "Documentation"
|
||||
Value = "doc"
|
||||
Compare = { Compare-ObjectsBasedonDocumentation @args }
|
||||
}
|
||||
}
|
||||
|
||||
if(!$comparisonType) { return }
|
||||
|
||||
$global:comparisonTypes += $comparisonType
|
||||
}
|
||||
|
||||
function Invoke-ShowMainWindow
|
||||
@@ -42,6 +131,510 @@ function Invoke-ShowMainWindow
|
||||
$global:spSubMenu.Children.Insert(0, $button)
|
||||
}
|
||||
|
||||
function Invoke-ViewActivated
|
||||
{
|
||||
if($global:currentViewObject.ViewInfo.ID -ne "IntuneGraphAPI") { return }
|
||||
|
||||
$tmp = $mnuMain.Items | Where Name -eq "EMBulk"
|
||||
if($tmp)
|
||||
{
|
||||
$tmp.AddChild(([System.Windows.Controls.Separator]::new())) | Out-Null
|
||||
$subItem = [System.Windows.Controls.MenuItem]::new()
|
||||
$subItem.Header = "_Compare"
|
||||
$subItem.Add_Click({Show-CompareBulkForm})
|
||||
$tmp.AddChild($subItem)
|
||||
}
|
||||
}
|
||||
|
||||
function Show-CompareBulkForm
|
||||
{
|
||||
$script:form = Get-XamlObject ($global:AppRootFolder + "\Xaml\BulkCompare.xaml") -AddVariables
|
||||
if(-not $script:form) { return }
|
||||
|
||||
$global:cbCompareProvider.ItemsSource = @(($global:compareProviders | Where BulkCompare -ne $null))
|
||||
$global:cbCompareProvider.SelectedValue = (Get-Setting "Compare" "Provider" "export")
|
||||
|
||||
$global:cbCompareSave.ItemsSource = @($script:saveType)
|
||||
$global:cbCompareSave.SelectedValue = (Get-Setting "Compare" "SaveType" "objectType")
|
||||
|
||||
$global:cbCompareType.ItemsSource = $global:comparisonTypes | Where ShowOnBulk -ne $false
|
||||
$global:cbCompareType.SelectedValue = (Get-Setting "Compare" "Type" "property")
|
||||
|
||||
$script:compareObjects = @()
|
||||
foreach($objType in $global:lstMenuItems.ItemsSource)
|
||||
{
|
||||
if(-not $objType.Title) { continue }
|
||||
|
||||
$script:compareObjects += New-Object PSObject -Property @{
|
||||
Title = $objType.Title
|
||||
Selected = $true
|
||||
ObjectType = $objType
|
||||
}
|
||||
}
|
||||
|
||||
$column = Get-GridCheckboxColumn "Selected"
|
||||
$global:dgObjectsToCompare.Columns.Add($column)
|
||||
|
||||
$column.Header.IsChecked = $true # All items are checked by default
|
||||
$column.Header.add_Click({
|
||||
foreach($item in $global:dgObjectsToCompare.ItemsSource)
|
||||
{
|
||||
$item.Selected = $this.IsChecked
|
||||
}
|
||||
$global:dgObjectsToCompare.Items.Refresh()
|
||||
}
|
||||
)
|
||||
|
||||
# Add Object type column
|
||||
$binding = [System.Windows.Data.Binding]::new("Title")
|
||||
$column = [System.Windows.Controls.DataGridTextColumn]::new()
|
||||
$column.Header = "Object type"
|
||||
$column.IsReadOnly = $true
|
||||
$column.Binding = $binding
|
||||
$global:dgObjectsToCompare.Columns.Add($column)
|
||||
|
||||
$global:dgObjectsToCompare.ItemsSource = $script:compareObjects
|
||||
|
||||
Add-XamlEvent $script:form "btnClose" "add_click" {
|
||||
$script:form = $null
|
||||
Show-ModalObject
|
||||
}
|
||||
|
||||
Add-XamlEvent $script:form "btnStartCompare" "add_click" {
|
||||
Write-Status "Compare objects"
|
||||
Save-Setting "Compare" "Provider" $global:cbCompareProvider.SelectedValue
|
||||
Save-Setting "Compare" "Type" $global:cbCompareType.SelectedValue
|
||||
if($global:cbCompareProvider.SelectedItem.BulkCompare)
|
||||
{
|
||||
& $global:cbCompareProvider.SelectedItem.BulkCompare
|
||||
}
|
||||
Write-Status ""
|
||||
}
|
||||
|
||||
$global:cbCompareProvider.Add_SelectionChanged({
|
||||
Set-CompareProviderOptions $this
|
||||
})
|
||||
|
||||
Set-CompareProviderOptions $global:cbCompareProvider
|
||||
|
||||
Show-ModalForm "Bulk Compare Objects" $script:form -HideButtons
|
||||
}
|
||||
|
||||
function Set-CompareProviderOptions
|
||||
{
|
||||
param($control)
|
||||
|
||||
$providerOptions = $null
|
||||
$firstTime = $false
|
||||
if($control.SelectedItem.ProviderOptions)
|
||||
{
|
||||
if($script:CompareProviderOptionsCache -isnot [Hashtable]) { $script:CompareProviderOptionsCache = @{} }
|
||||
if($script:CompareProviderOptionsCache.Keys -contains $control.SelectedValue)
|
||||
{
|
||||
$providerOptions = $script:CompareProviderOptionsCache[$control.SelectedValue]
|
||||
}
|
||||
else
|
||||
{
|
||||
$providerOptions = Get-XamlObject ($global:AppRootFolder + "\Xaml\$($control.SelectedItem.ProviderOptions).xaml") -AddVariables
|
||||
if($providerOptions)
|
||||
{
|
||||
$firstTime = $true
|
||||
$script:CompareProviderOptionsCache.Add($control.SelectedValue, $providerOptions)
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Log "Failed to create options for $($control.SelectedItem.Name)" 3
|
||||
}
|
||||
}
|
||||
$global:ccContentProviderOptions.Content = $providerOptions
|
||||
}
|
||||
else
|
||||
{
|
||||
$global:ccContentProviderOptions.Content = $null
|
||||
}
|
||||
$global:ccContentProviderOptions.Visibility = (?: ($global:ccContentProviderOptions.Content -eq $null) "Collapsed" "Visible")
|
||||
|
||||
if($control.SelectedItem.Activate)
|
||||
{
|
||||
if($firstTime)
|
||||
{
|
||||
Write-Log "Initialize $($global:cbCompareProvider.SelectedItem.Name) provider options"
|
||||
}
|
||||
|
||||
& $control.SelectedItem.Activate $providerOptions $firstTime
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-ActivateCompareExportObjects
|
||||
{
|
||||
param($providerOptions, $firstTime)
|
||||
|
||||
if($firstTime)
|
||||
{
|
||||
$path = Get-Setting "" "LastUsedFullPath"
|
||||
if($path)
|
||||
{
|
||||
$path = [IO.Directory]::GetParent($path).FullName
|
||||
}
|
||||
Set-XamlProperty $providerOptions "txtExportPath" "Text" (?? $path (Get-SettingValue "RootFolder"))
|
||||
|
||||
Add-XamlEvent $providerOptions "browseExportPath" "add_click" ({
|
||||
$folder = Get-Folder (Get-XamlProperty $this.Parent "txtExportPath" "Text") "Select root folder for compare"
|
||||
if($folder)
|
||||
{
|
||||
Set-XamlProperty $this.Parent "txtExportPath" "Text" $folder
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-ActivateCompareNamesObjects
|
||||
{
|
||||
param($providerOptions, $firstTime)
|
||||
|
||||
if($providerOptions -and $firstTime)
|
||||
{
|
||||
Set-XamlProperty $providerOptions "txtCompareSource" "Text" (Get-Setting "Compare" "CompareSource" "")
|
||||
Set-XamlProperty $providerOptions "txtCompareWith" "Text" (Get-Setting "Compare" "CompareWith" "")
|
||||
|
||||
Set-XamlProperty $providerOptions "txtSavePath" "Text" (Get-Setting "Compare" "SavePath" "")
|
||||
Add-XamlEvent $providerOptions "browseSavePath" "add_click" ({
|
||||
$folder = Get-Folder (Get-XamlProperty $this.Parent "txtSavePath" "Text") "Select folder"
|
||||
if($folder)
|
||||
{
|
||||
Set-XamlProperty $this.Parent "txtSavePath" "Text" $folder
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function Start-BulkCompareNamedObjects
|
||||
{
|
||||
Write-Log "****************************************************************"
|
||||
Write-Log "Start bulk Named Objects compare"
|
||||
Write-Log "****************************************************************"
|
||||
$compareObjectsResult = @()
|
||||
|
||||
$compareSource = (Get-XamlProperty $global:ccContentProviderOptions.Content "txtCompareSource" "Text")
|
||||
$compareWith = (Get-XamlProperty $global:ccContentProviderOptions.Content "txtCompareWith" "Text")
|
||||
|
||||
if(-not $compareSource -or -not $compareWith)
|
||||
{
|
||||
[System.Windows.MessageBox]::Show("Both source and compare name patterns must be specified", "Error", "OK", "Error")
|
||||
return
|
||||
}
|
||||
|
||||
Save-Setting "Compare" "CompareSource" $compareSource
|
||||
Save-Setting "Compare" "CompareWith" $compareWith
|
||||
|
||||
Invoke-BulkCompareNamedObjects $compareSource $compareWith
|
||||
|
||||
Write-Log "****************************************************************"
|
||||
Write-Log "Bulk compare Named Objects finished"
|
||||
Write-Log "****************************************************************"
|
||||
Write-Status ""
|
||||
}
|
||||
|
||||
function Invoke-BulkCompareNamedObjects
|
||||
{
|
||||
param($sourcePattern, $comparePattern)
|
||||
|
||||
$outputType = $global:cbCompareSave.SelectedValue
|
||||
|
||||
Save-Setting "Compare" "SaveType" $outputType
|
||||
|
||||
$compResultValues = @()
|
||||
$compareObjectsResult = @()
|
||||
|
||||
$outputFolder = (Get-XamlProperty $global:ccContentProviderOptions.Content "txtSavePath" "Text")
|
||||
if(-not $outputFolder)
|
||||
{
|
||||
$outputFolder = Expand-FileName "%MyDocuments%"
|
||||
}
|
||||
|
||||
$compareProps = $script:defaultCompareProps
|
||||
|
||||
foreach($removeProp in $global:cbCompareProvider.SelectedItem.RemoveProperties)
|
||||
{
|
||||
$compareProps.Remove($removeProp) | Out-Null
|
||||
}
|
||||
|
||||
foreach($removeProp in $global:cbCompareType.SelectedItem.RemoveProperties)
|
||||
{
|
||||
$compareProps.Remove($removeProp) | Out-Null
|
||||
}
|
||||
|
||||
foreach($item in ($global:dgObjectsToCompare.ItemsSource | where Selected -eq $true))
|
||||
{
|
||||
Write-Status "Compare $($item.ObjectType.Title) objects" -Force -SkipLog
|
||||
Write-Log "----------------------------------------------------------------"
|
||||
Write-Log "Compare $($item.ObjectType.Title) objects"
|
||||
Write-Log "----------------------------------------------------------------"
|
||||
|
||||
$url = $item.ObjectType.API
|
||||
if($item.ObjectType.QUERYLIST)
|
||||
{
|
||||
$url = "$($url.Trim())?$($item.ObjectType.QUERYLIST.Trim())"
|
||||
}
|
||||
|
||||
$graphObjects = @(Get-GraphObjects -Url $url -property $item.ObjectType.ViewProperties -objectType $item.ObjectType)
|
||||
|
||||
$nameProp = ?? $item.ObjectType.NameProperty "displayName"
|
||||
|
||||
foreach($graphObj in ($graphObjects | Where { $_.Object."$($nameProp)" -imatch [regex]::Escape($sourcePattern) }))
|
||||
{
|
||||
$sourceName = $graphObj.Object."$($nameProp)"
|
||||
$compareName = $sourceName -ireplace [regex]::Escape($sourcePattern),$comparePattern
|
||||
|
||||
$compareObj = $graphObjects | Where { $_.Object."$($nameProp)" -eq $compareName -and $_.Object.'@OData.Type' -eq $graphObj.Object.'@OData.Type' }
|
||||
|
||||
if(($compareObj | measure).Count -gt 1)
|
||||
{
|
||||
Write-Log "Multiple objects found with name $compareName. Compare will not be performed" 2
|
||||
continue
|
||||
}
|
||||
elseif($compareObj)
|
||||
{
|
||||
$sourceObj = Get-GraphObject $graphObj.Object $graphObj.ObjectType
|
||||
$compareObj = Get-GraphObject $compareObj.Object $compareObj.ObjectType
|
||||
$compareProperties = Compare-Objects $sourceObj.Object $compareObj.Object $sourceObj.ObjectType
|
||||
}
|
||||
else
|
||||
{
|
||||
# Add objects that are exported but deleted
|
||||
Write-Log "Object '$((Get-GraphObjectName $graphObj.Object $graphObj.ObjectType))' with id $($graphObj.Object.Id) has no matching object with the compate pattern" 2
|
||||
$compareProperties = @([PSCustomObject]@{
|
||||
Object1Value = (Get-GraphObjectName $graphObj.Object $graphObj.ObjectType)
|
||||
Object2Value = $null
|
||||
Match = $false
|
||||
})
|
||||
}
|
||||
|
||||
$compareObjectsResult += [PSCustomObject]@{
|
||||
Object1 = $sourceObj.Object
|
||||
Object2 = $compareObj.Object
|
||||
ObjectType = $item.ObjectType
|
||||
Id = $sourceObj.Object.Id
|
||||
Result = $compareProperties
|
||||
}
|
||||
}
|
||||
|
||||
if($outputType -eq "objectType")
|
||||
{
|
||||
$compResultValues = @()
|
||||
}
|
||||
|
||||
foreach($compObj in @($compareObjectsResult | Where { $_.ObjectType.Id -eq $item.ObjectType.Id }))
|
||||
{
|
||||
$objName = Get-GraphObjectName (?? $compObj.Object1 $compObj.Object2) $item.ObjectType
|
||||
foreach($compValue in $compObj.Result)
|
||||
{
|
||||
$compResultValues += [PSCustomObject]@{
|
||||
ObjectName = $objName
|
||||
Id = $compObj.Id
|
||||
Type = $compObj.ObjectType.Title
|
||||
ODataType = $compObj.Object1.'@OData.Type'
|
||||
Property = $compValue.PropertyName
|
||||
Value1 = $compValue.Object1Value
|
||||
Value2 = $compValue.Object2Value
|
||||
Category = $compValue.Category
|
||||
SubCategory = $compValue.SubCategory
|
||||
Match = $compValue.Match
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($outputType -eq "objectType")
|
||||
{
|
||||
$fileName = Remove-InvalidFileNameChars (Expand-FileName "Compare-$($graphObj.ObjectType.Id)-$sourcePattern-$comparePattern-%DateTime%.csv")
|
||||
Save-BulkCompareResults $compResultValues (Join-Path $outputFolder $fileName) $compareProps
|
||||
}
|
||||
}
|
||||
#$fileName = Expand-FileName $fileName
|
||||
|
||||
if($compareObjectsResult.Count -eq 0)
|
||||
{
|
||||
[System.Windows.MessageBox]::Show("No objects were comparced. Verify name patterns", "Error", "OK", "Error")
|
||||
}
|
||||
elseif($outputType -eq "all")
|
||||
{
|
||||
$fileName = Remove-InvalidFileNameChars (Expand-FileName "Compare-$sourcePattern-$comparePattern-%DateTime%.csv")
|
||||
Save-BulkCompareResults $compResultValues (Join-Path $outputFolder $fileName) $compareProps
|
||||
}
|
||||
}
|
||||
|
||||
function Start-BulkCompareExportObjects
|
||||
{
|
||||
Write-Log "****************************************************************"
|
||||
Write-Log "Start bulk Exported Objects compare"
|
||||
Write-Log "****************************************************************"
|
||||
$compareObjectsResult = @()
|
||||
$rootFolder = (Get-XamlProperty $global:ccContentProviderOptions.Content "txtExportPath" "Text")
|
||||
|
||||
$compareProps = $script:defaultCompareProps
|
||||
|
||||
foreach($removeProp in $global:cbCompareProvider.SelectedItem.RemoveProperties)
|
||||
{
|
||||
$compareProps.Remove($removeProp) | Out-Null
|
||||
}
|
||||
|
||||
foreach($removeProp in $global:cbCompareType.SelectedItem.RemoveProperties)
|
||||
{
|
||||
$compareProps.Remove($removeProp) | Out-Null
|
||||
}
|
||||
|
||||
if(-not $rootFolder)
|
||||
{
|
||||
[System.Windows.MessageBox]::Show("Root folder must be specified", "Error", "OK", "Error")
|
||||
return
|
||||
}
|
||||
|
||||
if([IO.Directory]::Exists($rootFolder) -eq $false)
|
||||
{
|
||||
[System.Windows.MessageBox]::Show("Root folder $rootFolder does not exist", "Error", "OK", "Error")
|
||||
return
|
||||
}
|
||||
|
||||
$outputType = $global:cbCompareSave.SelectedValue
|
||||
Save-Setting "Compare" "SaveType" $outputType
|
||||
|
||||
$compResultValues = @()
|
||||
|
||||
foreach($item in ($global:dgObjectsToCompare.ItemsSource | where Selected -eq $true))
|
||||
{
|
||||
Write-Status "Compare $($item.ObjectType.Title) objects" -Force -SkipLog
|
||||
Write-Log "----------------------------------------------------------------"
|
||||
Write-Log "Compare $($item.ObjectType.Title) objects"
|
||||
Write-Log "----------------------------------------------------------------"
|
||||
|
||||
$folder = Join-Path $rootFolder $item.ObjectType.Id
|
||||
|
||||
if([IO.Directory]::Exists($folder))
|
||||
{
|
||||
Save-Setting "" "LastUsedFullPath" $folder
|
||||
|
||||
$url = $item.ObjectType.API
|
||||
if($item.ObjectType.QUERYLIST)
|
||||
{
|
||||
$url = "$($url.Trim())?$($item.ObjectType.QUERYLIST.Trim())"
|
||||
}
|
||||
|
||||
$graphObjects = @(Get-GraphObjects -Url $url -property $item.ObjectType.ViewProperties -objectType $item.ObjectType)
|
||||
|
||||
foreach ($fileObj in @(Get-GraphFileObjects $folder -ObjectType $item.ObjectType))
|
||||
{
|
||||
if(-not $fileObj.Object.Id)
|
||||
{
|
||||
Write-Log "Object from file '$($fileObj.FullName)' has no Id property. Compare not supported" 2
|
||||
continue
|
||||
}
|
||||
$curObject = $graphObjects | Where { $_.Object.Id -eq $fileObj.Object.Id }
|
||||
|
||||
if(-not $curObject)
|
||||
{
|
||||
# Add objects that are exported but deleted
|
||||
Write-Log "Object '$((Get-GraphObjectName $fileObj.Object $fileObj.ObjectType))' with id $($fileObj.Object.Id) not found in Intune. Deleted?" 2
|
||||
$compareProperties = @([PSCustomObject]@{
|
||||
Object1Value = $null
|
||||
Object2Value = (Get-GraphObjectName $fileObj.Object $item.ObjectType)
|
||||
Match = $false
|
||||
})
|
||||
}
|
||||
else
|
||||
{
|
||||
$sourceObj = Get-GraphObject $curObject.Object $curObject.ObjectType
|
||||
$compareProperties = Compare-Objects $sourceObj.Object $fileObj.Object $item.ObjectType
|
||||
}
|
||||
|
||||
$compareObjectsResult += [PSCustomObject]@{
|
||||
Object1 = $curObject.Object
|
||||
Object2 = $fileObj.Object
|
||||
ObjectType = $item.ObjectType
|
||||
Id = $fileObj.Object.Id
|
||||
Result = $compareProperties
|
||||
}
|
||||
}
|
||||
|
||||
foreach($graphObj in $graphObjects)
|
||||
{
|
||||
# Add objects that are not exported
|
||||
if(($compareObjectsResult | Where { $_.Id -eq $graphObj.Id})) { continue }
|
||||
|
||||
$compareObjectsResult += [PSCustomObject]@{
|
||||
Object1 = $curObject.Object
|
||||
Object2 = $null
|
||||
ObjectType = $item.ObjectType
|
||||
Id = $graphObj.Id
|
||||
Result = @([PSCustomObject]@{
|
||||
Object1Value = (Get-GraphObjectName $graphObj.Object $item.ObjectType)
|
||||
Object2Value = $null
|
||||
Match = $false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if($outputType -eq "objectType")
|
||||
{
|
||||
$compResultValues = @()
|
||||
}
|
||||
|
||||
foreach($compObj in @($compareObjectsResult | Where { $_.ObjectType.Id -eq $item.ObjectType.Id }))
|
||||
{
|
||||
$objName = Get-GraphObjectName (?? $compObj.Object1 $compObj.Object2) $item.ObjectType
|
||||
foreach($compValue in $compObj.Result)
|
||||
{
|
||||
$compResultValues += [PSCustomObject]@{
|
||||
ObjectName = $objName
|
||||
Id = $compObj.Id
|
||||
Type = $compObj.ObjectType.Title
|
||||
ODataType = $compObj.Object1.'@OData.Type'
|
||||
Property = $compValue.PropertyName
|
||||
Value1 = $compValue.Object1Value
|
||||
Value2 = $compValue.Object2Value
|
||||
Category = $compValue.Category
|
||||
SubCategory = $compValue.SubCategory
|
||||
Match = $compValue.Match
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($outputType -eq "objectType")
|
||||
{
|
||||
Save-BulkCompareResults $compResultValues (Join-Path $rootFolder "Compare_$(((Get-Date).ToString("yyyyMMdd-HHmm"))).csv") $compareProps
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Log "Folder $folder not found. Skipping import" 2
|
||||
}
|
||||
}
|
||||
|
||||
if($outputType -eq "all" -and $compResultValues.Count -gt 0)
|
||||
{
|
||||
Save-BulkCompareResults $compResultValues (Join-Path $folder "Compare_$(((Get-Date).ToString("yyyyMMDD-HHmm"))).csv") $compareProps
|
||||
}
|
||||
|
||||
Write-Log "****************************************************************"
|
||||
Write-Log "Bulk compare Exported Objects finished"
|
||||
Write-Log "****************************************************************"
|
||||
Write-Status ""
|
||||
if($compareObjectsResult.Count -eq 0)
|
||||
{
|
||||
[System.Windows.MessageBox]::Show("No objects were comparced. Verify folder and exported files", "Error", "OK", "Error")
|
||||
}
|
||||
}
|
||||
|
||||
function Save-BulkCompareResults
|
||||
{
|
||||
param($compResultValues, $file, $props)
|
||||
|
||||
if($compResultValues.Count -gt 0)
|
||||
{
|
||||
Write-Log "Save bulk comare results to $file"
|
||||
$compResultValues | Select -Property $props | ConvertTo-Csv -NoTypeInformation | Out-File $file -Force -Encoding UTF8
|
||||
}
|
||||
}
|
||||
|
||||
function Show-CompareForm
|
||||
{
|
||||
param($objInfo)
|
||||
@@ -49,12 +642,15 @@ function Show-CompareForm
|
||||
$script:cmpForm = Get-XamlObject ($global:AppRootFolder + "\Xaml\CompareForm.xaml") -AddVariables
|
||||
if(-not $script:cmpForm) { return }
|
||||
|
||||
$script:cmpForm.Tag = $objInfo
|
||||
|
||||
$script:copareSource = $objInfo
|
||||
|
||||
$global:cbCompareType.ItemsSource = ("[ { Name: `"Property`",Value: `"property`" }, { Name: `"Documentation`",Value: `"doc`" }]" | ConvertFrom-Json)
|
||||
$global:cbCompareType.ItemsSource = $global:comparisonTypes | Where ShowOnObject -ne $false
|
||||
$global:cbCompareType.SelectedValue = (Get-Setting "Compare" "Type" "property")
|
||||
|
||||
$global:txtIntuneObject.Text = (Get-GraphObjectName $objInfo.Object $objInfo.ObjectType)
|
||||
$global:txtIntuneObject.Tag = $objInfo
|
||||
|
||||
Add-XamlEvent $script:cmpForm "btnClose" "add_click" {
|
||||
$script:cmpForm = $null
|
||||
@@ -65,7 +661,7 @@ function Show-CompareForm
|
||||
Write-Status "Compare objects"
|
||||
Save-Setting "Compare" "Type" $global:cbCompareType.SelectedValue
|
||||
$script:currentObjName = ""
|
||||
Invoke-CompareObjects
|
||||
Start-CompareExportObject
|
||||
Write-Status ""
|
||||
}
|
||||
|
||||
@@ -80,22 +676,42 @@ function Show-CompareForm
|
||||
$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 = Get-CompareCsvInfo $global:dgCompareInfo.ItemsSource $script:cmpForm.Tag
|
||||
$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
|
||||
|
||||
(Get-CompareCsvInfo $global:dgCompareInfo.ItemsSource $script:cmpForm.Tag) | Set-Clipboard
|
||||
}
|
||||
|
||||
Add-XamlEvent $script:cmpForm "browseCompareObject" "add_click" {
|
||||
|
||||
$path = Get-Setting "" "LastUsedFullPath"
|
||||
if($path)
|
||||
{
|
||||
$path = [IO.Directory]::GetParent($path).FullName
|
||||
if($global:txtIntuneObject.Tag.ObjectType)
|
||||
{
|
||||
$objectTypePath = [IO.Path]::Combine($path, $global:txtIntuneObject.Tag.ObjectType.Id)
|
||||
if([IO.Direcotry]::Exists($objectTypePath))
|
||||
{
|
||||
$path = $objectTypePath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$path = (?: ($global:lastCompareFile -eq $null) $path ([IO.FileInfo]$global:lastCompareFile).DirectoryName)
|
||||
|
||||
$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($path)
|
||||
{
|
||||
$of.InitialDirectory = $path
|
||||
}
|
||||
|
||||
if($of.ShowDialog())
|
||||
{
|
||||
Set-XamlProperty $script:cmpForm "txtCompareFile" "Text" $of.FileName
|
||||
@@ -110,18 +726,55 @@ function Show-CompareForm
|
||||
Show-ModalForm "Compare Intune Objects" $script:cmpForm -HideButtons
|
||||
}
|
||||
|
||||
function Invoke-CompareObjects
|
||||
function Get-CompareCsvInfo
|
||||
{
|
||||
param($comareInfo, $objInfo)
|
||||
|
||||
$compResultValues = @()
|
||||
$objName = Get-GraphObjectName $objInfo.Object $objInfo.ObjectType
|
||||
foreach($compValue in $comareInfo)
|
||||
{
|
||||
$compResultValues += [PSCustomObject]@{
|
||||
ObjectName = $objName
|
||||
Id = $objInfo.Object.Id
|
||||
Type = $objInfo.ObjectType.Title
|
||||
ODataType = $objInfo.Object.'@OData.Type'
|
||||
Property = $compValue.PropertyName
|
||||
Value1 = $compValue.Object1Value
|
||||
Value2 = $compValue.Object2Value
|
||||
Category = $compValue.Category
|
||||
SubCategory = $compValue.SubCategory
|
||||
Match = $compValue.Match
|
||||
}
|
||||
}
|
||||
|
||||
$compareProps = $script:defaultCompareProps
|
||||
|
||||
# !!! Not supported yet
|
||||
#foreach($removeProp in $global:cbCompareProvider.SelectedItem.RemoveProperties)
|
||||
#{
|
||||
# $compareProps.Remove($removeProp) | Out-Null
|
||||
#}
|
||||
|
||||
foreach($removeProp in $global:cbCompareType.SelectedItem.RemoveProperties)
|
||||
{
|
||||
$compareProps.Remove($removeProp) | Out-Null
|
||||
}
|
||||
$compResultValues | Select -Property $compareProps | ConvertTo-Csv -NoTypeInformation
|
||||
}
|
||||
|
||||
function Start-CompareExportObject
|
||||
{
|
||||
if(-not $script:copareSource) { return }
|
||||
|
||||
if(-not $global:txtCompareFile.Text)
|
||||
{
|
||||
[System.Windows.MessageBox]::Show("No file selected", "Comapre", "OK", "Error")
|
||||
[System.Windows.MessageBox]::Show("No file selected", "Compare", "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")
|
||||
[System.Windows.MessageBox]::Show("File '$($global:txtCompareFile.Text)' not found", "Compare", "OK", "Error")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -138,7 +791,7 @@ function Invoke-CompareObjects
|
||||
}
|
||||
catch
|
||||
{
|
||||
[System.Windows.MessageBox]::Show("Failed to convert json file '$($global:txtCompareFile.Text)'", "Comapre", "OK", "Error")
|
||||
[System.Windows.MessageBox]::Show("Failed to convert json file '$($global:txtCompareFile.Text)'", "Compare", "OK", "Error")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -148,24 +801,45 @@ function Invoke-CompareObjects
|
||||
|
||||
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")
|
||||
if(([System.Windows.MessageBox]::Show("The object types does not match.`n`nDo you to compare the objects?", "Compare", "YesNo", "Warning")) -eq "No")
|
||||
{
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$compareResult = Compare-Objects $obj.Object $compareObj $obj.ObjectType
|
||||
|
||||
$global:dgCompareInfo.ItemsSource = $compareResult
|
||||
}
|
||||
|
||||
function Compare-Objects
|
||||
{
|
||||
param($obj1, $obj2, $objectType)
|
||||
|
||||
$script:compareProperties = @()
|
||||
|
||||
if($global:cbCompareType.SelectedValue -eq "property")
|
||||
if($global:cbCompareType.SelectedItem.Compare)
|
||||
{
|
||||
Compare-ObjectsBasedonProperty $obj.Object $compareObj $obj.ObjectType
|
||||
$compareResult = & $global:cbCompareType.SelectedItem.Compare $obj1 $obj2 $objectType
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Log "Selected comparison type ($($global:cbCompareType.SelectedItem.Name)) does not have a Compare property specified" 3
|
||||
}
|
||||
<#
|
||||
elseif($global:cbCompareType.SelectedValue -eq "property")
|
||||
{
|
||||
$compareResult = Compare-ObjectsBasedonProperty $obj1 $obj2 $objectType
|
||||
}
|
||||
elseif($global:cbCompareType.SelectedValue -eq "doc")
|
||||
{
|
||||
Compare-ObjectsBasedonDocumentation $obj $compareObj
|
||||
$compareResult = Compare-ObjectsBasedonDocumentation $obj1 $obj2 $objectType
|
||||
}
|
||||
$global:dgCompareInfo.ItemsSource = $script:compareProperties
|
||||
#>
|
||||
|
||||
$compareResult
|
||||
}
|
||||
|
||||
function Set-ColumnVisibility
|
||||
{
|
||||
param($showCategory = $false, $showSubCategory = $false)
|
||||
@@ -208,7 +882,7 @@ function Compare-ObjectsBasedonProperty
|
||||
{
|
||||
param($obj1, $obj2, $objectType)
|
||||
|
||||
Write-Status "Compare properties"
|
||||
Write-Status "Compare objects based on property values"
|
||||
|
||||
Set-ColumnVisibility $false
|
||||
|
||||
@@ -262,14 +936,16 @@ function Compare-ObjectsBasedonProperty
|
||||
$val1 = ($obj1.$propName | ConvertTo-Json -Depth 10)
|
||||
$val2 = ($obj2.$propName | ConvertTo-Json -Depth 10)
|
||||
Add-CompareProperty $propName $val1 $val2
|
||||
}
|
||||
}
|
||||
|
||||
$script:compareProperties
|
||||
}
|
||||
|
||||
function Get-CompareCustomColumnsDoc
|
||||
{
|
||||
param($objInfo)
|
||||
param($obj)
|
||||
|
||||
if($objInfo.Object.'@OData.Type' -eq "#microsoft.graph.deviceEnrollmentPlatformRestrictionsConfiguration")
|
||||
if($obj.'@OData.Type' -eq "#microsoft.graph.deviceEnrollmentPlatformRestrictionsConfiguration")
|
||||
{
|
||||
Set-ColumnVisibility $true $true
|
||||
}
|
||||
@@ -281,23 +957,34 @@ function Get-CompareCustomColumnsDoc
|
||||
|
||||
function Compare-ObjectsBasedonDocumentation
|
||||
{
|
||||
param($obj1, $obj2)
|
||||
param($obj1, $obj2, $objectType)
|
||||
|
||||
Get-CompareCustomColumnsDoc $obj
|
||||
Write-Status "Compare objects based on documentation values"
|
||||
|
||||
Get-CompareCustomColumnsDoc $obj1
|
||||
|
||||
# ToDo: set this based on configuration value
|
||||
$script:assignmentOutput = "simpleFullCompare"
|
||||
|
||||
$docObj1 = Invoke-ObjectDocumentation $obj1
|
||||
$docObj1 = Invoke-ObjectDocumentation ([PSCustomObject]@{
|
||||
Object = $obj1
|
||||
ObjectType = $objectType
|
||||
})
|
||||
|
||||
$obj2 | Add-Member Noteproperty -Name "@CompareObject" -Value $true -Force
|
||||
$obj2 | Add-Member Noteproperty -Name "@ObjectFromFile" -Value $true -Force
|
||||
|
||||
$docObj2 = Invoke-ObjectDocumentation ([PSCustomObject]@{
|
||||
Object = $obj2
|
||||
ObjectType = $obj1.ObjectType
|
||||
ObjectType = $objectType
|
||||
})
|
||||
|
||||
$settingsValue = ?? $obj1.ObjectType.CompareValue "Value"
|
||||
$settingsValue = ?? $objectType.CompareValue "Value"
|
||||
|
||||
if($docObj1.BasicInfo -and -not ($docObj1.BasicInfo | where Value -eq $obj1.Id))
|
||||
{
|
||||
# Make sure the Id property is included
|
||||
Add-CompareProperty "Id" $obj1.Id $obj2.Id $docObj1.BasicInfo[0].Category
|
||||
}
|
||||
|
||||
foreach ($prop in $docObj1.BasicInfo)
|
||||
{
|
||||
@@ -412,6 +1099,28 @@ function Compare-ObjectsBasedonDocumentation
|
||||
Add-CompareProperty $applicabilityRule.Property $val1 $val2 $applicabilityRule.Category
|
||||
}
|
||||
|
||||
$complianceActionsAdded = @()
|
||||
foreach($complianceAction in $docObj1.ComplianceActions)
|
||||
{
|
||||
$complianceAction2 = $docObj2.ComplianceActions | Where { $_.IdStr -eq $complianceAction.IdStr }
|
||||
$complianceActionsAdded += $complianceAction.IdStr
|
||||
$val1 = ($complianceAction.Action + [environment]::NewLine + $complianceAction.Schedule + [environment]::NewLine + $complianceAction.MessageTemplateId + [environment]::NewLine + $complianceAction.EmailCCIds)
|
||||
$val2 = ($complianceAction2.Action + [environment]::NewLine + $complianceAction2.Schedule + [environment]::NewLine + $complianceAction2.MessageTemplateId + [environment]::NewLine + $complianceAction2.EmailCCIds)
|
||||
|
||||
Add-CompareProperty $complianceAction.Category $val1 $val2
|
||||
}
|
||||
|
||||
foreach($complianceAction in $docObj2.ComplianceActions)
|
||||
{
|
||||
if(($complianceAction.IdStr) -in $complianceActionsAdded) { continue }
|
||||
$complianceAction2 = $docObj1.ComplianceActions | Where { $_.IdStr -eq $complianceAction.IdStr }
|
||||
$complianceActionsAdded += $complianceAction.IdStr
|
||||
$val2 = ($complianceAction.Action + [environment]::NewLine + $complianceAction.Schedule + [environment]::NewLine + $complianceAction.MessageTemplateId + [environment]::NewLine + $complianceAction.EmailCCIds)
|
||||
$val1 = ($complianceAction2.Action + [environment]::NewLine + $complianceAction2.Schedule + [environment]::NewLine + $complianceAction2.MessageTemplateId + [environment]::NewLine + $complianceAction2.EmailCCIds)
|
||||
|
||||
Add-CompareProperty $complianceAction.Category $val1 $val2
|
||||
}
|
||||
|
||||
$script:assignmentStr = Get-LanguageString "TableHeaders.assignment"
|
||||
$script:groupsAdded = @()
|
||||
|
||||
@@ -449,6 +1158,8 @@ function Compare-ObjectsBasedonDocumentation
|
||||
{
|
||||
Add-AssignmentInfo $docObj2 $docObj1 $assignment -ReversedValue
|
||||
}
|
||||
|
||||
$script:compareProperties
|
||||
}
|
||||
|
||||
function Add-AssignmentInfo
|
||||
|
||||
173
Extensions/Copy.psm1
Normal file
173
Extensions/Copy.psm1
Normal file
@@ -0,0 +1,173 @@
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'1.0.0'
|
||||
}
|
||||
|
||||
function Invoke-InitializeModule
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
function Invoke-ViewActivated
|
||||
{
|
||||
if($global:currentViewObject.ViewInfo.ID -ne "IntuneGraphAPI") { return }
|
||||
|
||||
$tmp = $mnuMain.Items | Where Name -eq "EMBulk"
|
||||
if($tmp)
|
||||
{
|
||||
$tmp.AddChild(([System.Windows.Controls.Separator]::new())) | Out-Null
|
||||
$subItem = [System.Windows.Controls.MenuItem]::new()
|
||||
$subItem.Header = "Cop_y"
|
||||
$subItem.Add_Click({Show-CopyBulkForm})
|
||||
$tmp.AddChild($subItem)
|
||||
}
|
||||
}
|
||||
|
||||
function Show-CopyBulkForm
|
||||
{
|
||||
$script:form = Get-XamlObject ($global:AppRootFolder + "\Xaml\BulkCopy.xaml") -AddVariables
|
||||
if(-not $script:form) { return }
|
||||
|
||||
$global:txtCopyFromPattern.Text = Get-Setting "Copy" "CopyFromPattern"
|
||||
$global:txtCopyToPattern.Text = Get-Setting "Copy" "CopyToPattern"
|
||||
|
||||
$script:copyObjects = @()
|
||||
foreach($objType in $global:lstMenuItems.ItemsSource)
|
||||
{
|
||||
if(-not $objType.Title) { continue }
|
||||
|
||||
$script:copyObjects += New-Object PSObject -Property @{
|
||||
Title = $objType.Title
|
||||
Selected = $true
|
||||
ObjectType = $objType
|
||||
}
|
||||
}
|
||||
|
||||
$column = Get-GridCheckboxColumn "Selected"
|
||||
$global:dgObjectsToCopy.Columns.Add($column)
|
||||
|
||||
$column.Header.IsChecked = $true # All items are checked by default
|
||||
$column.Header.add_Click({
|
||||
foreach($item in $global:dgObjectsToCopy.ItemsSource)
|
||||
{
|
||||
$item.Selected = $this.IsChecked
|
||||
}
|
||||
$global:dgObjectsToCopy.Items.Refresh()
|
||||
}
|
||||
)
|
||||
|
||||
# Add Object type column
|
||||
$binding = [System.Windows.Data.Binding]::new("Title")
|
||||
$column = [System.Windows.Controls.DataGridTextColumn]::new()
|
||||
$column.Header = "Object type"
|
||||
$column.IsReadOnly = $true
|
||||
$column.Binding = $binding
|
||||
$global:dgObjectsToCopy.Columns.Add($column)
|
||||
|
||||
$global:dgObjectsToCopy.ItemsSource = $script:copyObjects
|
||||
|
||||
Add-XamlEvent $script:form "btnClose" "add_click" {
|
||||
$script:form = $null
|
||||
Show-ModalObject
|
||||
}
|
||||
|
||||
Add-XamlEvent $script:form "btnStartCopy" "add_click" {
|
||||
Write-Status "Copy objects"
|
||||
Start-BulkCopyObjects
|
||||
Write-Status ""
|
||||
}
|
||||
|
||||
Show-ModalForm "Bulk Copy Objects" $script:form -HideButtons
|
||||
}
|
||||
|
||||
function Start-BulkCopyObjects
|
||||
{
|
||||
Write-Log "****************************************************************"
|
||||
Write-Log "Start bulk copy"
|
||||
Write-Log "****************************************************************"
|
||||
|
||||
$copyFrom = $global:txtCopyFromPattern.Text
|
||||
$copyTo = $global:txtCopyToPattern.Text
|
||||
|
||||
if(-not $copyFrom -or -not $copyTo)
|
||||
{
|
||||
[System.Windows.MessageBox]::Show("Both name patterns must be specified", "Error", "OK", "Error")
|
||||
return
|
||||
}
|
||||
|
||||
Save-Setting "Copy" "CopyFromPattern" $global:txtCopyFromPattern.Text
|
||||
Save-Setting "Copy" "CopyToPattern" $global:txtCopyToPattern.Text
|
||||
|
||||
foreach($item in ($global:dgObjectsToCopy.ItemsSource | where Selected -eq $true))
|
||||
{
|
||||
Write-Status "Copy $($item.ObjectType.Title) objects" -Force -SkipLog
|
||||
Write-Log "----------------------------------------------------------------"
|
||||
Write-Log "Copy $($item.ObjectType.Title) objects"
|
||||
Write-Log "----------------------------------------------------------------"
|
||||
|
||||
$url = $item.ObjectType.API
|
||||
if($item.ObjectType.QUERYLIST)
|
||||
{
|
||||
$url = "$($url.Trim())?$($item.ObjectType.QUERYLIST.Trim())"
|
||||
}
|
||||
|
||||
$graphObjects = @(Get-GraphObjects -Url $url -property $item.ObjectType.ViewProperties -objectType $item.ObjectType)
|
||||
|
||||
$nameProp = ?? $item.ObjectType.NameProperty "displayName"
|
||||
|
||||
foreach($graphObj in ($graphObjects | Where { $_.Object."$($nameProp)" -imatch [regex]::Escape($copyFrom) }))
|
||||
{
|
||||
$sourceName = $graphObj.Object."$($nameProp)"
|
||||
$copyName = $sourceName -ireplace [regex]::Escape($copyFrom),$copyTo
|
||||
|
||||
$copyObj = $graphObjects | Where { $_.Object."$($nameProp)" -eq $copyName -and $_.Object.'@OData.Type' -eq $graphObj.Object.'@OData.Type' }
|
||||
|
||||
if(($copyObj | measure).Count -gt 0)
|
||||
{
|
||||
Write-Log "Object with name $copyName already exists. $sourceName will not be copied" 2
|
||||
continue
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Status "Create $copyName from $sourceName" -Force
|
||||
|
||||
if($graphObj.ObjectType.PreCopyCommand)
|
||||
{
|
||||
if((& $graphObj.ObjectType.PreCopyCommand $graphObj.Object $graphObj.ObjectType $copyName))
|
||||
{
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
$copyFromObj = (Get-GraphObject $graphObj.Object $graphObj.ObjectType -SkipAssignments).Object
|
||||
|
||||
# Convert to Json and back to clone the object
|
||||
$obj = ConvertTo-Json $copyFromObj -Depth 10 | ConvertFrom-Json
|
||||
if($obj)
|
||||
{
|
||||
# Import new profile
|
||||
Set-GraphObjectName $obj $graphObj.ObjectType $copyName
|
||||
|
||||
$newObj = Import-GraphObject $obj $graphObj.ObjectType
|
||||
if($newObj)
|
||||
{
|
||||
if($graphObj.ObjectType.PostCopyCommand)
|
||||
{
|
||||
& $graphObj.ObjectType.PostCopyCommand $copyFromObj $newObj $graphObj.ObjectType
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-log "Failed to copy $sourceName" 3
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Log "****************************************************************"
|
||||
Write-Log "Bulk copy finished"
|
||||
Write-Log "****************************************************************"
|
||||
Write-Status ""
|
||||
}
|
||||
@@ -20,7 +20,7 @@ $global:documentationProviders = @()
|
||||
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'1.0.1'
|
||||
'1.0.2'
|
||||
}
|
||||
|
||||
function Invoke-InitializeModule
|
||||
@@ -124,7 +124,13 @@ function Get-ObjectDocumentation
|
||||
{
|
||||
param($documentationObj)
|
||||
|
||||
Write-Status "Get documentation info for $((Get-GraphObjectName $documentationObj.Object $documentationObj.ObjectType)) ($($documentationObj.ObjectType.Title))"
|
||||
$additionalInfo = ""
|
||||
if($documentationObj.Object.'@ObjectFromFile' -eq $true)
|
||||
{
|
||||
$additionalInfo = " - From File"
|
||||
}
|
||||
|
||||
Write-Status "Get documentation info for $((Get-GraphObjectName $documentationObj.Object $documentationObj.ObjectType)) ($($documentationObj.ObjectType.Title))$additionalInfo"
|
||||
|
||||
$status = $null
|
||||
$inputType = "Settings"
|
||||
@@ -225,24 +231,8 @@ function Get-ObjectDocumentation
|
||||
{
|
||||
$inputType = "Property"
|
||||
$processed = $true
|
||||
<#
|
||||
if([IO.File]::Exists(($global:AppRootFolder + "\Documentation\ObjectInfo\$($obj.'@OData.Type').json")))
|
||||
{
|
||||
# Process object based on OData type
|
||||
$processed = Invoke-TranslateCustomProfileObject $obj "$($obj.'@OData.Type')"
|
||||
}
|
||||
elseif($objectType -and [IO.File]::Exists(($global:AppRootFolder + "\Documentation\ObjectInfo\#$($objectType.Id).json")))
|
||||
{
|
||||
# Process object based on Intune Object Type ($objectType)
|
||||
# '#' is added to front of name to distinguish manually created files from generated files
|
||||
$processed = Invoke-TranslateCustomProfileObject $obj "#$($objectType.Id)"
|
||||
}
|
||||
else
|
||||
{
|
||||
# Process objects based on generated Category Files and ObjectCategories.json
|
||||
$processed = Invoke-TranslateProfileObject $obj
|
||||
}
|
||||
#>
|
||||
|
||||
|
||||
$processed = Invoke-TranslateProfileObject $obj
|
||||
|
||||
if($processed -eq $false)
|
||||
@@ -685,7 +675,7 @@ function Invoke-TranslateADMXObject
|
||||
$categoryObj = $script:admxCategories | Where { $definitionValue.definition.id -in ($_.definitions.id) }
|
||||
$category = $script:admxCategories.definitions | Where { $definitionValue.definition.id -in ($_.id) }
|
||||
# Get presentation values for the current settings (with presentation object included)
|
||||
if($definitionValue.presentationValues -or $obj.'@CompareObject' -eq $true) #$definitionValue.'definition@odata.bind')
|
||||
if($definitionValue.presentationValues -or $obj.'@ObjectFromFile' -eq $true) #$definitionValue.'definition@odata.bind')
|
||||
{
|
||||
# Documenting exported json
|
||||
#$presentationValues = (Invoke-GraphRequest -Url "$($definitionValue.'definition@odata.bind')/presentations?`$expand=presentation" -ODataMetadata "minimal").value
|
||||
@@ -1039,7 +1029,7 @@ function Invoke-TranslateIntentObject
|
||||
foreach($category in ($categories | Sort -Property displayName))
|
||||
{
|
||||
# Get settings for the category. This will put them in the correct order...
|
||||
if($obj.'@CompareObject' -ne $true)
|
||||
if($obj.'@ObjectFromFile' -ne $true)
|
||||
{
|
||||
$settings = (Invoke-GraphRequest "/deviceManagement/intents/$($obj.Id)/categories/$($category.Id)/settings?`$expand=Microsoft.Graph.DeviceManagementComplexSettingInstance/Value" -ODataMetadata "minimal" @params).Value
|
||||
}
|
||||
@@ -2662,7 +2652,9 @@ function Invoke-TranslateScheduledActionType
|
||||
foreach($actionConfig in $actionRule.scheduledActionConfigurations)
|
||||
{
|
||||
$notificationTemplate = $null
|
||||
$notificationTemplateId = $null
|
||||
$additionalNotifications = $null
|
||||
$additionalNotificationsList = $null
|
||||
|
||||
if($actionConfig.actionType -eq "notification")
|
||||
{
|
||||
@@ -2694,6 +2686,7 @@ function Invoke-TranslateScheduledActionType
|
||||
if($actionConfig.notificationTemplateId -ne [Guid]::Empty)
|
||||
{
|
||||
$notificationTemplate = Get-LanguageString "ScheduledAction.Notification.selected"
|
||||
$notificationTemplateId = $actionConfig.notificationTemplateId
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2703,6 +2696,7 @@ function Invoke-TranslateScheduledActionType
|
||||
if($actionConfig.notificationMessageCCList.Count -gt 0)
|
||||
{
|
||||
$additionalNotifications = ((Get-LanguageString "ScheduledAction.Notification.numSelected") -f $actionConfig.notificationMessageCCList.Count)
|
||||
$additionalNotificationsList = $actionConfig.notificationMessageCCList -join ","
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2710,11 +2704,26 @@ function Invoke-TranslateScheduledActionType
|
||||
}
|
||||
}
|
||||
|
||||
$objClone = $actionConfig | ConvertTo-Json -Depth 10 | ConvertFrom-Json
|
||||
|
||||
Remove-Property $objClone "Id"
|
||||
foreach($prop in $objClone.PSObject.Properties)
|
||||
{
|
||||
if($prop.Name -like "*@odata*")
|
||||
{
|
||||
Remove-Property $objClone $prop.Name
|
||||
}
|
||||
}
|
||||
|
||||
# ToDo: Resolve MessageTemplateId and EmailCCIds to actual object names
|
||||
$script:objectComplianceActionData += New-Object PSObject -Property @{
|
||||
IdStr = ($objClone | ConvertTo-Json -Depth 10 -Compress)
|
||||
Action = $actionType
|
||||
Schedule = $schedule
|
||||
MessageTemplate = $notificationTemplate
|
||||
MessageTemplateId = $notificationTemplateId
|
||||
EmailCC = $additionalNotifications
|
||||
EmailCCIds = $additionalNotificationsList
|
||||
Category=$category
|
||||
RawJsonValue=($actionConfig | ConvertTo-Json -Depth 20 -Compress)
|
||||
}
|
||||
@@ -3455,11 +3464,10 @@ function Show-DocumentationForm
|
||||
$tmpArr += $script:objectSettingsData | Select -Property $script:settingsProperties | ConvertTo-Csv -NoTypeInformation
|
||||
}
|
||||
|
||||
if($script:objectSettingsData.Count -gt 0)
|
||||
if($script:applicabilityRules.Count -gt 0)
|
||||
{
|
||||
$tmpArr += $script:applicabilityRules | Select -Property Rule,Property,Value,Category | ConvertTo-Csv -NoTypeInformation
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if($script:objectComplianceActionData.Count -gt 0)
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ This module will also document some objects based on PowerShell functions
|
||||
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'1.0.0'
|
||||
'1.0.1'
|
||||
}
|
||||
|
||||
function Invoke-InitializeModule
|
||||
@@ -68,7 +68,14 @@ function Invoke-CDDocumentObject
|
||||
return [PSCustomObject]@{
|
||||
Properties = @("Name","Value","Category","SubCategory")
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif($type -eq '#microsoft.graph.policySet')
|
||||
{
|
||||
Invoke-CDDocumentPolicySet $documentationObj
|
||||
return [PSCustomObject]@{
|
||||
Properties = @("Name","Value","Category","SubCategory")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Get-CDAllManagedApps
|
||||
@@ -2040,4 +2047,102 @@ function Invoke-CDDocumentConditionalAccess
|
||||
EntityKey = "persistentBrowser"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Document Policy Sets
|
||||
function Invoke-CDDocumentPolicySet
|
||||
{
|
||||
param($documentationObj)
|
||||
|
||||
$obj = $documentationObj.Object
|
||||
$objectType = $documentationObj.ObjectType
|
||||
|
||||
$script:objectSeparator = ?? $global:cbDocumentationObjectSeparator.SelectedValue ([System.Environment]::NewLine)
|
||||
$script:propertySeparator = ?? $global:cbDocumentationPropertySeparator.SelectedValue ","
|
||||
|
||||
###################################################
|
||||
# Basic info
|
||||
###################################################
|
||||
|
||||
Add-BasicDefaultValues $obj $objectType
|
||||
Add-BasicPropertyValue (Get-LanguageString "TableHeaders.configurationType") (Get-LanguageString "SettingDetails.appConfiguration")
|
||||
|
||||
|
||||
###################################################
|
||||
# Basic info
|
||||
###################################################
|
||||
|
||||
$addedSettings = @()
|
||||
|
||||
$policySetSettings = (
|
||||
[PSCustomObject]@{
|
||||
Types = @(
|
||||
@('#microsoft.graph.mobileAppPolicySetItem','appTitle'),
|
||||
@('#microsoft.graph.targetedManagedAppConfigurationPolicySetItem','appConfigurationTitle'),
|
||||
@('#microsoft.graph.managedAppProtectionPolicySetItem','appProtectionTitle'),
|
||||
@('#microsoft.graph.iosLobAppProvisioningConfigurationPolicySetItem','iOSAppProvisioningTitle'))
|
||||
Category = (Get-LanguageString "PolicySet.appManagement")
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Types = @(
|
||||
@('#microsoft.graph.deviceConfigurationPolicySetItem','deviceConfigurationTitle'),
|
||||
@('#microsoft.graph.deviceCompliancePolicyPolicySetItem','deviceComplianceTitle'),
|
||||
@('#microsoft.graph.deviceManagementScriptPolicySetItem','powershellScriptTitle'))
|
||||
Category = (Get-LanguageString "PolicySet.deviceManagement")
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Types = @(
|
||||
@('#microsoft.graph.enrollmentRestrictionsConfigurationPolicySetItem','deviceTypeRestrictionTitle'),
|
||||
@('#microsoft.graph.windowsAutopilotDeploymentProfilePolicySetItem','windowsAutopilotDeploymentProfileTitle'),
|
||||
@('#microsoft.graph.windows10EnrollmentCompletionPageConfigurationPolicySetItem','enrollmentStatusSettingTitle'))
|
||||
Category = (Get-LanguageString "PolicySet.deviceEnrollment")
|
||||
}
|
||||
)
|
||||
|
||||
foreach($policySettingType in $policySetSettings)
|
||||
{
|
||||
foreach($subType in $policySettingType.Types)
|
||||
{
|
||||
foreach($setting in ($obj.items | where '@OData.Type' -eq $subType[0]))
|
||||
{
|
||||
if($setting.status -eq "error")
|
||||
{
|
||||
Write-Log "Skipping missing $($subType[0]) type with id $($setting.id). Error code: $($setting.errorCode)"
|
||||
continue
|
||||
}
|
||||
|
||||
Add-CustomSettingObject ([PSCustomObject]@{
|
||||
Name = $setting.displayName
|
||||
Value = (Get-CDDocumentPolicySetValue $setting)
|
||||
EntityKey = $setting.id
|
||||
Category = $policySettingType.Category
|
||||
SubCategory = (Get-LanguageString "PolicySet.$($subType[1])")
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Get-CDDocumentPolicySetValue
|
||||
{
|
||||
param($policySetItem)
|
||||
|
||||
if($policySetItem.'@OData.Type' -eq '#microsoft.graph.enrollmentRestrictionsConfigurationPolicySetItem' -or
|
||||
$policySetItem.'@OData.Type' -eq '#microsoft.graph.windows10EnrollmentCompletionPageConfigurationPolicySetItem')
|
||||
{
|
||||
return $policySetItem.Priority
|
||||
}
|
||||
elseif($policySetItem.'@OData.Type' -eq '#microsoft.graph.windowsAutopilotDeploymentProfilePolicySetItem')
|
||||
{
|
||||
if($policySetItem.itemType -eq '#microsoft.graph.azureADWindowsAutopilotDeploymentProfile')
|
||||
{
|
||||
return (Get-LanguageString "Autopilot.DirectoryService.azureAD")
|
||||
}
|
||||
elseif($policySetItem.itemType -eq '#microsoft.graph.activeDirectoryWindowsAutopilotDeploymentProfile')
|
||||
{
|
||||
return (Get-LanguageString "Autopilot.DirectoryService.activeDirectoryAD")
|
||||
}
|
||||
}
|
||||
# ToDo: Add support for all PolicySet items
|
||||
}
|
||||
#endregion
|
||||
@@ -3,7 +3,7 @@
|
||||
#https://docs.microsoft.com/en-us/office/vba/api/overview/word
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'1.0.0'
|
||||
'1.0.1'
|
||||
}
|
||||
|
||||
function Invoke-InitializeModule
|
||||
@@ -225,24 +225,12 @@ function Invoke-WordPostProcessItems
|
||||
$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
|
||||
$fileName = Expand-FileName $fileName
|
||||
|
||||
$format = [Microsoft.Office.Interop.Word.WdSaveFormat]::wdFormatDocumentDefault
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ This module is for the Endpoint Manager/Intune View. It manages Export/Import/Co
|
||||
#>
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'3.1.2'
|
||||
'3.1.3'
|
||||
}
|
||||
|
||||
function Invoke-InitializeModule
|
||||
@@ -562,7 +562,7 @@ function Invoke-EMAuthenticateToMSAL
|
||||
{
|
||||
$global:EMViewObject.AppInfo = Get-GraphAppInfo "EMAzureApp" "d1ddf0e4-d672-4dae-b554-9d5bdfd93547"
|
||||
Set-MSALCurrentApp $global:EMViewObject.AppInfo
|
||||
& $global:msalAuthenticator.Login -Account (?? $global:MSALToken.Account.UserName (Get-Setting "" "LastLoggedOnUser")) -Permissions $global:EMViewObject.Permissions
|
||||
& $global:msalAuthenticator.Login -Account (?? $global:MSALToken.Account.UserName (Get-Setting "" "LastLoggedOnUser"))
|
||||
}
|
||||
|
||||
function Invoke-EMDeactivateView
|
||||
@@ -1178,7 +1178,7 @@ function Start-PostListAppProtection
|
||||
param($objList, $objectType)
|
||||
|
||||
# App Configurations for Managed Apps are included in App Protections e.g. the /deviceAppManagement/managedAppPolicies API
|
||||
# For some reason, the some $filter options is not supported to filter out these objects
|
||||
# For some reason, the $filter option is not supported to filter out these objects
|
||||
# e.g. not isof(...) to excluded the type, not startsWith(id, 'A_') to exlude based on Id
|
||||
# These filters generates a request error so fiter them out manually in this function instead
|
||||
# The portal is probably doing the same thing since these are included in the return but not in the UI
|
||||
|
||||
@@ -10,7 +10,7 @@ This module is for the Endpoint Info View. It shows read-only objects in Intune
|
||||
#>
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'3.1.1'
|
||||
'3.1.2'
|
||||
}
|
||||
|
||||
function Invoke-InitializeModule
|
||||
@@ -100,6 +100,6 @@ function Invoke-EMInfoAuthenticateToMSAL
|
||||
$usr = (?? $global:MSALToken.Account.UserName (Get-Setting "" "LastLoggedOnUser"))
|
||||
if($usr)
|
||||
{
|
||||
& $global:msalAuthenticator.Login -Account $usr -Permissions $global:EMInfoViewObject.Permissions
|
||||
& $global:msalAuthenticator.Login -Account $usr
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ This module manages Authentication for the application with MSAL. It is also res
|
||||
#>
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'3.0.1'
|
||||
'3.0.2'
|
||||
}
|
||||
|
||||
$global:msalAuthenticator = $null
|
||||
@@ -520,9 +520,7 @@ function Connect-MSALUser
|
||||
[switch]
|
||||
$Interactive,
|
||||
|
||||
$Account,
|
||||
|
||||
$Permissions = @() # Addidional permissions required by the current view object
|
||||
$Account
|
||||
)
|
||||
|
||||
# No login during first time the app is started
|
||||
@@ -563,7 +561,7 @@ function Connect-MSALUser
|
||||
$global:MSALToken = $null
|
||||
}
|
||||
|
||||
if((Get-SettingValue "UseDefaultPermissions") -eq $true)
|
||||
if((Get-SettingValue "UseDefaultPermissions") -eq $true -or ($global:currentViewObject.ViewInfo.Permissions | measure).Count -eq 0)
|
||||
{
|
||||
[string[]] $Scopes = "https://graph.microsoft.com/.default"
|
||||
$useDefaultPermissions = $true
|
||||
@@ -582,11 +580,7 @@ function Connect-MSALUser
|
||||
$reqScopes += "RoleManagement.Read.Directory"
|
||||
}
|
||||
|
||||
if($Permissions.Count -gt 0)
|
||||
{
|
||||
$script:curViewPermissions = $Permissions
|
||||
}
|
||||
$reqScopes += $script:curViewPermissions
|
||||
$script:curViewPermissions = $global:currentViewObject.ViewInfo.Permissions
|
||||
|
||||
foreach($tmpScope in $script:curViewPermissions)
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ This module manages Microsoft Grap fuctions like calling APIs, managing graph ob
|
||||
#>
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'3.1.1'
|
||||
'3.1.2'
|
||||
}
|
||||
|
||||
$global:MSGraphGlobalApps = @(
|
||||
@@ -622,6 +622,7 @@ function Show-GraphExportForm
|
||||
Set-XamlProperty $script:exportForm "txtExportPath" "Text" (?? (Get-Setting "" "LastUsedRoot") (Get-SettingValue "RootFolder"))
|
||||
Set-XamlProperty $script:exportForm "chkAddObjectType" "IsChecked" (Get-SettingValue "AddObjectType")
|
||||
Set-XamlProperty $script:exportForm "chkAddCompanyName" "IsChecked" (Get-SettingValue "AddCompanyName")
|
||||
Set-XamlProperty $script:exportForm "chkExportAssignments" "IsChecked" (Get-SettingValue "ExportAssignments")
|
||||
|
||||
Set-XamlProperty $script:exportForm "btnExportSelected" "IsEnabled" ($global:dgObjects.SelectedItem -ne $null)
|
||||
if(($global:dgObjects.ItemsSource | Where IsSelected -eq $true).Count -gt 0)
|
||||
@@ -672,6 +673,7 @@ function Show-GraphBulkExportForm
|
||||
|
||||
Set-XamlProperty $script:exportForm "txtExportPath" "Text" (?? (Get-Setting "" "LastUsedRoot") (Get-SettingValue "RootFolder"))
|
||||
Set-XamlProperty $script:exportForm "chkAddCompanyName" "IsChecked" (Get-SettingValue "AddCompanyName")
|
||||
Set-XamlProperty $script:exportForm "chkExportAssignments" "IsChecked" (Get-SettingValue "ExportAssignments")
|
||||
|
||||
Add-XamlEvent $script:exportForm "browseExportPath" "add_click" ({
|
||||
$folder = Get-Folder (Get-XamlProperty $script:exportForm "txtExportPath" "Text") "Select root folder for export"
|
||||
@@ -1147,11 +1149,20 @@ function Get-GraphFileObjects
|
||||
$fileArr = @()
|
||||
foreach($file in (Get-Item -path "$path\*.json" @params))
|
||||
{
|
||||
if($ObjectType.LoadObject)
|
||||
{
|
||||
$graphObj = & $ObjectType.LoadObject $file.FullName
|
||||
}
|
||||
else
|
||||
{
|
||||
$graphObj = (ConvertFrom-Json (Get-Content $file.FullName -Raw))
|
||||
}
|
||||
|
||||
$obj = New-Object PSObject -Property @{
|
||||
FileName = $file.Name
|
||||
FileInfo = $file
|
||||
Selected = $SelectedStatus
|
||||
Object = (ConvertFrom-Json (Get-Content $file.FullName -Raw))
|
||||
Object = $graphObj
|
||||
ObjectType = $ObjectType
|
||||
}
|
||||
|
||||
@@ -1818,13 +1829,7 @@ function Export-GraphObject
|
||||
|
||||
if($chkExportAssignments.IsChecked -ne $true -and $obj.Assignments)
|
||||
{
|
||||
### ToDo: Fix full support for including Assignments. $extend=Assignments might not work
|
||||
### E.g. Check AutoPilot
|
||||
Remove-Property $obj $Assignments
|
||||
}
|
||||
elseif($chkExportAssignments.IsChecked -eq $true -and -not $obj.Assignments)
|
||||
{
|
||||
|
||||
Remove-Property $obj "Assignments"
|
||||
}
|
||||
|
||||
$obj | ConvertTo-Json -Depth 10 | Out-File ([IO.Path]::Combine($exportFolder, (Remove-InvalidFileNameChars "$((Get-GraphObjectName $obj $objectType)).json")))
|
||||
|
||||
Reference in New Issue
Block a user