This commit is contained in:
Mikael Karlsson
2021-07-12 20:24:27 +10:00
parent 06688e7193
commit fb46337f98
31 changed files with 907 additions and 182 deletions

View File

@@ -11,7 +11,7 @@ Objects can be compared based on Properties or Documentatation info.
function Get-ModuleVersion
{
'1.0.4'
'1.0.5'
}
function Invoke-InitializeModule
@@ -370,14 +370,8 @@ function Invoke-BulkCompareNamedObjects
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)
$graphObjects = @(Get-GraphObjects -property $item.ObjectType.ViewProperties -objectType $item.ObjectType)
$nameProp = ?? $item.ObjectType.NameProperty "displayName"
@@ -512,14 +506,8 @@ function Start-BulkCompareExportObjects
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)
$graphObjects = @(Get-GraphObjects -property $item.ObjectType.ViewProperties -objectType $item.ObjectType)
foreach ($fileObj in @(Get-GraphFileObjects $folder -ObjectType $item.ObjectType))
{

View File

@@ -1,6 +1,6 @@
function Get-ModuleVersion
{
'1.0.0'
'1.0.1'
}
function Invoke-InitializeModule
@@ -105,14 +105,8 @@ function Start-BulkCopyObjects
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)
$graphObjects = @(Get-GraphObjects -property $item.ObjectType.ViewProperties -objectType $item.ObjectType)
$nameProp = ?? $item.ObjectType.NameProperty "displayName"

View File

@@ -20,7 +20,7 @@ $global:documentationProviders = @()
function Get-ModuleVersion
{
'1.0.3'
'1.0.4'
}
function Invoke-InitializeModule
@@ -3281,14 +3281,8 @@ function Show-DocumentationForm
foreach($objectType in ($global:currentViewObject.ViewItems | Where GroupId -eq $objGroup.GroupId))
{
Write-Status "Get $($objectType.Title) objects"
$url = $objectType.API
if($objectType.QUERYLIST)
{
$url = "$($url.Trim())?$($objectType.QUERYLIST.Trim())"
}
$graphObjects = @(Get-GraphObjects -Url $url -property $objectType.ViewProperties -objectType $objectType)
$graphObjects = @(Get-GraphObjects -property $objectType.ViewProperties -objectType $objectType)
if($objectType.PostListCommand)
{

View File

@@ -10,7 +10,7 @@ This module will also document some objects based on PowerShell functions
function Get-ModuleVersion
{
'1.0.2'
'1.0.3'
}
function Invoke-InitializeModule
@@ -85,6 +85,13 @@ function Invoke-CDDocumentObject
return [PSCustomObject]@{
Properties = @("Name","Value","Category","SubCategory")
}
}
elseif($type -eq '#microsoft.graph.notificationMessageTemplate')
{
Invoke-CDDocumentNotification $documentationObj
return [PSCustomObject]@{
Properties = @("Name","Value","Category","SubCategory")
}
}
}
@@ -2155,7 +2162,9 @@ function Get-CDDocumentPolicySetValue
}
# ToDo: Add support for all PolicySet items
}
#endregion
#region Custom Profile
function Invoke-CDDocumentCustomOMAUri
{
param($documentationObj)
@@ -2264,6 +2273,168 @@ function Invoke-CDDocumentCustomOMAUri
})
}
}
}
#endregion
#region Notification
function Invoke-CDDocumentNotification
{
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 "Titles.notifications")
###################################################
# Settings
###################################################
$category = Get-LanguageString "TableHeaders.settings"
if($obj.brandingOptions)
{
$brandingOptions = $obj.brandingOptions.Split(',')
}
else
{
$brandingOptions = @()
}
foreach($brandingOption in @('includeCompanyLogo','includeCompanyName','includeContactInformation','includeCompanyPortalLink'))
{
if($brandingOption -eq 'includeCompanyLogo')
{
$label = (Get-LanguageString "NotificationMessage.companyLogo")
}
elseif($brandingOption -eq 'includeCompanyName')
{
$label = (Get-LanguageString "NotificationMessage.companyName")
}
elseif($brandingOption -eq 'includeContactInformation')
{
$label = (Get-LanguageString "NotificationMessage.companyContact")
}
elseif($brandingOption -eq 'includeCompanyPortalLink')
{
$label = (Get-LanguageString "NotificationMessage.iwLink")
}
if(($brandingOption -in $brandingOptions))
{
$value = Get-LanguageString "BooleanActions.enable"
}
else
{
$value = Get-LanguageString "BooleanActions.disable"
}
Add-CustomSettingObject ([PSCustomObject]@{
Name = $label
Value = $value
EntityKey = $brandingOption
Category = $category
SubCategory = $null
})
}
#$subCategory = Get-LanguageString "NotificationMessage.localeLabel"
$subCategory = Get-LanguageString "NotificationMessage.listTitle"
foreach($template in $obj.localizedNotificationMessages)
{
$first,$second = $template.locale.Split('-')
$baseInfo = [cultureinfo]$first
$lng = $baseInfo.EnglishName.ToLower()
if($first -eq 'en')
{
if($second -eq "US")
{
$lng = ($lng + "US")
}
elseif($second -eq "GB")
{
$lng = ($lng + "UK")
}
}
elseif($first -eq 'es')
{
if($second -eq "es")
{
$lng = ($lng + "Spain")
}
elseif($second -eq "mx")
{
$lng = ($lng + "Mexico")
}
}
elseif($first -eq 'fr')
{
if($second -eq "ca")
{
$lng = ($lng + "Canada")
}
elseif($second -eq "fr")
{
$lng = ($lng + "France")
}
}
elseif($first -eq 'pt')
{
if($second -eq "pt")
{
$lng = ($lng + "Portugal")
}
elseif($second -eq "br")
{
$lng = ($lng + "Brazil")
}
}
elseif($first -eq 'zh')
{
if($second -eq "tw")
{
$lng = ($lng + "Traditional")
}
elseif($second -eq "cn")
{
$lng = ($lng + "Simplified")
}
}
elseif($first -eq 'nb')
{
$lng = "norwegian"
}
$label = Get-LanguageString "NotificationMessage.NotificationMessageTemplatesTab.$lng"
if(-not $label) { continue }
$value = $template.subject
if($template.isDefault)
{
$value = ($value + $script:objectSeparator + (Get-LanguageString "NotificationMessage.isDefaultLocale") + ": " + (Get-LanguageString "SettingDetails.trueOption"))
}
$fullValue = ($value + $script:objectSeparator + $template.messageTemplate)
Add-CustomSettingObject ([PSCustomObject]@{
Name = $label
Value = $fullValue
EntityKey = $template.locale
Category = $category
SubCategory = $subCategory
})
}
}
#endregion

View File

@@ -10,7 +10,7 @@ This module is for the Endpoint Manager/Intune View. It manages Export/Import/Co
#>
function Get-ModuleVersion
{
'3.1.6'
'3.1.7'
}
function Invoke-InitializeModule
@@ -27,6 +27,7 @@ function Invoke-InitializeModule
Title = "Application"
Key = "EMAzureApp"
Type = "List"
SelectedValuePath = "ClientId"
ItemsSource = $global:MSGraphGlobalApps
DefaultValue = ""
SubPath = "EndpointManager"
@@ -72,22 +73,6 @@ function Invoke-InitializeModule
SubPath = "EndpointManager"
}) "EndpointManager"
Add-SettingsObject (New-Object PSObject -Property @{
Title = "Show Delete button"
Key = "EMAllowDelete"
Type = "Boolean"
DefaultValue = $false
Description = "Allow deleting individual objectes"
}) "EndpointManager"
Add-SettingsObject (New-Object PSObject -Property @{
Title = "Show Bulk Delete "
Key = "EMAllowBulkDelete"
Type = "Boolean"
DefaultValue = $false
Description = "Allow using bulk delete to delete all objects of selected types"
}) "EndpointManager"
$viewPanel = Get-XamlObject ($global:AppRootFolder + "\Xaml\EndpointManagerPanel.xaml") -AddVariables
Set-EMViewPanel $viewPanel
@@ -157,6 +142,7 @@ function Invoke-InitializeModule
PostFileImportCommand = { Start-PostFileImportEndpointSecurity @args }
#PreCopyCommand = { Start-PreCopyEndpointSecurity @args }
PostCopyCommand = { Start-PostCopyEndpointSecurity @args }
PreUpdateCommand = { Start-PreUpdateEndpointSecurity @args }
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
GroupId = "EndpointSecurity"
})
@@ -170,6 +156,7 @@ function Invoke-InitializeModule
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
Dependencies = @("Locations","Notifications")
PostExportCommand = { Start-PostExportCompliancePolicies @args }
PreUpdateCommand = { Start-PreUpdateCompliancePolicies @args }
GroupId = "CompliancePolicies"
})
@@ -222,6 +209,9 @@ function Invoke-InitializeModule
PreImportCommand = { Start-PreImportESP @args }
PostExportCommand = { Start-PostExportESP @args }
PreDeleteCommand = { Start-PreDeleteEnrollmentRestrictions @args } # Note: Uses same PreDelete as restrictions
PreReplaceCommand = { Start-PreReplaceEnrollmentRestrictions @args } # Note: Uses same PreReplaceCommand as restrictions
PostReplaceCommand = { Start-PostReplaceEnrollmentRestrictions @args } # Note: Uses same PostReplaceCommand as restrictions
PreFilesImportCommand = { Start-PreFilesImportEnrollmentRestrictions @args } # Note: Uses same PreFilesImportCommand as restrictions
QUERYLIST = "`$filter=endsWith(id,'Windows10EnrollmentCompletionPageConfiguration')"
Permissons=@("DeviceManagementServiceConfig.ReadWrite.All")
SkipRemoveProperties = @('Id')
@@ -238,6 +228,9 @@ function Invoke-InitializeModule
PostExportCommand = { Start-PostExportEnrollmentRestrictions @args }
PreImportCommand = { Start-PreImportEnrollmentRestrictions @args }
PreDeleteCommand = { Start-PreDeleteEnrollmentRestrictions @args }
PreReplaceCommand = { Start-PreReplaceEnrollmentRestrictions @args }
PostReplaceCommand = { Start-PostReplaceEnrollmentRestrictions @args }
PreFilesImportCommand = { Start-PreFilesImportEnrollmentRestrictions @args }
Permissons=@("DeviceManagementServiceConfig.ReadWrite.All")
SkipRemoveProperties = @('Id')
AssignmentsType = "enrollmentConfigurationAssignments"
@@ -387,6 +380,7 @@ function Invoke-InitializeModule
CopyDefaultName = "%displayName% Copy" # '-' is not allowed in the name
Permissons=@("DeviceManagementServiceConfig.ReadWrite.All")
PreImportAssignmentsCommand = { Start-PreImportAssignmentsAutoPilot @args }
PreDeleteCommand = { Start-PreDeleteAutoPilot @args }
GroupId = "WinEnrollment"
})
@@ -834,6 +828,58 @@ function Start-PostCopyEndpointSecurity
}
}
function Start-PreUpdateEndpointSecurity
{
param($obj, $objectType, $curObject, $fromObj)
if(-not $fromObj.settings) { return }
$strAPI = "/deviceManagement/intents/$($curObject.Object.id)/updateSettings"
$curObject = Get-GraphObject $curObject.Object $objectType
$curValues = @()
foreach($val in $curObject.Object.settings)
{
if($fromObj.settings | Where { $_.definitionId -eq $val.definitionId}) { continue }
# Set all existing values to null
# Note: This will not remove them from the configured list just set them Not Configured
$curValues += [PSCustomObject]@{
'@odata.type' = $val.'@odata.type'
definitionId = $val.definitionId
id = $val.id
valueJson = "null"
}
}
$curValues += $fromObj.settings
<#
if($curValues.Count -gt 0)
{
$tmpObj = [PSCustomObject]@{
settings = $curValues
}
$json = ConvertTo-Json $tmpObj -Depth 10
# Set all existing values to null
# Note: This will not remove them from the configured list just set them Not Configured
Invoke-GraphRequest -Url $strAPI -Content $json -HttpMethod "POST" | Out-Null
}
#>
$tmpObj = [PSCustomObject]@{
settings = $curValues
}
Start-GraphPreImport $tmpObj.settings
$json = ConvertTo-Json $tmpObj -Depth 10
Invoke-GraphRequest -Url $strAPI -Content $json -HttpMethod "POST" | Out-Null
Remove-Property $obj "templateId"
}
#endregion
#region
@@ -892,6 +938,23 @@ function Start-PostExportCompliancePolicies
}
}
}
function Start-PreUpdateCompliancePolicies
{
param($obj, $objectType, $curObject, $fromObj)
$strAPI = "/deviceManagement/deviceCompliancePolicies/$($curObject.Object.id)/scheduleActionsForRules"
$tmpObj = [PSCustomObject]@{
deviceComplianceScheduledActionForRules = $obj.scheduledActionsForRule
}
$json = ConvertTo-Json $tmpObj -Depth 10
Invoke-GraphRequest -Url $strAPI -Content $json -HttpMethod "POST" | Out-Null
Remove-Property $obj "scheduledActionsForRule"
}
#endregion
#region Intune Branding functions
@@ -1242,7 +1305,7 @@ function Start-PostImportAppProtection
$tmp = $newObject."@odata.type".Split('.')[-1]
$objectClass = Get-GraphObjectClassName $tmp
$response = Invoke-GraphRequest -Url "/deviceAppManagement/$objectClass/$($obj.Id)/targetApps" -Content "{ apps: $(ConvertTo-Json $global:ImportObjectInfo.Apps -Depth 10)}" -HttpMethod POST
Invoke-GraphRequest -Url "/deviceAppManagement/$objectClass/$($obj.Id)/targetApps" -Content "{ apps: $(ConvertTo-Json $global:ImportObjectInfo.Apps -Depth 10)}" -HttpMethod POST | Out-Null
}
catch {}
}
@@ -1504,12 +1567,63 @@ function Start-PreImportPolicySets
{
foreach($prop in ($item.PSObject.Properties | Where {$_.Name -notin $keepProperties}))
{
#if($prop.Name -in $keepProperties) { continue }
Remove-Property $item $prop.Name
}
#@("itemType","displayName","status","errorCode") | foreach { Remove-Property $item $_ }
}
}
function Update-EMPolicySetAssignment
{
param($assignment, $sourceObject, $newObject, $objectType)
$api = "/deviceAppManagement/policySets/$($assignment.SourceId)?`$expand=assignments,items"
$psObj = Invoke-GraphRequest -Url $api -ODataMetadata "Minimal"
if(-not $psObj)
{
return
}
$curItem = $psObj.Items | Where payloadId -eq $sourceObject.Id
if(-not $curItem)
{
return
}
$api = "/deviceAppManagement/policySets/$($assignment.SourceId)/update"
$curItemClone = $curItem | ConvertTo-Json -Depth 10 | ConvertFrom-Json
$newItem = $curItem | ConvertTo-Json -Depth 10 | ConvertFrom-Json
$newItem.payloadId = $newObject.Id
if($newItem.guidedDeploymentTags -is [String] -and [String]::IsNullOrEmpty($newItem.guidedDeploymentTags))
{
$newItem.guidedDeploymentTags = @()
}
$keepProperties = @('@odata.type','payloadId','Settings','guidedDeploymentTags')
#itemType? e.g. #microsoft.graph.iosManagedAppProtection
#priority?
foreach($prop in ($newItem.PSObject.Properties | Where {$_.Name -notin $keepProperties}))
{
Remove-Property $newItem $prop.Name
}
$update = @{}
$update.Add('addedPolicySetItems',@($newItem))
$update.Add('updatedPolicySetItems', @())
$update.Add('deletedPolicySetItems',@($curItemClone.Id))
$json = $update | ConvertTo-Json -Depth 10
Write-Log "Update PolicySet $($psObj.displayName) - Replace: $((Get-GraphObjectName $newObject $objectType))"
Invoke-GraphRequest -Url $api -HttpMethod "POST" -Content $json
}
#endregion
#endregion Locations
@@ -1718,6 +1832,38 @@ function Start-PreDeleteEnrollmentRestrictions
@{ "Delete" = $false }
}
}
function Start-PreReplaceEnrollmentRestrictions
{
param($obj, $objectType, $sourceObj, $fromFile)
if($sourceObj.Priority -eq 0) { @{ "Replace" = $false } }
}
function Start-PostReplaceEnrollmentRestrictions
{
param($obj, $objectType, $sourceObj, $fromFile)
if($sourceObj.Priority -eq 0) { return }
$api = "/deviceManagement/deviceEnrollmentConfigurations/$($obj.id)/setpriority"
$priority = [PSCustomObject]@{
priority = $sourceObj.Priority
}
$json = $priority | ConvertTo-Json -Depth 10
Write-Log "Update priority for $($obj.displayName) to $($sourceObj.Priority)"
Invoke-GraphRequest $api -HttpMethod "POST" -Content $json
}
function Start-PreFilesImportEnrollmentRestrictions
{
param($objectType, $filesToImport)
$filesToImport | sort-object -property @{e={$_.Object.priority}}
}
#endregion
#region ScopeTags
@@ -1736,6 +1882,32 @@ function Start-PreImportAssignmentsAutoPilot
Add-EMAssignmentsToObject $obj $objectType $file $assignments
}
function Start-PreDeleteAutoPilot
{
param($obj, $objectType)
Write-Log "Delete AutoPilot profile assignments"
if(-not $obj.Assignments)
{
$tmpObj = (Get-GraphObject $obj $objectType).Object
}
else
{
$tmpObj = $obj
}
foreach($assignment in $tmpObj.Assignments)
{
if($assignment.Source -ne "direct") { continue }
$api = "/deviceManagement/windowsAutopilotDeploymentProfiles/$($obj.Id)/assignments/$($assignment.Id)"
Invoke-GraphRequest $api -HttpMethod "DELETE"
}
}
#endregion
#region Health Scripts

View File

@@ -10,7 +10,7 @@ This module manages Microsoft Grap fuctions like calling APIs, managing graph ob
#>
function Get-ModuleVersion
{
'3.1.2'
'3.1.3'
}
$global:MSGraphGlobalApps = @(
@@ -26,6 +26,25 @@ function Invoke-InitializeModule
$global:LoadedDependencyObject = $null
$global:MigrationTableCache = $null
$script:lstImportTypes = @(
[PSCustomObject]@{
Name = "Always import"
Value = "alwaysImport"
},
[PSCustomObject]@{
Name = "Skip if object exists"
Value = "skipIfExist"
},
[PSCustomObject]@{
Name = "Replace (Preview)"
Value = "replace"
},
[PSCustomObject]@{
Name = "Update (Experimental)"
Value = "update"
}
)
# Make sure MS Graph settings are added before exiting before App Id and Tenant Id is missing
Write-Log "Add settings and menu items"
@@ -83,12 +102,53 @@ function Invoke-InitializeModule
Description = "Convert AD synched groups to Azure AD group during import if the group does not exist"
}) "ImportExport"
Add-SettingsObject (New-Object PSObject -Property @{
Title = "Import type"
Key = "ImportType"
Type = "List"
ItemsSource = $script:lstImportTypes
DefaultValue = "alwaysImport"
}) "ImportExport"
Add-SettingsObject (New-Object PSObject -Property @{
Title = "Import Assignments"
Key = "ImportAssignments"
Type = "Boolean"
DefaultValue = $true
Description = "Import assignments when importing objects"
Description = "Default value for Import assignments when importing objects"
}) "ImportExport"
Add-SettingsObject (New-Object PSObject -Property @{
Title = "Import Scope (Tags)"
Key = "ImportScopeTags"
Type = "Boolean"
DefaultValue = $true
Description = "Default value for Import Scope (Tags) when importing objects"
}) "ImportExport"
Add-SettingsObject (New-Object PSObject -Property @{
Title = "Show Delete button"
Key = "EMAllowDelete"
Type = "Boolean"
DefaultValue = $false
Description = "Allow deleting individual objectes"
}) "ImportExport"
Add-SettingsObject (New-Object PSObject -Property @{
Title = "Show Bulk Delete "
Key = "EMAllowBulkDelete"
Type = "Boolean"
DefaultValue = $false
Description = "Allow using bulk delete to delete all objects of selected types"
}) "ImportExport"
Add-SettingsObject (New-Object PSObject -Property @{
Title = "Allow update on import (Preview)"
Key = "AllowUpdate"
Type = "Boolean"
DefaultValue = $false
Description = "This will enable the option to update/replace an existing object during import"
}) "ImportExport"
}
@@ -127,6 +187,7 @@ function Get-GraphAppInfo
function Invoke-GraphAuthenticationUpdated
{
$global:MigrationTableCache = $null
$global:MigrationTableCacheId = $null
$global:LoadedDependencyObjects = $null
$global:migFileObj = $null
}
@@ -142,7 +203,7 @@ function Invoke-GraphRequest
$Headers,
[ValidateSet("GET","POST","OPTIONS","DELETE", "PATCH")]
[ValidateSet("GET","POST","OPTIONS","DELETE", "PATCH","PUT")]
[Alias("Method")]
$HttpMethod = "GET",
@@ -240,6 +301,8 @@ function Invoke-GraphRequest
{
throw $global:error[0]
}
if($HttpMethod -eq "PATCH" -and [String]::IsNullOrempty($ret)) { $ret = $true }
}
catch
{
@@ -255,14 +318,16 @@ function Invoke-GraphRequest
function Get-GraphObjects
{
param(
[Array]
[String]
$Url,
[Array]
$property = $null,
[Array]
$exclude,
$SortProperty = "displayName",
$objectType)
$objectType,
[switch]
$SingleObject)
$objects = @()
@@ -274,7 +339,29 @@ function Get-GraphObjects
$params.Add('ODataMetadata',$objectType.ODataMetadata)
}
if(-not $url)
{
$url = $objectType.API
}
if($SingleObject -ne $true -and $objectType.QUERYLIST)
{
if(($url.IndexOf('?')) -eq -1)
{
$url = "$($url.Trim())?$($objectType.QUERYLIST.Trim())"
}
else
{
$url = "$($url.Trim())&$($objectType.QUERYLIST.Trim())" # Risky...does not check that the parameter is already in use
}
}
$graphObjects = Invoke-GraphRequest -Url $url @params
if($SingleObject -ne $true -and $objectType.PostListCommand)
{
$graphObjects = & $objectType.PostListCommand $graphObjects $objectType
}
if($graphObjects -and ($graphObjects | GM -Name Value -MemberType NoteProperty))
{
@@ -341,18 +428,7 @@ function Show-GraphObjects
$global:grdTitle.Visibility = "Visible"
}
$url = $global:curObjectType.API
if($global:curObjectType.QUERYLIST)
{
$url = "$($url.Trim())?$($global:curObjectType.QUERYLIST.Trim())"
}
$graphObjects = @(Get-GraphObjects -Url $url -property $global:curObjectType.ViewProperties -objectType $global:curObjectType)
if($global:curObjectType.PostListCommand)
{
$graphObjects = & $global:curObjectType.PostListCommand $graphObjects $global:curObjectType
}
$graphObjects = @(Get-GraphObjects -property $global:curObjectType.ViewProperties -objectType $global:curObjectType)
if(($graphObjects | measure).Count -eq 0) { return }
@@ -486,7 +562,7 @@ function Get-GraphObject
}
elseif($api.IndexOf("`$expand") -gt 1)
{
$api = ($api + ",")
$api = ($api + ",") # A bit risky...assumes that expand is last in the existing query
}
else
{
@@ -496,7 +572,7 @@ function Get-GraphObject
$api = ($api + ($expand -join ","))
}
$objInfo = Get-GraphObjects -Url $api -property $objectType.ViewProperties -objectType $objectType
$objInfo = Get-GraphObjects -Url $api -property $objectType.ViewProperties -objectType $objectType -SingleObject
if($objInfo -and $objectType.PostGetCommand)
{
@@ -746,18 +822,12 @@ function Show-GraphBulkExportForm
Write-Log "----------------------------------------------------------------"
Write-Log "Export $($item.ObjectType.Title) objects"
Write-Log "----------------------------------------------------------------"
$url = $item.ObjectType.API
if($item.ObjectType.QUERYLIST)
{
$url = "$($url.Trim())?$($item.ObjectType.QUERYLIST.Trim())"
}
try
{
$folder = Get-GraphObjectFolder $item.ObjectType (Get-XamlProperty $script:exportForm "txtExportPath" "Text") (Get-XamlProperty $script:exportForm "chkAddObjectType" "IsChecked") (Get-XamlProperty $script:exportForm "chkAddCompanyName" "IsChecked")
$objects = @(Get-GraphObjects -Url $url -property $item.ObjectType.ViewProperties -objectType $item.ObjectType)
$objects = @(Get-GraphObjects -property $item.ObjectType.ViewProperties -objectType $item.ObjectType)
foreach($obj in $objects)
{
Write-Status "Export $($item.Title): $((Get-GraphObjectName $obj.Object $obj.ObjectType))" -Force
@@ -797,13 +867,44 @@ function Show-GraphImportForm
}
Set-XamlProperty $script:importForm "txtImportPath" "Text" (?? $path (Get-SettingValue "RootFolder"))
Set-XamlProperty $script:importForm "chkImportAssignments" "IsChecked" (Get-SettingValue "ImportAssignments")
Set-XamlProperty $script:importForm "chkImportScopes" "IsChecked" (Get-SettingValue "ImportScopeTags")
Set-XamlProperty $script:importForm "cbImportType" "ItemsSource" $script:lstImportTypes
Set-XamlProperty $script:importForm "cbImportType" "SelectedValue" (Get-SettingValue "ImportType" "alwaysImport")
if((Get-SettingValue "AllowUpdate") -eq $true)
{
Set-XamlProperty $script:importForm "lblImportType" "Visibility" "Visible"
Set-XamlProperty $script:importForm "cbImportType" "Visibility" "Visible"
}
$column = Get-GridCheckboxColumn "Selected"
$global:dgObjectsToImport.Columns.Add($column)
$column.Header.IsChecked = $true # All items are checked by default
$column.Header.add_Click({
foreach($item in $global:dgObjectsToImport.ItemsSource)
{
$item.Selected = $this.IsChecked
}
$global:dgObjectsToImport.Items.Refresh()
}
)
# Add Object type column
$binding = [System.Windows.Data.Binding]::new("fileName")
$column = [System.Windows.Controls.DataGridTextColumn]::new()
$column.Header = "File Name"
$column.IsReadOnly = $true
$column.Binding = $binding
$global:dgObjectsToImport.Columns.Add($column)
Add-XamlEvent $script:importForm "browseImportPath" "add_click" ({
$folder = Get-Folder (Get-XamlProperty $script:importForm "txtImportPath" "Text") "Select root folder for import"
if($folder)
{
Set-XamlProperty $script:importForm "txtImportPath" "Text" $folder
$global:lstFiles.ItemsSource = @(Get-GraphFileObjects $folder)
$global:dgObjectsToImport.ItemsSource = @(Get-GraphFileObjects $folder)
Save-Setting "" "LastUsedFullPath" $folder
Set-XamlProperty $script:importForm "lblMigrationTableInfo" "Content" (Get-MigrationTableInfo)
}
@@ -817,26 +918,30 @@ function Show-GraphImportForm
Add-XamlEvent $script:importForm "btnImportSelected" "add_click" {
Write-Status "Import objects"
Get-GraphDependencyDefaultObjects
foreach ($fileObj in ($global:lstFiles.ItemsSource | Where Selected -eq $true))
$allowUpdate = ((Get-SettingValue "AllowUpdate") -eq $true)
$filesToImport = $global:dgObjectsToImport.ItemsSource | Where Selected -eq $true
if($global:curObjectType.PreFilesImportCommand)
{
Import-GraphFile $fileObj
$filesToImport = & $global:curObjectType.PreFilesImportCommand $global:curObjectType $filesToImport
}
foreach ($fileObj in $filesToImport)
{
if($allowUpdate -and $global:cbImportType.SelectedValue -ne "alwaysImport" -and (Reset-GraphObjet $fileObj $global:dgObjects.ItemsSource))
{
continue
}
Import-GraphFile $fileObj
}
Show-GraphObjects
Show-ModalObject
Write-Status ""
}
Add-XamlEvent $script:importForm "chkCheckAll" "add_click" {
foreach($obj in $global:lstFiles.Items)
{
$obj.Selected = $global:chkCheckAll.IsChecked
}
$global:lstFiles.Items.Refresh()
}
Add-XamlEvent $script:importForm "btnGetFiles" "add_click" {
# Used when the user manually updates the path and the press Get Files
$global:lstFiles.ItemsSource = @(Get-GraphFileObjects $global:txtImportPath.Text)
$global:dgObjectsToImport.ItemsSource = @(Get-GraphFileObjects $global:txtImportPath.Text)
if([IO.Directory]::Exists($global:txtImportPath.Text))
{
Save-Setting "" "LastUsedFullPath" $global:txtImportPath.Text
@@ -848,7 +953,7 @@ function Show-GraphImportForm
if($global:txtImportPath.Text)
{
$global:lstFiles.ItemsSource = @(Get-GraphFileObjects $global:txtImportPath.Text)
$global:dgObjectsToImport.ItemsSource = @(Get-GraphFileObjects $global:txtImportPath.Text)
Set-XamlProperty $script:importForm "lblMigrationTableInfo" "Content" (Get-MigrationTableInfo)
}
@@ -867,7 +972,16 @@ function Show-GraphBulkImportForm
}
Set-XamlProperty $script:importForm "txtImportPath" "Text" (?? $path (Get-SettingValue "RootFolder"))
#Set-XamlProperty $script:importForm "chkAddCompanyName" "IsChecked" (Get-SettingValue "AddCompanyName")
Set-XamlProperty $script:importForm "chkImportAssignments" "IsChecked" (Get-SettingValue "ImportAssignments")
Set-XamlProperty $script:importForm "chkImportScopes" "IsChecked" (Get-SettingValue "ImportScopeTags")
Set-XamlProperty $script:importForm "cbImportType" "ItemsSource" $script:lstImportTypes
Set-XamlProperty $script:importForm "cbImportType" "SelectedValue" (Get-SettingValue "ImportType" "alwaysImport")
if((Get-SettingValue "AllowUpdate") -eq $true)
{
Set-XamlProperty $script:importForm "lblImportType" "Visibility" "Visible"
Set-XamlProperty $script:importForm "cbImportType" "Visibility" "Visible"
}
Add-XamlEvent $script:importForm "browseImportPath" "add_click" ({
$folder = Get-Folder (Get-XamlProperty $script:importForm "txtImportPath" "Text") "Select root folder for import"
@@ -938,6 +1052,8 @@ function Show-GraphBulkImportForm
Get-GraphDependencyDefaultObjects
$importedObjects = 0
$allowUpdate = ((Get-SettingValue "AllowUpdate") -eq $true)
foreach($item in ($script:importObjects | where Selected -eq $true | sort-object -property @{e={$_.ObjectType.ImportOrder}}))
{
Write-Status "Import $($item.ObjectType.Title) objects" -Force
@@ -946,10 +1062,34 @@ function Show-GraphBulkImportForm
Write-Log "----------------------------------------------------------------"
$folder = Get-GraphObjectFolder $item.ObjectType (Get-XamlProperty $script:importForm "txtImportPath" "Text") (Get-XamlProperty $script:importForm "chkAddObjectType" "IsChecked")
$graphObjects = $null
if($allowUpdate -and $global:cbImportType.SelectedValue -ne "alwaysImport")
{
try
{
Write-Status "Get $($item.Title) objects" -Force
$graphObjects = @(Get-GraphObjects -property $item.ObjectType.ViewProperties -objectType $item.ObjectType)
}
catch {}
}
if([IO.Directory]::Exists($folder))
{
foreach ($fileObj in @(Get-GraphFileObjects $folder -ObjectType $item.ObjectType))
$filesToImport = Get-GraphFileObjects $folder -ObjectType $item.ObjectType
if($item.ObjectType.PreFilesImportCommand)
{
$filesToImport = & $item.ObjectType.PreFilesImportCommand $item.ObjectType $filesToImport
}
foreach ($fileObj in @($filesToImport))
{
if($allowUpdate -and $global:cbImportType.SelectedValue -ne "alwaysImport" -and $graphObjects -and (Reset-GraphObjet $fileObj $graphObjects))
{
$importedObjects++
continue
}
Import-GraphFile $fileObj
$importedObjects++
}
@@ -1111,19 +1251,13 @@ function Show-GraphBulkDeleteForm
Write-Log "----------------------------------------------------------------"
Write-Log "Delete $($item.ObjectType.Title) objects"
Write-Log "----------------------------------------------------------------"
$url = $item.ObjectType.API
if($item.ObjectType.QUERYLIST)
{
$url = "$($url.Trim())?$($item.ObjectType.QUERYLIST.Trim())"
}
try
{
Write-Status "Get $($item.Title) objects" -Force
$objects = @(Get-GraphObjects -Url $url -property $item.ObjectType.ViewProperties -objectType $item.ObjectType)
$objects = @(Get-GraphObjects -property $item.ObjectType.ViewProperties -objectType $item.ObjectType)
foreach($obj in $objects)
{
{
Write-Status "Delete $($item.Title): $((Get-GraphObjectName $obj.Object $obj.ObjectType))" -Force
Remove-GraphObject $obj.Object $obj.ObjectType $folder
}
@@ -1225,72 +1359,7 @@ function Import-GraphFile
if($newObj -and $objClone.Assignments -and $global:chkImportAssignments.IsChecked -eq $true)
{
$preConfig = $null
if($file.ObjectType.PreImportAssignmentsCommand)
{
$preConfig = & $file.ObjectType.PreImportAssignmentsCommand $newObj $file.ObjectType $file.FileInfo.FullName $objClone.Assignments
}
###### Import Assignments ######
if($preConfig -isnot [Hashtable]) { $preConfig = @{} }
if($preConfig["Import"] -eq $false) { return } # Assignment managed manually so skip further processing
$api = ?? $preConfig["API"] "$($file.ObjectType.API)/$($newObj.Id)/assign"
$method = ?? $preConfig["Method"] "POST"
$keepProperties = ?? $file.ObjectType.AssignmentProperties @("target")
$keepTargetProperties = ?? $file.ObjectType.AssignmentTargetProperties @("@odata.type","groupId")
$ObjectAssignments = @()
foreach($assignment in $objClone.Assignments)
{
if($assignment.target.UserId -or ($assignment.Source -and $assignment.Source -ne "direct"))
{
# E.g. Source could be PolicySet...so should not be added here
continue
}
$assignment.Id = ""
foreach($prop in $assignment.PSObject.Properties)
{
if($prop.Name -in $keepProperties) { continue }
Remove-Property $assignment $prop.Name
}
foreach($prop in $assignment.target.PSObject.Properties)
{
if($prop.Name -in $keepTargetProperties) { continue }
Remove-Property $assignment.target $prop.Name
}
$ObjectAssignments += $assignment
}
$objClone.Assignments = $ObjectAssignments
if(($objClone.Assignments | measure).Count -gt 0)
{
$json = "{ `"$((?? $file.ObjectType.AssignmentsType "assignments"))`": "
$strAssign = "$((Update-JsonForEnvironment ($objClone.Assignments | ConvertTo-Json -Depth 10)))"
# Array characters [ ] is not included if there is only one assignment
# Added them if they are missing
if($strAssign.Trim().StartsWith("[") -eq $false)
{
$strAssign = (" [ " + $strAssign + " ] ")
}
$json = ($json + $strAssign + "}")
if($json)
{
$objAssign = Invoke-GraphRequest $api -HttpMethod $method -Content $json
}
}
if($assignmentsProcessed -ne $true -and $file.ObjectType.PostImportAssignmentsCommand)
{
& $file.ObjectType.PostImportAssignmentsCommand $newObj $file.ObjectType $file.FileInfo.FullName $objAssign
}
Import-GraphObjectAssignment $newObj $file.ObjectType $objClone.Assignments $file.FileInfo.FullName | Out-Null
}
}
catch
@@ -1299,6 +1368,227 @@ function Import-GraphFile
}
}
function Reset-GraphObjet
{
param($fileObj, $objectList)
$nameProp = ?? $fileObj.ObjectType.NameProperty "displayName"
$curObject = $objectList | Where { $_.Object.$nameProp -eq $fileObj.Object.$nameProp -and $_.Object.'@OData.Type' -eq $fileObj.Object.'@OData.Type' }
if($global:cbImportType.SelectedValue -eq "skipIfExist" -and ($curObject | measure).Count -gt 0)
{
Write-Log "Objects with name $($fileObj.Object.$nameProp) already exists. Object will not be imported"
return $true
}
elseif(($curObject | measure).Count -gt 1)
{
Write-Log "Multiple objects return with name $($fileObj.Object.$nameProp). Object will not be imported or replaced" 2
return $true
}
elseif(($curObject | measure).Count -eq 1)
{
Write-Log "Update $((Get-GraphObjectName $fileObj.Object $fileObj.ObjectType)) with id $($curObject.Object.Id)"
$objectType = $fileObj.ObjectType
# Clone the object before removing properties
$obj = $fileObj.Object | ConvertTo-Json -Depth 10 | ConvertFrom-Json
Start-GraphPreImport $obj $objectType
Remove-Property $obj "Assignments"
Remove-Property $obj "isAssigned"
if($global:cbImportType.SelectedValue -eq "update")
{
$params = @{}
$strAPI = (?? $objectType.APIPATCH $objectType.API) + "/$($curObject.Object.Id)"
$method = "PATCH"
if($objectType.PreUpdateCommand)
{
$ret = & $objectType.PreUpdateCommand $obj $objectType $curObject $fileObj.Object
if($ret -is [HashTable])
{
if($ret.ContainsKey("Import") -and $ret["Import"] -eq $false)
{
# Import handled manually
return $false
}
if($ret.ContainsKey("API"))
{
$strAPI = $ret["API"]
}
if($ret.ContainsKey("Method"))
{
$method = $ret["Method"]
}
if($ret.ContainsKey("AdditionalHeaders") -and $ret["AdditionalHeaders"] -is [HashTable])
{
$params.Add("AdditionalHeaders",$ret["AdditionalHeaders"])
}
}
}
$json = ConvertTo-Json $obj -Depth 10
if($true) #$global:MigrationTableCacheId -ne $global:Organization.Id)
{
# Call Update-JsonForEnvironment before importing the object
# E.g. PolicySets contains references, AppConfiguration policies reference apps etc.
$json = Update-JsonForEnvironment $json
}
$objectUpdated = (Invoke-GraphRequest -Url $strAPI -Content $json -HttpMethod $method @params)
if($objectUpdated)
{
Write-Log "Object updated successfully"
}
if($objectUpdated -and $objectType.PostUpdateCommand)
{
# Reload the updated object
$updatedObject = Get-GraphObject $curObject.Object $objectType
& $objectType.PostUpdateCommand $updatedObject $fileObj
}
return $true
}
elseif($global:cbImportType.SelectedValue -eq "replace")
{
$replace = $true
$import = $true
$delete = $true
if($objectType.PreReplaceCommand)
{
$ret = & $objectType.PreReplaceCommand $obj $objectType $curObject.Object $fileObj
if($ret -is [Hashtable])
{
if($ret["Replace"] -eq $false) { $replace = $false }
if($ret["Import"] -eq $false) { $import = $false }
if($ret["Delete"] -eq $false) { $delete = $false }
}
}
if($import)
{
$newObj = Import-GraphObject $obj $objectType $fileObj.FileInfo.FullName
}
if($newObj -and $replace)
{
if($objectType.PostReplaceCommand)
{
$ret = & $objectType.PostReplaceCommand $newObj $objectType $curObject.Object $fileObj
if($ret -is [Hashtable])
{
if($ret["Delete"] -eq $false) { $delete = $false }
}
}
# Load all information about current object to include assignments
$curObject = Get-GraphObject $curObject.Object $objectType
$refAssignments = $curObject.Object.Assignments | Where { $_.Source -ne "direct" }
if($refAssignments)
{
foreach($refAssignment in $refAssignments)
{
if($refAssignment.Source -eq "policySets")
{
Update-EMPolicySetAssignment $refAssignment $curObject $newObj $objectType
}
}
}
Import-GraphObjectAssignment $newObj $objectType $curObject.Object.Assignments $fileObj.FileInfo.FullName -CopyAssignments | Out-Null
if($delete)
{
Remove-GraphObject $curObject.Object $objectType
}
}
elseif($replace -eq $false) # Might not be 100% correct. Replace -eq $false probably means that the object was patched and not imported eg default enrollment restrictions etc.
{
Write-Log "Failed to import file for $($fileObj.Object.$nameProp) ($($objectType.Title))" 2
}
return $true
}
}
# No object to update. Import the file
return $false
}
function Import-GraphObjectAssignment
{
param($obj, $objectType, $assignments, $fromFile, [switch]$CopyAssignments)
if(($assignments | measure).Count -eq 0) { return }
$preConfig = $null
$clonedAssignments = $assignments | ConvertTo-Json -Depth 10 | ConvertFrom-Json
if($objectType.PreImportAssignmentsCommand)
{
$preConfig = & $objectType.PreImportAssignmentsCommand $obj $objectType $fromFile $clonedAssignments
}
if($preConfig -isnot [Hashtable]) { $preConfig = @{} }
if($preConfig["Import"] -eq $false) { return } # Assignment managed manually so skip further processing
$api = ?? $preConfig["API"] "$($objectType.API)/$($newObj.Id)/assign"
$method = ?? $preConfig["Method"] "POST"
$keepProperties = ?? $objectType.AssignmentProperties @("target")
$keepTargetProperties = ?? $objectType.AssignmentTargetProperties @("@odata.type","groupId")
$ObjectAssignments = @()
foreach($assignment in $clonedAssignments)
{
if(($assignment.target.UserId -and $CopyAssignments -ne $true) -or ($assignment.Source -and $assignment.Source -ne "direct"))
{
# E.g. Source could be PolicySet...so should not be added here
continue
}
$assignment.Id = ""
foreach($prop in $assignment.PSObject.Properties)
{
if($prop.Name -in $keepProperties) { continue }
Remove-Property $assignment $prop.Name
}
foreach($prop in $assignment.target.PSObject.Properties)
{
if($prop.Name -in $keepTargetProperties) { continue }
Remove-Property $assignment.target $prop.Name
}
$ObjectAssignments += $assignment
}
if($ObjectAssignments.Count -eq 0) { return } # No "Direct" assignments
$htAssignments = @{}
$htAssignments.Add((?? $objectType.AssignmentsType "assignments"), @($ObjectAssignments))
$json = $htAssignments | ConvertTo-Json -Depth 10
if($CopyAssignments -ne $true)
{
$json = Update-JsonForEnvironment $json
}
$objAssign = Invoke-GraphRequest $api -HttpMethod $method -Content $json
if($objectType.PostImportAssignmentsCommand)
{
& $objectType.PostImportAssignmentsCommand $obj $objectType $fromFile $objAssign
}
}
#endregion
#region Migration Info
@@ -1564,13 +1854,14 @@ function Get-GraphMigrationObjectsFromFile
$migFileName = Get-GraphMigrationTableForImport
if(-not $migFileName) { return }
$global:MigrationTableCache = @()
$migFileObj = ConvertFrom-Json (Get-Content $migFileName -Raw)
# No need to translate migrated objects in the same environment as exported
if($migFileObj.TenantId -eq $global:organization.Id) { return }
$global:MigrationTableCache = @()
$global:MigrationTableCacheId = $migFileObj.TenantId
Write-Status "Loading migration objects"
if($global:chkImportAssignments.IsChecked -eq $true)
@@ -1940,6 +2231,11 @@ function Import-GraphObject
$newObj = (Invoke-GraphRequest -Url $strAPI -Content $json -HttpMethod $method @params)
if($newObj -and $method -eq "POST")
{
Write-Log "$($objectType.Title) object imported successfully with id: $($newObj.Id)"
}
if($newObj -and $objectType.PostImportCommand)
{
& $objectType.PostImportCommand $newObj $objectType $fromFile