3.1.3
Bulk Compare, Bulk Copy + bug fixes
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,4 +4,5 @@
|
|||||||
/CloudAPIPowerShellManagement.Log
|
/CloudAPIPowerShellManagement.Log
|
||||||
/CloudAPIPowerShellManagement.Lo_
|
/CloudAPIPowerShellManagement.Lo_
|
||||||
/Documentation/Get-LanguageStrings.ps1
|
/Documentation/Get-LanguageStrings.ps1
|
||||||
|
/*.csv
|
||||||
|
|
||||||
|
|||||||
111
Core.psm1
111
Core.psm1
@@ -12,7 +12,7 @@ This module handles the WPF UI
|
|||||||
|
|
||||||
function Get-ModuleVersion
|
function Get-ModuleVersion
|
||||||
{
|
{
|
||||||
'3.0.1'
|
'3.1.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
function Start-CoreApp
|
function Start-CoreApp
|
||||||
@@ -363,6 +363,24 @@ function Get-XamlObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Invoke-RegisterName
|
||||||
|
{
|
||||||
|
param($parent, $name, $registerTo)
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$control = $parent.FindName($name)
|
||||||
|
if($control)
|
||||||
|
{
|
||||||
|
$registerTo.RegisterName($name, $control)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Write-LogError "Failed to register $name" $_.Exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Dialogs
|
#region Dialogs
|
||||||
@@ -429,6 +447,65 @@ function Show-AboutDialog
|
|||||||
Show-ModalForm "About" $script:dlgAbout
|
Show-ModalForm "About" $script:dlgAbout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Show-UpdatesDialog
|
||||||
|
{
|
||||||
|
$script:dlgUpdates = Get-XamlObject ($global:AppRootFolder + "\Xaml\UpdatesDialog.xaml")
|
||||||
|
if(-not $script:dlgUpdates) { return }
|
||||||
|
|
||||||
|
Write-Status "Getting Release Notes Information"
|
||||||
|
|
||||||
|
Add-XamlEvent $script:dlgUpdates "btnClose" "add_click" {
|
||||||
|
$script:dlgUpdates = $null
|
||||||
|
Show-ModalObject
|
||||||
|
}
|
||||||
|
|
||||||
|
#Get-Module | Where Name -eq "Core"
|
||||||
|
$fileContent = Get-Content -Raw -Path ($global:AppRootFolder + "\ReleaseNotes.md")
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$tmp = $fileContent.Replace("`r`n","`n")
|
||||||
|
$mystring = ("blob $($tmp.Length)`0" + $tmp)
|
||||||
|
$mystream = [IO.MemoryStream]::new([byte[]][char[]]$mystring)
|
||||||
|
$curHash = Get-FileHash -InputStream $mystream -Algorithm SHA1
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if($mystream) { $mystream.Dispose() }
|
||||||
|
}
|
||||||
|
|
||||||
|
<#
|
||||||
|
$latest = Invoke-RestMethod "https://api.github.com/repos/Micke-K/IntuneManagement/releases/latest"
|
||||||
|
if($latest)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
#>
|
||||||
|
|
||||||
|
$content = Invoke-RestMethod "https://api.github.com/repos/Micke-K/IntuneManagement/contents/ReleaseNotes.md"
|
||||||
|
if($content)
|
||||||
|
{
|
||||||
|
$txt = [System.Text.Encoding]::ASCII.GetString(([System.Convert]::FromBase64String($content.content)))
|
||||||
|
Set-XamlProperty $script:dlgUpdates "txtReleaseNotes" "Text" $txt
|
||||||
|
|
||||||
|
if($content.sha -ne $curHash.Hash)
|
||||||
|
{
|
||||||
|
# ReleaseNotes.md not matching
|
||||||
|
Set-XamlProperty $script:dlgUpdates "tabLocalReleaseNotes" "Visibility" "Visible"
|
||||||
|
Set-XamlProperty $script:dlgUpdates "txtReleaseNotes" "Text" $fileContent
|
||||||
|
Set-XamlProperty $script:dlgUpdates "txtReleaseNotesMatch" "Visibility" "Collapsed"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Set-XamlProperty $script:dlgUpdates "txtReleaseNotesNoMatch" "Visibility" "Collapsed"
|
||||||
|
Set-XamlProperty $script:dlgUpdates "tabLocalReleaseNotes" "Visibility" "Collapsed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Status ""
|
||||||
|
|
||||||
|
Show-ModalForm "Release Notes" $script:dlgUpdates -HideButtons
|
||||||
|
}
|
||||||
|
|
||||||
function Show-InputDialog
|
function Show-InputDialog
|
||||||
{
|
{
|
||||||
param(
|
param(
|
||||||
@@ -664,6 +741,9 @@ function Get-Folder
|
|||||||
function Remove-Property
|
function Remove-Property
|
||||||
{
|
{
|
||||||
param($obj, $prop)
|
param($obj, $prop)
|
||||||
|
|
||||||
|
if(-not $prop) { return }
|
||||||
|
|
||||||
if(($obj | GM -MemberType NoteProperty -Name $prop))
|
if(($obj | GM -MemberType NoteProperty -Name $prop))
|
||||||
{
|
{
|
||||||
Write-LogDebug "Remove property $prop"
|
Write-LogDebug "Remove property $prop"
|
||||||
@@ -692,6 +772,28 @@ function Get-GridCheckboxColumn
|
|||||||
$column
|
$column
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Expand-FileName
|
||||||
|
{
|
||||||
|
param($fileName)
|
||||||
|
|
||||||
|
[Environment]::SetEnvironmentVariable("Date",(Get-Date).ToString("yyyy-MM-dd"),[System.EnvironmentVariableTarget]::Process)
|
||||||
|
[Environment]::SetEnvironmentVariable("DateTime",(Get-Date).ToString("yyyyMMdd-HHmm"),[System.EnvironmentVariableTarget]::Process)
|
||||||
|
[Environment]::SetEnvironmentVariable("Organization",$global:Organization.displayName,[System.EnvironmentVariableTarget]::Process)
|
||||||
|
|
||||||
|
$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("DateTime",$null,[System.EnvironmentVariableTarget]::Process)
|
||||||
|
[Environment]::SetEnvironmentVariable("Organization",$null,[System.EnvironmentVariableTarget]::Process)
|
||||||
|
|
||||||
|
$fileName
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Reg functions
|
#region Reg functions
|
||||||
@@ -708,7 +810,7 @@ function Save-Setting
|
|||||||
$regPath = Get-RegPath $SubPath
|
$regPath = Get-RegPath $SubPath
|
||||||
if((Test-Path $regPath) -eq $false)
|
if((Test-Path $regPath) -eq $false)
|
||||||
{
|
{
|
||||||
New-Item (Get-RegPath $SubPath) -ErrorAction SilentlyContinue
|
New-Item (Get-RegPath $SubPath) -Force -ErrorAction SilentlyContinue | Out-Null
|
||||||
}
|
}
|
||||||
New-ItemProperty -Path $regPath -Name $Key -Value $Value -Type $Type -Force | Out-Null
|
New-ItemProperty -Path $regPath -Name $Key -Value $Value -Type $Type -Force | Out-Null
|
||||||
}
|
}
|
||||||
@@ -1201,6 +1303,8 @@ function Show-View
|
|||||||
& $global:currentViewObject.ViewInfo.Deactivating
|
& $global:currentViewObject.ViewInfo.Deactivating
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$global:currentViewObject = $viewObject
|
||||||
|
|
||||||
$viewItems = ?: ($viewObject.ViewInfo.Sort -ne $false) ($viewObject.ViewItems | Sort-Object -Property Title) ($viewObject.ViewItems)
|
$viewItems = ?: ($viewObject.ViewInfo.Sort -ne $false) ($viewObject.ViewItems | Sort-Object -Property Title) ($viewObject.ViewItems)
|
||||||
|
|
||||||
$lblMenuTitle.Content = $viewObject.ViewInfo.Title
|
$lblMenuTitle.Content = $viewObject.ViewInfo.Title
|
||||||
@@ -1227,8 +1331,6 @@ function Show-View
|
|||||||
$grdViewPanel.Children.Add($viewObject.ViewInfo.ViewPanel) | Out-Null
|
$grdViewPanel.Children.Add($viewObject.ViewInfo.ViewPanel) | Out-Null
|
||||||
}
|
}
|
||||||
|
|
||||||
$global:currentViewObject = $viewObject
|
|
||||||
|
|
||||||
Set-MainTitle
|
Set-MainTitle
|
||||||
|
|
||||||
Show-AuthenticationInfo
|
Show-AuthenticationInfo
|
||||||
@@ -1286,6 +1388,7 @@ function Get-MainWindow
|
|||||||
|
|
||||||
# ToDo: Convert to a list for data binding
|
# ToDo: Convert to a list for data binding
|
||||||
Add-XamlEvent $window "mnuSettings" "Add_Click" -scriptBlock ([scriptblock]{ Show-SettingsForm })
|
Add-XamlEvent $window "mnuSettings" "Add_Click" -scriptBlock ([scriptblock]{ Show-SettingsForm })
|
||||||
|
Add-XamlEvent $window "mnuUpdates" "Add_Click" -scriptBlock ([scriptblock]{ Show-UpdatesDialog })
|
||||||
Add-XamlEvent $window "mnuAbout" "Add_Click" -scriptBlock ([scriptblock]{ Show-AboutDialog })
|
Add-XamlEvent $window "mnuAbout" "Add_Click" -scriptBlock ([scriptblock]{ Show-AboutDialog })
|
||||||
Add-XamlEvent $window "mnuExit" "Add_Click" -scriptBlock ([scriptblock]{
|
Add-XamlEvent $window "mnuExit" "Add_Click" -scriptBlock ([scriptblock]{
|
||||||
if([System.Windows.MessageBox]::Show("Are you sure you want to exit?", "Exit?", "YesNo", "Question") -eq "Yes")
|
if([System.Windows.MessageBox]::Show("Are you sure you want to exit?", "Exit?", "YesNo", "Question") -eq "Yes")
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -11,13 +11,102 @@ Objects can be compared based on Properties or Documentatation info.
|
|||||||
|
|
||||||
function Get-ModuleVersion
|
function Get-ModuleVersion
|
||||||
{
|
{
|
||||||
'1.0.1'
|
'1.0.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
function Invoke-InitializeModule
|
function Invoke-InitializeModule
|
||||||
{
|
{
|
||||||
# Make sure we add the default Output types
|
$global:comparisonTypes = $null
|
||||||
Add-OutputType
|
$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
|
function Invoke-ShowMainWindow
|
||||||
@@ -42,6 +131,510 @@ function Invoke-ShowMainWindow
|
|||||||
$global:spSubMenu.Children.Insert(0, $button)
|
$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
|
function Show-CompareForm
|
||||||
{
|
{
|
||||||
param($objInfo)
|
param($objInfo)
|
||||||
@@ -49,12 +642,15 @@ function Show-CompareForm
|
|||||||
$script:cmpForm = Get-XamlObject ($global:AppRootFolder + "\Xaml\CompareForm.xaml") -AddVariables
|
$script:cmpForm = Get-XamlObject ($global:AppRootFolder + "\Xaml\CompareForm.xaml") -AddVariables
|
||||||
if(-not $script:cmpForm) { return }
|
if(-not $script:cmpForm) { return }
|
||||||
|
|
||||||
|
$script:cmpForm.Tag = $objInfo
|
||||||
|
|
||||||
$script:copareSource = $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:cbCompareType.SelectedValue = (Get-Setting "Compare" "Type" "property")
|
||||||
|
|
||||||
$global:txtIntuneObject.Text = (Get-GraphObjectName $objInfo.Object $objInfo.ObjectType)
|
$global:txtIntuneObject.Text = (Get-GraphObjectName $objInfo.Object $objInfo.ObjectType)
|
||||||
|
$global:txtIntuneObject.Tag = $objInfo
|
||||||
|
|
||||||
Add-XamlEvent $script:cmpForm "btnClose" "add_click" {
|
Add-XamlEvent $script:cmpForm "btnClose" "add_click" {
|
||||||
$script:cmpForm = $null
|
$script:cmpForm = $null
|
||||||
@@ -65,7 +661,7 @@ function Show-CompareForm
|
|||||||
Write-Status "Compare objects"
|
Write-Status "Compare objects"
|
||||||
Save-Setting "Compare" "Type" $global:cbCompareType.SelectedValue
|
Save-Setting "Compare" "Type" $global:cbCompareType.SelectedValue
|
||||||
$script:currentObjName = ""
|
$script:currentObjName = ""
|
||||||
Invoke-CompareObjects
|
Start-CompareExportObject
|
||||||
Write-Status ""
|
Write-Status ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,22 +676,42 @@ function Show-CompareForm
|
|||||||
$sf.Filter = "CSV (*.csv)|*.csv|All files (*.*)| *.*"
|
$sf.Filter = "CSV (*.csv)|*.csv|All files (*.*)| *.*"
|
||||||
if($sf.ShowDialog() -eq "OK")
|
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
|
$csvInfo | Out-File $sf.FileName -Force -Encoding UTF8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Add-XamlEvent $script:cmpForm "btnCompareCopy" "add_click" {
|
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" {
|
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 = [System.Windows.Forms.OpenFileDialog]::new()
|
||||||
$of.Multiselect = $false
|
$of.Multiselect = $false
|
||||||
$of.Filter = "Json files (*.json)|*.json"
|
$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())
|
if($of.ShowDialog())
|
||||||
{
|
{
|
||||||
Set-XamlProperty $script:cmpForm "txtCompareFile" "Text" $of.FileName
|
Set-XamlProperty $script:cmpForm "txtCompareFile" "Text" $of.FileName
|
||||||
@@ -110,18 +726,55 @@ function Show-CompareForm
|
|||||||
Show-ModalForm "Compare Intune Objects" $script:cmpForm -HideButtons
|
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 $script:copareSource) { return }
|
||||||
|
|
||||||
if(-not $global:txtCompareFile.Text)
|
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
|
return
|
||||||
}
|
}
|
||||||
elseif([IO.File]::Exists($global:txtCompareFile.Text) -eq $false)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +791,7 @@ function Invoke-CompareObjects
|
|||||||
}
|
}
|
||||||
catch
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,24 +801,45 @@ function Invoke-CompareObjects
|
|||||||
|
|
||||||
if($obj.Object."@OData.Type" -ne $compareObj."@OData.Type")
|
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
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$compareResult = Compare-Objects $obj.Object $compareObj $obj.ObjectType
|
||||||
|
|
||||||
|
$global:dgCompareInfo.ItemsSource = $compareResult
|
||||||
|
}
|
||||||
|
|
||||||
|
function Compare-Objects
|
||||||
|
{
|
||||||
|
param($obj1, $obj2, $objectType)
|
||||||
|
|
||||||
$script:compareProperties = @()
|
$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")
|
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
|
function Set-ColumnVisibility
|
||||||
{
|
{
|
||||||
param($showCategory = $false, $showSubCategory = $false)
|
param($showCategory = $false, $showSubCategory = $false)
|
||||||
@@ -208,7 +882,7 @@ function Compare-ObjectsBasedonProperty
|
|||||||
{
|
{
|
||||||
param($obj1, $obj2, $objectType)
|
param($obj1, $obj2, $objectType)
|
||||||
|
|
||||||
Write-Status "Compare properties"
|
Write-Status "Compare objects based on property values"
|
||||||
|
|
||||||
Set-ColumnVisibility $false
|
Set-ColumnVisibility $false
|
||||||
|
|
||||||
@@ -262,14 +936,16 @@ function Compare-ObjectsBasedonProperty
|
|||||||
$val1 = ($obj1.$propName | ConvertTo-Json -Depth 10)
|
$val1 = ($obj1.$propName | ConvertTo-Json -Depth 10)
|
||||||
$val2 = ($obj2.$propName | ConvertTo-Json -Depth 10)
|
$val2 = ($obj2.$propName | ConvertTo-Json -Depth 10)
|
||||||
Add-CompareProperty $propName $val1 $val2
|
Add-CompareProperty $propName $val1 $val2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$script:compareProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
function Get-CompareCustomColumnsDoc
|
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
|
Set-ColumnVisibility $true $true
|
||||||
}
|
}
|
||||||
@@ -281,23 +957,34 @@ function Get-CompareCustomColumnsDoc
|
|||||||
|
|
||||||
function Compare-ObjectsBasedonDocumentation
|
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
|
# ToDo: set this based on configuration value
|
||||||
$script:assignmentOutput = "simpleFullCompare"
|
$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]@{
|
$docObj2 = Invoke-ObjectDocumentation ([PSCustomObject]@{
|
||||||
Object = $obj2
|
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)
|
foreach ($prop in $docObj1.BasicInfo)
|
||||||
{
|
{
|
||||||
@@ -412,6 +1099,28 @@ function Compare-ObjectsBasedonDocumentation
|
|||||||
Add-CompareProperty $applicabilityRule.Property $val1 $val2 $applicabilityRule.Category
|
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:assignmentStr = Get-LanguageString "TableHeaders.assignment"
|
||||||
$script:groupsAdded = @()
|
$script:groupsAdded = @()
|
||||||
|
|
||||||
@@ -449,6 +1158,8 @@ function Compare-ObjectsBasedonDocumentation
|
|||||||
{
|
{
|
||||||
Add-AssignmentInfo $docObj2 $docObj1 $assignment -ReversedValue
|
Add-AssignmentInfo $docObj2 $docObj1 $assignment -ReversedValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$script:compareProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
function Add-AssignmentInfo
|
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
|
function Get-ModuleVersion
|
||||||
{
|
{
|
||||||
'1.0.1'
|
'1.0.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
function Invoke-InitializeModule
|
function Invoke-InitializeModule
|
||||||
@@ -124,7 +124,13 @@ function Get-ObjectDocumentation
|
|||||||
{
|
{
|
||||||
param($documentationObj)
|
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
|
$status = $null
|
||||||
$inputType = "Settings"
|
$inputType = "Settings"
|
||||||
@@ -225,24 +231,8 @@ function Get-ObjectDocumentation
|
|||||||
{
|
{
|
||||||
$inputType = "Property"
|
$inputType = "Property"
|
||||||
$processed = $true
|
$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
|
$processed = Invoke-TranslateProfileObject $obj
|
||||||
|
|
||||||
if($processed -eq $false)
|
if($processed -eq $false)
|
||||||
@@ -685,7 +675,7 @@ function Invoke-TranslateADMXObject
|
|||||||
$categoryObj = $script:admxCategories | Where { $definitionValue.definition.id -in ($_.definitions.id) }
|
$categoryObj = $script:admxCategories | Where { $definitionValue.definition.id -in ($_.definitions.id) }
|
||||||
$category = $script:admxCategories.definitions | Where { $definitionValue.definition.id -in ($_.id) }
|
$category = $script:admxCategories.definitions | Where { $definitionValue.definition.id -in ($_.id) }
|
||||||
# Get presentation values for the current settings (with presentation object included)
|
# 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
|
# Documenting exported json
|
||||||
#$presentationValues = (Invoke-GraphRequest -Url "$($definitionValue.'definition@odata.bind')/presentations?`$expand=presentation" -ODataMetadata "minimal").value
|
#$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))
|
foreach($category in ($categories | Sort -Property displayName))
|
||||||
{
|
{
|
||||||
# Get settings for the category. This will put them in the correct order...
|
# 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
|
$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)
|
foreach($actionConfig in $actionRule.scheduledActionConfigurations)
|
||||||
{
|
{
|
||||||
$notificationTemplate = $null
|
$notificationTemplate = $null
|
||||||
|
$notificationTemplateId = $null
|
||||||
$additionalNotifications = $null
|
$additionalNotifications = $null
|
||||||
|
$additionalNotificationsList = $null
|
||||||
|
|
||||||
if($actionConfig.actionType -eq "notification")
|
if($actionConfig.actionType -eq "notification")
|
||||||
{
|
{
|
||||||
@@ -2694,6 +2686,7 @@ function Invoke-TranslateScheduledActionType
|
|||||||
if($actionConfig.notificationTemplateId -ne [Guid]::Empty)
|
if($actionConfig.notificationTemplateId -ne [Guid]::Empty)
|
||||||
{
|
{
|
||||||
$notificationTemplate = Get-LanguageString "ScheduledAction.Notification.selected"
|
$notificationTemplate = Get-LanguageString "ScheduledAction.Notification.selected"
|
||||||
|
$notificationTemplateId = $actionConfig.notificationTemplateId
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2703,6 +2696,7 @@ function Invoke-TranslateScheduledActionType
|
|||||||
if($actionConfig.notificationMessageCCList.Count -gt 0)
|
if($actionConfig.notificationMessageCCList.Count -gt 0)
|
||||||
{
|
{
|
||||||
$additionalNotifications = ((Get-LanguageString "ScheduledAction.Notification.numSelected") -f $actionConfig.notificationMessageCCList.Count)
|
$additionalNotifications = ((Get-LanguageString "ScheduledAction.Notification.numSelected") -f $actionConfig.notificationMessageCCList.Count)
|
||||||
|
$additionalNotificationsList = $actionConfig.notificationMessageCCList -join ","
|
||||||
}
|
}
|
||||||
else
|
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 @{
|
$script:objectComplianceActionData += New-Object PSObject -Property @{
|
||||||
|
IdStr = ($objClone | ConvertTo-Json -Depth 10 -Compress)
|
||||||
Action = $actionType
|
Action = $actionType
|
||||||
Schedule = $schedule
|
Schedule = $schedule
|
||||||
MessageTemplate = $notificationTemplate
|
MessageTemplate = $notificationTemplate
|
||||||
|
MessageTemplateId = $notificationTemplateId
|
||||||
EmailCC = $additionalNotifications
|
EmailCC = $additionalNotifications
|
||||||
|
EmailCCIds = $additionalNotificationsList
|
||||||
Category=$category
|
Category=$category
|
||||||
RawJsonValue=($actionConfig | ConvertTo-Json -Depth 20 -Compress)
|
RawJsonValue=($actionConfig | ConvertTo-Json -Depth 20 -Compress)
|
||||||
}
|
}
|
||||||
@@ -3455,11 +3464,10 @@ function Show-DocumentationForm
|
|||||||
$tmpArr += $script:objectSettingsData | Select -Property $script:settingsProperties | ConvertTo-Csv -NoTypeInformation
|
$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
|
$tmpArr += $script:applicabilityRules | Select -Property Rule,Property,Value,Category | ConvertTo-Csv -NoTypeInformation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if($script:objectComplianceActionData.Count -gt 0)
|
if($script:objectComplianceActionData.Count -gt 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ This module will also document some objects based on PowerShell functions
|
|||||||
|
|
||||||
function Get-ModuleVersion
|
function Get-ModuleVersion
|
||||||
{
|
{
|
||||||
'1.0.0'
|
'1.0.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
function Invoke-InitializeModule
|
function Invoke-InitializeModule
|
||||||
@@ -68,7 +68,14 @@ function Invoke-CDDocumentObject
|
|||||||
return [PSCustomObject]@{
|
return [PSCustomObject]@{
|
||||||
Properties = @("Name","Value","Category","SubCategory")
|
Properties = @("Name","Value","Category","SubCategory")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
elseif($type -eq '#microsoft.graph.policySet')
|
||||||
|
{
|
||||||
|
Invoke-CDDocumentPolicySet $documentationObj
|
||||||
|
return [PSCustomObject]@{
|
||||||
|
Properties = @("Name","Value","Category","SubCategory")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Get-CDAllManagedApps
|
function Get-CDAllManagedApps
|
||||||
@@ -2040,4 +2047,102 @@ function Invoke-CDDocumentConditionalAccess
|
|||||||
EntityKey = "persistentBrowser"
|
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
|
#https://docs.microsoft.com/en-us/office/vba/api/overview/word
|
||||||
function Get-ModuleVersion
|
function Get-ModuleVersion
|
||||||
{
|
{
|
||||||
'1.0.0'
|
'1.0.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
function Invoke-InitializeModule
|
function Invoke-InitializeModule
|
||||||
@@ -225,24 +225,12 @@ function Invoke-WordPostProcessItems
|
|||||||
$script:doc.TablesOfFigures | ForEach-Object -Process { $_.Update() | Out-Null }
|
$script:doc.TablesOfFigures | ForEach-Object -Process { $_.Update() | Out-Null }
|
||||||
|
|
||||||
$fileName = $global:txtWordDocumentName.Text
|
$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)
|
if(-not $fileName)
|
||||||
{
|
{
|
||||||
$fileName = "%MyDocuments%\%Organization%-%Date%.docx"
|
$fileName = "%MyDocuments%\%Organization%-%Date%.docx"
|
||||||
}
|
}
|
||||||
$fileName = [Environment]::ExpandEnvironmentVariables($fileName)
|
|
||||||
|
|
||||||
foreach($tmpFolder in ([System.Enum]::GetNames([System.Environment+SpecialFolder])))
|
$fileName = Expand-FileName $fileName
|
||||||
{
|
|
||||||
$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
|
$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
|
function Get-ModuleVersion
|
||||||
{
|
{
|
||||||
'3.1.2'
|
'3.1.3'
|
||||||
}
|
}
|
||||||
|
|
||||||
function Invoke-InitializeModule
|
function Invoke-InitializeModule
|
||||||
@@ -562,7 +562,7 @@ function Invoke-EMAuthenticateToMSAL
|
|||||||
{
|
{
|
||||||
$global:EMViewObject.AppInfo = Get-GraphAppInfo "EMAzureApp" "d1ddf0e4-d672-4dae-b554-9d5bdfd93547"
|
$global:EMViewObject.AppInfo = Get-GraphAppInfo "EMAzureApp" "d1ddf0e4-d672-4dae-b554-9d5bdfd93547"
|
||||||
Set-MSALCurrentApp $global:EMViewObject.AppInfo
|
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
|
function Invoke-EMDeactivateView
|
||||||
@@ -1178,7 +1178,7 @@ function Start-PostListAppProtection
|
|||||||
param($objList, $objectType)
|
param($objList, $objectType)
|
||||||
|
|
||||||
# App Configurations for Managed Apps are included in App Protections e.g. the /deviceAppManagement/managedAppPolicies API
|
# 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
|
# 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
|
# 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
|
# 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
|
function Get-ModuleVersion
|
||||||
{
|
{
|
||||||
'3.1.1'
|
'3.1.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
function Invoke-InitializeModule
|
function Invoke-InitializeModule
|
||||||
@@ -100,6 +100,6 @@ function Invoke-EMInfoAuthenticateToMSAL
|
|||||||
$usr = (?? $global:MSALToken.Account.UserName (Get-Setting "" "LastLoggedOnUser"))
|
$usr = (?? $global:MSALToken.Account.UserName (Get-Setting "" "LastLoggedOnUser"))
|
||||||
if($usr)
|
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
|
function Get-ModuleVersion
|
||||||
{
|
{
|
||||||
'3.0.1'
|
'3.0.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
$global:msalAuthenticator = $null
|
$global:msalAuthenticator = $null
|
||||||
@@ -520,9 +520,7 @@ function Connect-MSALUser
|
|||||||
[switch]
|
[switch]
|
||||||
$Interactive,
|
$Interactive,
|
||||||
|
|
||||||
$Account,
|
$Account
|
||||||
|
|
||||||
$Permissions = @() # Addidional permissions required by the current view object
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# No login during first time the app is started
|
# No login during first time the app is started
|
||||||
@@ -563,7 +561,7 @@ function Connect-MSALUser
|
|||||||
$global:MSALToken = $null
|
$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"
|
[string[]] $Scopes = "https://graph.microsoft.com/.default"
|
||||||
$useDefaultPermissions = $true
|
$useDefaultPermissions = $true
|
||||||
@@ -582,11 +580,7 @@ function Connect-MSALUser
|
|||||||
$reqScopes += "RoleManagement.Read.Directory"
|
$reqScopes += "RoleManagement.Read.Directory"
|
||||||
}
|
}
|
||||||
|
|
||||||
if($Permissions.Count -gt 0)
|
$script:curViewPermissions = $global:currentViewObject.ViewInfo.Permissions
|
||||||
{
|
|
||||||
$script:curViewPermissions = $Permissions
|
|
||||||
}
|
|
||||||
$reqScopes += $script:curViewPermissions
|
|
||||||
|
|
||||||
foreach($tmpScope in $script:curViewPermissions)
|
foreach($tmpScope in $script:curViewPermissions)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ This module manages Microsoft Grap fuctions like calling APIs, managing graph ob
|
|||||||
#>
|
#>
|
||||||
function Get-ModuleVersion
|
function Get-ModuleVersion
|
||||||
{
|
{
|
||||||
'3.1.1'
|
'3.1.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
$global:MSGraphGlobalApps = @(
|
$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 "txtExportPath" "Text" (?? (Get-Setting "" "LastUsedRoot") (Get-SettingValue "RootFolder"))
|
||||||
Set-XamlProperty $script:exportForm "chkAddObjectType" "IsChecked" (Get-SettingValue "AddObjectType")
|
Set-XamlProperty $script:exportForm "chkAddObjectType" "IsChecked" (Get-SettingValue "AddObjectType")
|
||||||
Set-XamlProperty $script:exportForm "chkAddCompanyName" "IsChecked" (Get-SettingValue "AddCompanyName")
|
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)
|
Set-XamlProperty $script:exportForm "btnExportSelected" "IsEnabled" ($global:dgObjects.SelectedItem -ne $null)
|
||||||
if(($global:dgObjects.ItemsSource | Where IsSelected -eq $true).Count -gt 0)
|
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 "txtExportPath" "Text" (?? (Get-Setting "" "LastUsedRoot") (Get-SettingValue "RootFolder"))
|
||||||
Set-XamlProperty $script:exportForm "chkAddCompanyName" "IsChecked" (Get-SettingValue "AddCompanyName")
|
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" ({
|
Add-XamlEvent $script:exportForm "browseExportPath" "add_click" ({
|
||||||
$folder = Get-Folder (Get-XamlProperty $script:exportForm "txtExportPath" "Text") "Select root folder for export"
|
$folder = Get-Folder (Get-XamlProperty $script:exportForm "txtExportPath" "Text") "Select root folder for export"
|
||||||
@@ -1147,11 +1149,20 @@ function Get-GraphFileObjects
|
|||||||
$fileArr = @()
|
$fileArr = @()
|
||||||
foreach($file in (Get-Item -path "$path\*.json" @params))
|
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 @{
|
$obj = New-Object PSObject -Property @{
|
||||||
FileName = $file.Name
|
FileName = $file.Name
|
||||||
FileInfo = $file
|
FileInfo = $file
|
||||||
Selected = $SelectedStatus
|
Selected = $SelectedStatus
|
||||||
Object = (ConvertFrom-Json (Get-Content $file.FullName -Raw))
|
Object = $graphObj
|
||||||
ObjectType = $ObjectType
|
ObjectType = $ObjectType
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1818,13 +1829,7 @@ function Export-GraphObject
|
|||||||
|
|
||||||
if($chkExportAssignments.IsChecked -ne $true -and $obj.Assignments)
|
if($chkExportAssignments.IsChecked -ne $true -and $obj.Assignments)
|
||||||
{
|
{
|
||||||
### ToDo: Fix full support for including Assignments. $extend=Assignments might not work
|
Remove-Property $obj "Assignments"
|
||||||
### E.g. Check AutoPilot
|
|
||||||
Remove-Property $obj $Assignments
|
|
||||||
}
|
|
||||||
elseif($chkExportAssignments.IsChecked -eq $true -and -not $obj.Assignments)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$obj | ConvertTo-Json -Depth 10 | Out-File ([IO.Path]::Combine($exportFolder, (Remove-InvalidFileNameChars "$((Get-GraphObjectName $obj $objectType)).json")))
|
$obj | ConvertTo-Json -Depth 10 | Out-File ([IO.Path]::Combine($exportFolder, (Remove-InvalidFileNameChars "$((Get-GraphObjectName $obj $objectType)).json")))
|
||||||
|
|||||||
24
README.md
24
README.md
@@ -26,7 +26,29 @@ Objects can be compared based on property values or documented values.
|
|||||||
|
|
||||||
The property value method is a quick way to compare objects but it will only show the names and values of the native Intune object. This is not a good comparison method for Settings objects since they have all the settings in one property.
|
The property value method is a quick way to compare objects but it will only show the names and values of the native Intune object. This is not a good comparison method for Settings objects since they have all the settings in one property.
|
||||||
|
|
||||||
The documentation method is a bit slower but will show the values as they are stated in the Intune portal. This is the recommended way to compare objects but note that this is only supported on object types that supports documentation.
|
The documentation method is a bit slower but will show the values as they are stated in the Intune portal. This is the recommended way to compare objects but note that this is only supported on object types that supports documentation.
|
||||||
|
|
||||||
|
Bulk compare is supported. This can be performed in two ways:
|
||||||
|
|
||||||
|
* **Export File** - This will read each exported file and compare it with the existing object
|
||||||
|
|
||||||
|
The result file will be stored in the exported folder structure. Either in the Object Type folder or the parent folder depending on the Save as setting.
|
||||||
|
|
||||||
|
**Note:** This cannot be used with files exported from a different environment since it used the Id as identifier
|
||||||
|
|
||||||
|
* **Named Objects** - Compare file based on patterns
|
||||||
|
|
||||||
|
This can be used in where a pattern is used separate objects between different environments e.g. [Test] Policy 1 vs [Prod] Policy 1.
|
||||||
|
|
||||||
|
Output files are by default stored in the My Documents folder.
|
||||||
|
|
||||||
|
The output CSV can either be one file for ALL objects or one file for each Object Type.
|
||||||
|
|
||||||
|
## Bulk Copy
|
||||||
|
|
||||||
|
Bulk copy can be used to clone objects based on a name pattern. This can be used in the same scenario as Bulk Compare where the object names includes an environment identifier. The application will identify all objects matching the source pattern and copy each object with a new name matching the 'Copy object name pattern'. The object will not be copied if it detects that an object already exists with the new name.
|
||||||
|
|
||||||
|
**Note:** Assignments will NOT be copied.
|
||||||
|
|
||||||
## Change log
|
## Change log
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,27 @@
|
|||||||
# Release Notes
|
# Release Notes
|
||||||
|
|
||||||
|
## 3.1.3 - 2021-07-05
|
||||||
|
|
||||||
|
**New features**
|
||||||
|
|
||||||
|
- Bulk Compare
|
||||||
|
- Compare with exported files
|
||||||
|
- Compare with existing objects based on name patterns
|
||||||
|
- Bulk Copy
|
||||||
|
- Copy existing objects based on name patterns
|
||||||
|
- Support for documenting PolicySets
|
||||||
|
- Release Notes check - Check if there are any updates by comparing the local version of ReleaseNotes.md with the GitHub version
|
||||||
|
|
||||||
|
**Fixes**
|
||||||
|
|
||||||
|
* Fixed bug that caused an exception when exporting objects with an assignment and the 'Export Assignment' option disabled.
|
||||||
|
|
||||||
|
See [Issue 16](https://github.com/Micke-K/IntuneManagement/issues/16) for more info
|
||||||
|
|
||||||
|
* Export Assignments in Bulk Export and Object Export did not get default value from Settings
|
||||||
|
|
||||||
|
* Fixed issue where the required permissions were not passed during authentication
|
||||||
|
|
||||||
## 3.1.2 - 2021-06-20
|
## 3.1.2 - 2021-06-20
|
||||||
|
|
||||||
**New features**
|
**New features**
|
||||||
|
|||||||
79
Xaml/BulkCompare.xaml
Normal file
79
Xaml/BulkCompare.xaml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5,5,5,5" Grid.IsSharedSizeScope='True'>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid Name="grdImportProperties">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" SharedSizeGroup="TitleColumn" />
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Grid.Row='1' Margin="0,0,5,0">
|
||||||
|
<Label Content="Compare object" />
|
||||||
|
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="Specify the objects to compare e.g. compare exported files with existing objects or two existing objects based on name" />
|
||||||
|
</StackPanel>
|
||||||
|
<ComboBox Name="cbCompareProvider" Margin="0,5,0,0" MinWidth="250" Grid.Row='1' Grid.Column="1" HorizontalAlignment="Left"
|
||||||
|
DisplayMemberPath="Name" SelectedValuePath="Value" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<ContentControl Name="ccContentProviderOptions" Grid.Row="1" Grid.ColumnSpan="2" />
|
||||||
|
|
||||||
|
<Grid Grid.Row='2' VerticalAlignment="Stretch" >
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" SharedSizeGroup="TitleColumn" />
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,5,0" Grid.Row='0'>
|
||||||
|
<Label Content="Save as" />
|
||||||
|
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="Specifies how the export csv should be saved. One file per ObjectType or one file for all objects." />
|
||||||
|
</StackPanel>
|
||||||
|
<ComboBox Name="cbCompareSave" Margin="0,5,0,0" MinWidth="250" Grid.Row='0' Grid.Column="1" HorizontalAlignment="Left"
|
||||||
|
DisplayMemberPath="Name" SelectedValuePath="Value" />
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,5,0" Grid.Row='1' >
|
||||||
|
<Label Content="Comparison Type" />
|
||||||
|
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="Specify how objects should be compared" />
|
||||||
|
</StackPanel>
|
||||||
|
<ComboBox Name="cbCompareType" Margin="0,5,0,0" MinWidth="250" Grid.Row='1' Grid.Column="1" HorizontalAlignment="Left"
|
||||||
|
DisplayMemberPath="Name" SelectedValuePath="Value" />
|
||||||
|
|
||||||
|
<Grid Margin="0,0,5,0" Grid.Row='2' >
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Content="Objects to compare" />
|
||||||
|
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="Select the object types that should be compared" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<DataGrid Name="dgObjectsToCompare" Margin="0,5,0,5" Grid.Row='2' Grid.Column='1' CanUserAddRows="False" AutoGenerateColumns="False" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="White" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row='3' Grid.ColumnSpan='2' >
|
||||||
|
<Button Name="btnStartCompare" Content="Compare" Width='100' Margin="5,0,0,0" />
|
||||||
|
<Button Name="btnClose" Content="Close" Width='100' Margin="5,0,0,0" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
</Grid >
|
||||||
50
Xaml/BulkCopy.xaml
Normal file
50
Xaml/BulkCopy.xaml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5,5,5,5" Grid.IsSharedSizeScope='True'>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Grid Name="grdImportProperties">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" SharedSizeGroup="TitleColumn" />
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,5,0" >
|
||||||
|
<Label Content="Source object name pattern" />
|
||||||
|
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="Specify the pattern of the source objects e.g. Test -" />
|
||||||
|
</StackPanel>
|
||||||
|
<TextBox Text="" Name="txtCopyFromPattern" Grid.Column='1' Grid.Row='0' Margin="0,5,5,0" />
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,5,0" Grid.Row='1'>
|
||||||
|
<Label Content="Copy object name pattern" />
|
||||||
|
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="Specify the pattern of the new object e.g. Prod -" />
|
||||||
|
</StackPanel>
|
||||||
|
<TextBox Text="" Name="txtCopyToPattern" Grid.Column='1' Grid.Row='1' Margin="0,5,5,0" />
|
||||||
|
|
||||||
|
<Grid Margin="0,0,5,0" Grid.Row='2' >
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Label Content="Objects to copy" />
|
||||||
|
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="Select the object types that should be copied" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<DataGrid Name="dgObjectsToCopy" Margin="0,5,0,5" Grid.Row='2' Grid.Column='1' CanUserAddRows="False" AutoGenerateColumns="False" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="White" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row='3' Grid.ColumnSpan='2' >
|
||||||
|
<Button Name="btnStartCopy" Content="Copy" Width='100' Margin="5,0,0,0" />
|
||||||
|
<Button Name="btnClose" Content="Close" Width='100' Margin="5,0,0,0" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
</Grid >
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
<CheckBox Grid.Column='1' Grid.Row='1' Name='chkAddObjectType' VerticalAlignment="Center" IsEnabled="false" IsChecked="true" />
|
<CheckBox Grid.Column='1' Grid.Row='1' Name='chkAddObjectType' VerticalAlignment="Center" IsEnabled="false" IsChecked="true" />
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" Grid.Row='2' Margin="0,0,5,0">
|
<StackPanel Orientation="Horizontal" Grid.Row='2' Margin="0,0,5,0">
|
||||||
<Label Content="ExportAssignments" />
|
<Label Content="Export Assignments" />
|
||||||
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="Export object assignments" />
|
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="Export object assignments" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<CheckBox Grid.Column='1' Grid.Row='2' Name='chkExportAssignments' VerticalAlignment="Center" IsChecked="true" />
|
<CheckBox Grid.Column='1' Grid.Row='2' Name='chkExportAssignments' VerticalAlignment="Center" IsChecked="true" />
|
||||||
|
|||||||
27
Xaml/CompareExportOptions.xaml
Normal file
27
Xaml/CompareExportOptions.xaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<Grid Name="grdImportProperties" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" SharedSizeGroup="TitleColumn" />
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,5,0" >
|
||||||
|
<Label Content="Export root" />
|
||||||
|
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="The root folder where exported files are stored. This sould be the Company Name folder if it was included in the export." />
|
||||||
|
</StackPanel>
|
||||||
|
<Grid Grid.Column='1' Grid.Row='0' Margin="0,5,5,0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="5" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<TextBox Text="" Name="txtExportPath" />
|
||||||
|
<Button Grid.Column="2" Name="browseExportPath" Padding="5,2,5,2" Width="50" ToolTip="Browse for folder">...</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
40
Xaml/CompareNamedOptions.xaml
Normal file
40
Xaml/CompareNamedOptions.xaml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<Grid Name="grdImportProperties" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" SharedSizeGroup="TitleColumn" />
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,5,0" >
|
||||||
|
<Label Content="Source object name pattern" />
|
||||||
|
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="Specify the pattern of the source objects e.g. Test -" />
|
||||||
|
</StackPanel>
|
||||||
|
<TextBox Text="" Name="txtCompareSource" Grid.Column='1' Grid.Row='0' Margin="0,5,5,0" />
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,5,0" Grid.Row='1'>
|
||||||
|
<Label Content="Compare object name pattern" />
|
||||||
|
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="Specify the pattern of the objects that the source should be compared with e.g. Prod -" />
|
||||||
|
</StackPanel>
|
||||||
|
<TextBox Text="" Name="txtCompareWith" Grid.Column='1' Grid.Row='1' Margin="0,5,5,0" />
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,5,5,0" Grid.Row='2' >
|
||||||
|
<Label Content="Save folder" />
|
||||||
|
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="The folder where compare files will be saved. Default is the MyDocuments folder" />
|
||||||
|
</StackPanel>
|
||||||
|
<Grid Grid.Column='1' Grid.Row='2' Margin="0,5,5,0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="5" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<TextBox Text="" Name="txtSavePath" />
|
||||||
|
<Button Grid.Column="2" Name="browseSavePath" Padding="5,2,5,2" Width="50" ToolTip="Browse for folder">...</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
@@ -34,6 +34,7 @@
|
|||||||
<Menu Name="mnuMain" Padding="0,5,0,5" Grid.ColumnSpan="2" >
|
<Menu Name="mnuMain" Padding="0,5,0,5" Grid.ColumnSpan="2" >
|
||||||
<MenuItem Header="_File" >
|
<MenuItem Header="_File" >
|
||||||
<MenuItem Header="_Settings" Name="mnuSettings" />
|
<MenuItem Header="_Settings" Name="mnuSettings" />
|
||||||
|
<MenuItem Header="_Release Notes" Name="mnuUpdates" />
|
||||||
<MenuItem Header="_About" Name="mnuAbout" />
|
<MenuItem Header="_About" Name="mnuAbout" />
|
||||||
<MenuItem Header="_Exit" Name="mnuExit" />
|
<MenuItem Header="_Exit" Name="mnuExit" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|||||||
45
Xaml/UpdatesDialog.xaml
Normal file
45
Xaml/UpdatesDialog.xaml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,5,0,5">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TabControl SelectedIndex="0" Margin="0,0,0,5">
|
||||||
|
<TabItem Header="Release Notes">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Padding="0,0,5,0">
|
||||||
|
<TextBlock Name="txtReleaseNotes" />
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
</TabItem>
|
||||||
|
<TabItem Header="Local Release Notes" Name="tabLocalReleaseNotes">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Padding="0,0,5,0">
|
||||||
|
<TextBlock Name="txtReleaseNotesLocal" />
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
</TabItem>
|
||||||
|
</TabControl>
|
||||||
|
|
||||||
|
<StackPanel Name="spCompareSubMenu" Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row='1'>
|
||||||
|
<TextBlock Name="txtReleaseNotesMatch">
|
||||||
|
The local and GitHub versions of ReleaseNotes.md match.
|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock Name="txtReleaseNotesNoMatch">
|
||||||
|
The local and GitHub versions of ReleaseNotes.md does not match. Download the latest version from
|
||||||
|
<Hyperlink Name="linkSource" NavigateUri="https://github.com/Micke-K/IntuneManagement">
|
||||||
|
GitHub
|
||||||
|
</Hyperlink>.
|
||||||
|
</TextBlock>
|
||||||
|
<Button Name="btnClose" Content="Close" Width='100' Margin="5,0,0,0" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
Reference in New Issue
Block a user