Add files via upload
This commit is contained in:
304
Extensions/AppProtection.psm1
Normal file
304
Extensions/AppProtection.psm1
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Common module functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Add-ModuleMenuItems
|
||||||
|
{
|
||||||
|
Add-MenuItem (New-Object PSObject -Property @{
|
||||||
|
Title = (Get-AppProtectionName)
|
||||||
|
MenuID = "IntuneGraphAPI"
|
||||||
|
Script = [ScriptBlock]{Get-AppProtections}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedImportObjects
|
||||||
|
{
|
||||||
|
$global:importObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-AppProtectionName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Import all app protection/configuration policies"
|
||||||
|
Import-AllAppProtectionObjects (Join-Path $rootFolder (Get-AppProtectionFolderName))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedExportObjects
|
||||||
|
{
|
||||||
|
$global:exportObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-AppProtectionName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Export all app protection/configuration policies"
|
||||||
|
Get-AppProtectionObjects | ForEach-Object { Export-SingleAppProtection $PSItem.Object (Join-Path $rootFolder (Get-AppProtectionFolderName)) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllObjects
|
||||||
|
{
|
||||||
|
param($addObjectSubfolder)
|
||||||
|
|
||||||
|
$subFolder = ""
|
||||||
|
if($addObjectSubfolder) { $subFolder = Get-AppProtectionFolderName }
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Object specific functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Get-AppProtectionName
|
||||||
|
{
|
||||||
|
return "App Protection/Configuration"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-AppProtectionFolderName
|
||||||
|
{
|
||||||
|
return "AppProtection"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-AppProtections
|
||||||
|
{
|
||||||
|
Write-Status "Loading app protections and configurations"
|
||||||
|
$dgObjects.ItemsSource = @(Get-AppProtectionObjects)
|
||||||
|
|
||||||
|
#Scriptblocks that will perform the export tasks. empty by default
|
||||||
|
$script:exportParams = @{}
|
||||||
|
$script:exportParams.Add("ExportAllScript", [ScriptBlock]{
|
||||||
|
Export-AllAppProtections $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
$script:exportParams.Add("ExportSelectedScript", [ScriptBlock]{
|
||||||
|
Export-SelectedAppProtection $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import all files
|
||||||
|
$script:importAll = [ScriptBlock]{
|
||||||
|
Import-AllAppProtectionObjects $global:txtImportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import of selected files
|
||||||
|
$script:importSelected = [ScriptBlock]{
|
||||||
|
Import-AppProtectionObjects $global:lstFiles.ItemsSource -Selected
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will read json files
|
||||||
|
$script:getImportFiles = [ScriptBlock]{
|
||||||
|
Show-FileListBox
|
||||||
|
$global:lstFiles.ItemsSource = @(Get-JsonFileObjects $global:txtImportPath.Text -Exclude "*_Settings.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-DefaultObjectButtons -export ([scriptblock]{Show-DefaultExportGrid @script:exportParams}) -import ([scriptblock]{Show-DefaultImportGrid -ImportAll $script:importAll -ImportSelected $script:importSelected -GetFiles $script:getImportFiles}) -copy ([scriptblock]{Copy-AppProtection})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-AppProtectionObjects
|
||||||
|
{
|
||||||
|
Get-GraphObjects -Url "/deviceAppManagement/managedAppPolicies"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllAppProtections
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
foreach($objTmp in ($global:dgObjects.ItemsSource))
|
||||||
|
{
|
||||||
|
Export-SingleAppProtection $objTmp.Object $path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SelectedAppProtection
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Export-SingleAppProtection $global:dgObjects.SelectedItem.Object $path
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SingleAppProtection
|
||||||
|
{
|
||||||
|
param($psObj, $path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not $psObj) { return }
|
||||||
|
|
||||||
|
if($global:runningBulkExport -ne $true)
|
||||||
|
{
|
||||||
|
if($global:chkAddCompanyName.IsChecked) { $path = Join-Path $path $global:organization.displayName }
|
||||||
|
if($global:chkAddObjectType.IsChecked) { $path = Join-Path $path (Get-AppProtectionFolderName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
Write-Status "Export $($psObj.displayName)"
|
||||||
|
|
||||||
|
$obj = Get-AppProtectionObjectForExport $psObj
|
||||||
|
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
$fileName = "$path\$((Remove-InvalidFileNameChars $obj.displayName)).json"
|
||||||
|
ConvertTo-Json $obj -Depth 5 | Out-File $fileName -Force
|
||||||
|
|
||||||
|
Add-MigrationInfo $obj.assignments
|
||||||
|
}
|
||||||
|
$global:exportedObjects++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-AppProtectionObjectType
|
||||||
|
{
|
||||||
|
param($odataType)
|
||||||
|
|
||||||
|
if($odataType -like "*targetedManagedAppConfiguration*")
|
||||||
|
{
|
||||||
|
"targetedManagedAppConfigurations"
|
||||||
|
|
||||||
|
}
|
||||||
|
elseif($odataType -like "*iosManagedAppProtection*")
|
||||||
|
{
|
||||||
|
"iosManagedAppProtections"
|
||||||
|
}
|
||||||
|
elseif($odataType -like "*androidManagedAppProtection*")
|
||||||
|
{
|
||||||
|
"androidManagedAppProtections"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-AppProtectionObjectForExport
|
||||||
|
{
|
||||||
|
param($obj)
|
||||||
|
|
||||||
|
$objType = Get-AppProtectionObjectType $obj."@odata.type"
|
||||||
|
|
||||||
|
$expand = "?`$expand=assignments"
|
||||||
|
if($objType -eq "targetedManagedAppConfigurations")
|
||||||
|
{
|
||||||
|
$expand += ",Apps"
|
||||||
|
}
|
||||||
|
|
||||||
|
if($objType)
|
||||||
|
{
|
||||||
|
Invoke-GraphRequest -Url "/deviceAppManagement/$objType/$($obj.id)$($expand)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-AppProtection
|
||||||
|
{
|
||||||
|
if(-not $dgObjects.SelectedItem)
|
||||||
|
{
|
||||||
|
[System.Windows.MessageBox]::Show("No object selected`n`nSelect app protection/configuration item you want to copy", "Error", "OK", "Error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = Show-InputDialog "Copy app protection/configuration" "Select name for the new policy" "$($dgObjects.SelectedItem.displayName) - Copy"
|
||||||
|
|
||||||
|
if($ret)
|
||||||
|
{
|
||||||
|
# Export profile
|
||||||
|
Write-Status "Export $($dgObjects.SelectedItem.displayName)"
|
||||||
|
$obj = Get-AppProtectionObjectForExport $dgObjects.SelectedItem.Object
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
# Remove assignment properties
|
||||||
|
Remove-ObjectProperty $obj "assignments"
|
||||||
|
Remove-ObjectProperty $obj "assignments@odata.context"
|
||||||
|
|
||||||
|
# Import new profile
|
||||||
|
$obj.displayName = $ret
|
||||||
|
Import-AppProtection $obj | Out-Null
|
||||||
|
|
||||||
|
$dgObjects.ItemsSource = @(Get-AppProtectionObjects)
|
||||||
|
}
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
|
$dgObjects.Focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AppProtection
|
||||||
|
{
|
||||||
|
param($obj)
|
||||||
|
|
||||||
|
if(($obj | GM -MemberType NoteProperty -Name "Apps"))
|
||||||
|
{
|
||||||
|
$apps = $obj.Apps
|
||||||
|
# Remove apps properties
|
||||||
|
Remove-ObjectProperty $obj "apps"
|
||||||
|
Remove-ObjectProperty $obj "apps@odata.context"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Status "Import $($obj.displayName)"
|
||||||
|
|
||||||
|
$objType = Get-AppProtectionObjectType $obj."@odata.context"
|
||||||
|
|
||||||
|
if($objType)
|
||||||
|
{
|
||||||
|
#Import the app configuration policy
|
||||||
|
$response = Invoke-GraphRequest -Url "/deviceAppManagement/$objType" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST
|
||||||
|
if($response -and $apps)
|
||||||
|
{
|
||||||
|
# Import targeted apps
|
||||||
|
$response2 = Invoke-GraphRequest -Url "/deviceAppManagement/$objType/$($response.Id)/targetApps" -Content "{ apps: $(ConvertTo-Json $apps -Depth 5)}" -HttpMethod POST
|
||||||
|
}
|
||||||
|
$response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AllAppProtectionObjects
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Import-AppProtectionObjects (Get-JsonFileObjects $path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AppProtectionObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$Objects,
|
||||||
|
|
||||||
|
[switch]
|
||||||
|
$Selected
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Status "Import app protection/configuration policies"
|
||||||
|
|
||||||
|
foreach($obj in $objects)
|
||||||
|
{
|
||||||
|
if($Selected -and $obj.Selected -ne $true) { continue }
|
||||||
|
|
||||||
|
Write-Log "Import App Protection/Configuration: $($obj.Object.displayName)"
|
||||||
|
|
||||||
|
$assignments = Get-GraphAssignmentsObject $obj.Object ($obj.FileInfo.DirectoryName + "\" + $obj.FileInfo.BaseName + "_assignments.json")
|
||||||
|
|
||||||
|
$response = Import-AppProtection $obj.Object
|
||||||
|
|
||||||
|
if($response)
|
||||||
|
{
|
||||||
|
$global:importedObjects++
|
||||||
|
|
||||||
|
$dataType = Get-AppProtectionObjectType $response."@odata.context"
|
||||||
|
|
||||||
|
if($dataType)
|
||||||
|
{
|
||||||
|
Import-GraphAssignments $assignments "assignments" "/deviceAppManagement/$dataType/$($response.Id)/assign"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$dgObjects.ItemsSource = @(Get-AppProtectionObjects)
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
924
Extensions/Apps.psm1
Normal file
924
Extensions/Apps.psm1
Normal file
@@ -0,0 +1,924 @@
|
|||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Common module functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Add-ModuleMenuItems
|
||||||
|
{
|
||||||
|
Add-MenuItem (New-Object PSObject -Property @{
|
||||||
|
Title = (Get-ApplicationName)
|
||||||
|
MenuID = "IntuneGraphAPI"
|
||||||
|
Script = [ScriptBlock]{Get-Applications}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedImportObjects
|
||||||
|
{
|
||||||
|
$global:importObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-ApplicationName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Import all applications"
|
||||||
|
Import-AllApplicationObjects (Join-Path $rootFolder (Get-ApplicationFolderName))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedExportObjects
|
||||||
|
{
|
||||||
|
$global:exportObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-ApplicationName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Export all applications"
|
||||||
|
Get-ApplicationObjects | ForEach-Object { Export-SingleApplication $PSItem.Object (Join-Path $rootFolder (Get-ApplicationFolderName)) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllObjects
|
||||||
|
{
|
||||||
|
param($addObjectSubfolder)
|
||||||
|
|
||||||
|
$subFolder = ""
|
||||||
|
if($addObjectSubfolder) { $subFolder = Get-ApplicationFolderName }
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Object specific functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Get-ApplicationName
|
||||||
|
{
|
||||||
|
(Get-ApplicationFolderName)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ApplicationFolderName
|
||||||
|
{
|
||||||
|
"Applications"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-Applications
|
||||||
|
{
|
||||||
|
Write-Status "Loading applications"
|
||||||
|
$dgObjects.ItemsSource = @(Get-ApplicationObjects)
|
||||||
|
|
||||||
|
#Scriptblocks that will perform the export tasks. empty by default
|
||||||
|
$script:exportParams = @{}
|
||||||
|
$script:exportParams.Add("ExportAllScript", [ScriptBlock]{
|
||||||
|
Export-AllApplications $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
$script:exportParams.Add("ExportSelectedScript", [ScriptBlock]{
|
||||||
|
Export-SelectedApplication $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import all files
|
||||||
|
$script:importAll = [ScriptBlock]{
|
||||||
|
Import-AllApplicationObjects $global:txtImportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import of selected files
|
||||||
|
$script:importSelected = [ScriptBlock]{
|
||||||
|
Import-ApplicationObjects $global:lstFiles.ItemsSource -Selected
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will read json files
|
||||||
|
$script:getImportFiles = [ScriptBlock]{
|
||||||
|
Show-FileListBox
|
||||||
|
$global:lstFiles.ItemsSource = @(Get-JsonFileObjects $global:txtImportPath.Text -Exclude "*_Settings.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
$importExtension = (New-Object PSObject -Property @{
|
||||||
|
Xaml = @"
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<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="Packages path" />
|
||||||
|
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="Specify where the packge files for the applications are located. Application will not be imported unless package file is found" />
|
||||||
|
</StackPanel>
|
||||||
|
<Grid Grid.Column='1' Grid.Row='0'>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="5" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<TextBox Text="" Name="txtPackagePath" />
|
||||||
|
<Button Grid.Column="2" Name="btnBrowsePackagePath" Padding="5,0,5,0" Width="50" ToolTip="Browse for folder">...</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
"@
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($form)
|
||||||
|
$script:txtPackagePath = $form.FindName("txtPackagePath")
|
||||||
|
$btnBrowsePackagePath = $form.FindName("btnBrowsePackagePath")
|
||||||
|
$script:txtPackagePath.Text = Get-SettingValue "IntuneAppPackages"
|
||||||
|
|
||||||
|
$btnBrowsePackagePath.Tag = $script:txtPackagePath
|
||||||
|
$btnBrowsePackagePath.Add_Click({
|
||||||
|
$folder = Get-Folder $this.Tag.Text
|
||||||
|
if($folder) { $this.Tag.Text = $folder }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$script:importParams = @{}
|
||||||
|
$script:importParams.Add("Extension", $importExtension)
|
||||||
|
|
||||||
|
Add-DefaultObjectButtons -export ([scriptblock]{Show-DefaultExportGrid @script:exportParams}) -import ([scriptblock]{Show-DefaultImportGrid -ImportAll $script:importAll -ImportSelected $script:importSelected -GetFiles $script:getImportFiles @script:importParams}) # -copy ([scriptblock]{Copy-Application})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ApplicationObjects
|
||||||
|
{
|
||||||
|
Get-GraphObjects -Url "/deviceAppManagement/mobileApps?`$filter=(microsoft.graph.managedApp/appAvailability%20eq%20null%20or%20microsoft.graph.managedApp/appAvailability%20eq%20%27lineOfBusiness%27%20or%20isAssigned%20eq%20true)&`$orderby=displayName"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllApplications
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
foreach($objTmp in ($global:dgObjects.ItemsSource))
|
||||||
|
{
|
||||||
|
Export-SingleApplication $objTmp.Object $path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SelectedApplication
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Export-SingleApplication $global:dgObjects.SelectedItem.Object $path
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SingleApplication
|
||||||
|
{
|
||||||
|
param($psObj, $path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not $psObj) { return }
|
||||||
|
|
||||||
|
if($global:runningBulkExport -ne $true)
|
||||||
|
{
|
||||||
|
if($global:chkAddCompanyName.IsChecked) { $path = Join-Path $path $global:organization.displayName }
|
||||||
|
if($global:chkAddObjectType.IsChecked) { $path = Join-Path $path (Get-ApplicationFolderName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
Write-Status "Export $($psObj.displayName)"
|
||||||
|
$obj = Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$($psObj.id)?`$expand=assignments"
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
$fileName = "$path\$((Remove-InvalidFileNameChars $obj.displayName)).json"
|
||||||
|
ConvertTo-Json $obj -Depth 5 | Out-File $fileName -Force
|
||||||
|
|
||||||
|
Add-MigrationInfo $obj.assignments
|
||||||
|
}
|
||||||
|
$global:exportedObjects++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-Application
|
||||||
|
{
|
||||||
|
if(-not $dgObjects.SelectedItem)
|
||||||
|
{
|
||||||
|
[System.Windows.MessageBox]::Show("No object selected`n`nSelect application item you want to copy", "Error", "OK", "Error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = Show-InputDialog "Copy application" "Select name for the new object" "$($dgObjects.SelectedItem.displayName) - Copy"
|
||||||
|
|
||||||
|
if($ret)
|
||||||
|
{
|
||||||
|
# Export profile
|
||||||
|
Write-Status "Export $($dgObjects.SelectedItem.displayName)"
|
||||||
|
# Convert to Json and back to clone the object
|
||||||
|
$obj = ConvertTo-Json $dgObjects.SelectedItem.Object -Depth 5 | ConvertFrom-Json
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
# Import new profile
|
||||||
|
$obj.displayName = $ret
|
||||||
|
Import-Application $obj | Out-Null
|
||||||
|
|
||||||
|
$dgObjects.ItemsSource = @(Get-ApplicationObjects)
|
||||||
|
}
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
|
$dgObjects.Focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-Application
|
||||||
|
{
|
||||||
|
param($obj)
|
||||||
|
|
||||||
|
Remove-ObjectProperty $obj "uploadState"
|
||||||
|
Remove-ObjectProperty $obj "publishingState"
|
||||||
|
Remove-ObjectProperty $obj "isAssigned"
|
||||||
|
Remove-ObjectProperty $obj "roleScopeTagIds"
|
||||||
|
Remove-ObjectProperty $obj "dependentAppCount"
|
||||||
|
Remove-ObjectProperty $obj "committedContentVersion"
|
||||||
|
Remove-ObjectProperty $obj "id"
|
||||||
|
Remove-ObjectProperty $obj "createdDateTime"
|
||||||
|
Remove-ObjectProperty $obj "lastModifiedDateTime"
|
||||||
|
Remove-ObjectProperty $obj "isFeatured"
|
||||||
|
Remove-ObjectProperty $obj "size"
|
||||||
|
|
||||||
|
Write-Status "Import $($obj.displayName)"
|
||||||
|
|
||||||
|
Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AllApplicationObjects
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Import-ApplicationObjects (Get-JsonFileObjects $path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-ApplicationObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$Objects,
|
||||||
|
|
||||||
|
[switch]
|
||||||
|
$Selected
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Status "Import applications"
|
||||||
|
|
||||||
|
foreach($obj in $objects)
|
||||||
|
{
|
||||||
|
if($Selected -and $obj.Selected -ne $true) { continue }
|
||||||
|
|
||||||
|
if($global:runningBulkImport)
|
||||||
|
{
|
||||||
|
$pkgPath = Get-SettingValue "IntuneAppPackages"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$pkgPath = $script:txtPackagePath.Text
|
||||||
|
}
|
||||||
|
$appFile = "$($pkgPath)\$($obj.Object.fileName)"
|
||||||
|
|
||||||
|
if(Test-Path $appFile)
|
||||||
|
{
|
||||||
|
Write-Log "Import Application: $($obj.Object.displayName) ($($obj.Object."@odata.type"))"
|
||||||
|
|
||||||
|
$assignments = Get-GraphAssignmentsObject $obj.Object ($obj.FileInfo.DirectoryName + "\" + $obj.FileInfo.BaseName + "_assignments.json")
|
||||||
|
$response = Import-Application $obj.Object
|
||||||
|
|
||||||
|
if($response)
|
||||||
|
{
|
||||||
|
$global:importedObjects++
|
||||||
|
Copy-AppPackageToIntune $appFile $response
|
||||||
|
|
||||||
|
Import-GraphAssignments $assignments "mobileAppAssignments" "/deviceAppManagement/mobileApps/$($response.Id)/assign" "#microsoft.graph.mobileAppAssignment"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Log "Application file $appFile not found. Skipping app $($obj.Object.displayName)" 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$dgObjects.ItemsSource = @(Get-ApplicationObjects)
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function Start-DownloadAppContent
|
||||||
|
{
|
||||||
|
param($obj, $path)
|
||||||
|
# Not use but kept for reference. File can be download but it will be encrypted
|
||||||
|
|
||||||
|
$appId = $obj.Id
|
||||||
|
|
||||||
|
$appId = "b2b79110-31f7-40bd-923b-228415c92cdb"
|
||||||
|
|
||||||
|
$appInfo = Invoke-GraphRequest -Url "$($global:graphURL)/deviceAppManagement/mobileApps/$appId"
|
||||||
|
|
||||||
|
$appType = $appInfo.'@odata.type'.Trim('#')
|
||||||
|
|
||||||
|
$contentVersions = Invoke-GraphRequest -Url "$($global:graphURL)/deviceAppManagement/mobileApps/$appId/$appType/contentVersions"
|
||||||
|
|
||||||
|
$contentVerId = $contentVersions.Value[0].id
|
||||||
|
|
||||||
|
$contentFiles = Invoke-GraphRequest "$($global:graphURL)/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVerId/files"
|
||||||
|
|
||||||
|
foreach($tmpFile in $contentFiles)
|
||||||
|
{
|
||||||
|
$contentFile = Invoke-GraphRequest -Url "$($global:graphURL)/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVerId/files/$($tmpFile.Id)"
|
||||||
|
$downloadUrl = $contentFile.azureStorageUri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-AppPackageToIntune
|
||||||
|
{
|
||||||
|
param($packageFile, $appObj)
|
||||||
|
|
||||||
|
$appType = $appObj.'@odata.type'.Trim('#')
|
||||||
|
|
||||||
|
if($appType -eq "microsoft.graph.win32LobApp")
|
||||||
|
{
|
||||||
|
Copy-Win32LOBPackage $packageFile $appObj
|
||||||
|
}
|
||||||
|
elseif($appType -eq "microsoft.graph.windowsMobileMSI")
|
||||||
|
{
|
||||||
|
Copy-MSILOB $packageFile $appObj
|
||||||
|
}
|
||||||
|
elseif($appType -eq "microsoft.graph.iosLOBApp")
|
||||||
|
{
|
||||||
|
Copy-iOSLOB $packageFile $appObj
|
||||||
|
}
|
||||||
|
elseif($appType -eq "microsoft.graph.androidLOBApp")
|
||||||
|
{
|
||||||
|
Copy-AndroidLOB $packageFile $appObj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#########################################################################################
|
||||||
|
#
|
||||||
|
# Upload file functions are based on the following scripts
|
||||||
|
# https://github.com/microsoftgraph/powershell-intune-samples/tree/master/LOB_Application
|
||||||
|
#
|
||||||
|
#########################################################################################
|
||||||
|
|
||||||
|
function Export-IntunewinFileObject
|
||||||
|
{
|
||||||
|
param($intunewinFile, $objectName, $toFile)
|
||||||
|
|
||||||
|
Add-Type -Assembly System.IO.Compression.FileSystem
|
||||||
|
|
||||||
|
$zip = [IO.Compression.ZipFile]::OpenRead($intunewinFile)
|
||||||
|
|
||||||
|
$zip.Entries | where { $_.Name -like $objectName } | foreach {
|
||||||
|
|
||||||
|
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, $toFile, $true)
|
||||||
|
}
|
||||||
|
|
||||||
|
$zip.Dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-MSIFileInformation
|
||||||
|
{
|
||||||
|
param($MSIFile, $Properties)
|
||||||
|
|
||||||
|
$values = @{}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$wiObj = New-Object -ComObject WindowsInstaller.Installer
|
||||||
|
$MSIDb = $wiObj.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $null, $wiObj, @($MSIFile, 0))
|
||||||
|
|
||||||
|
foreach($prop in $Properties)
|
||||||
|
{
|
||||||
|
$Query = "SELECT Value FROM Property WHERE Property = '$($prop)'"
|
||||||
|
$View = $MSIDb.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $MSIDb, ($Query))
|
||||||
|
$View.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View, $null) | Out-Null
|
||||||
|
$Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $null, $View, $null)
|
||||||
|
$values.Add($prop, $Record.GetType().InvokeMember("StringData", "GetProperty", $null, $Record, 1).ToString().Trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
$MSIDb.GetType().InvokeMember("Commit", "InvokeMethod", $null, $MSIDb, $null) | Out-Null
|
||||||
|
$View.GetType().InvokeMember("Close", "InvokeMethod", $null, $View, $null) | Out-Null
|
||||||
|
$MSIDb = $null
|
||||||
|
$View = $null
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Write-Log "Failed to get MSI info from $MSIFile. $($_.Exception.Message)" 3
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($wiObj) | Out-Null
|
||||||
|
[System.GC]::Collect() | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
$values
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-MSILOB
|
||||||
|
{
|
||||||
|
param($msiFile, $appObj)
|
||||||
|
|
||||||
|
if(-not $msiFile -or (Test-Path $msiFile) -eq $false)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$appId = $appObj.Id
|
||||||
|
$appType = $appObj.'@odata.type'.Trim('#')
|
||||||
|
|
||||||
|
$tmpFile = [IO.Path]::GetTempFileName()
|
||||||
|
|
||||||
|
$msiInfo = Get-MSIFileInformation $msiFile @("ProductName", "ProductCode", "ProductVersion", "ProductLanguage")
|
||||||
|
|
||||||
|
if(-not $msiInfo) { return }
|
||||||
|
|
||||||
|
$fileEncryptionInfo = New-IntuneEncryptedFile $msiFile $tmpFile
|
||||||
|
|
||||||
|
[xml]$manifestXML = '<MobileMsiData MsiExecutionContext="Any" MsiRequiresReboot="false" MsiUpgradeCode="" MsiIsMachineInstall="true" MsiIsUserInstall="false" MsiIncludesServices="false" MsiContainsSystemRegistryKeys="false" MsiContainsSystemFolders="false"></MobileMsiData>'
|
||||||
|
$manifestXML.MobileMsiData.MsiUpgradeCode = $msiInfo["ProductCode"]
|
||||||
|
|
||||||
|
$appFileBody = @{
|
||||||
|
"@odata.type" = "#microsoft.graph.mobileAppContentFile"
|
||||||
|
name = [IO.Path]::GetFileName($msiFile)
|
||||||
|
size = (Get-Item $msiFile).Length
|
||||||
|
sizeEncrypted = (Get-Item $tmpFile).Length
|
||||||
|
manifest = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($manifestXML.OuterXml))
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-FileToIntuneApp $appId $appType $tmpFile $appFileBody
|
||||||
|
|
||||||
|
Remove-Item $tmpFile -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-iOSLOB
|
||||||
|
{
|
||||||
|
param($pkgFile, $appObj)
|
||||||
|
|
||||||
|
if(-not $pkgFile -or (Test-Path $pkgFile) -eq $false)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$appId = $appObj.Id
|
||||||
|
$appType = $appObj.'@odata.type'.Trim('#')
|
||||||
|
|
||||||
|
$tmpFile = [IO.Path]::GetTempFileName()
|
||||||
|
|
||||||
|
$fileEncryptionInfo = New-IntuneEncryptedFile $pkgFile $tmpFile
|
||||||
|
|
||||||
|
[string]$manifestStr = '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>items</key><array><dict><key>assets</key><array><dict><key>kind</key><string>software-package</string><key>url</key><string>{UrlPlaceHolder}</string></dict></array><key>metadata</key><dict><key>AppRestrictionPolicyTemplate</key> <string>http://management.microsoft.com/PolicyTemplates/AppRestrictions/iOS/v1</string><key>AppRestrictionTechnology</key><string>Windows Intune Application Restrictions Technology for iOS</string><key>IntuneMAMVersion</key><string></string><key>CFBundleSupportedPlatforms</key><array><string>iPhoneOS</string></array><key>MinimumOSVersion</key><string>9.0</string><key>bundle-identifier</key><string>bundleid</string><key>bundle-version</key><string>bundleversion</string><key>kind</key><string>software</string><key>subtitle</key><string>LaunchMeSubtitle</string><key>title</key><string>bundletitle</string></dict></dict></array></dict></plist>'
|
||||||
|
|
||||||
|
$manifestStr = $manifestStr.replace("bundleid", $appObj.bundleId)
|
||||||
|
$manifestStr = $manifestStr.replace("bundleversion",$appObj.identityVersion)
|
||||||
|
$manifestStr = $manifestStr.replace("bundletitle",$appObj.$displayName)
|
||||||
|
|
||||||
|
$appFileBody = @{
|
||||||
|
"@odata.type" = "#microsoft.graph.mobileAppContentFile"
|
||||||
|
name = [IO.Path]::GetFileName($pkgFile)
|
||||||
|
size = (Get-Item $pkgFile).Length
|
||||||
|
sizeEncrypted = (Get-Item $tmpFile).Length
|
||||||
|
manifest = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($manifestStr))
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-FileToIntuneApp $appId $appType $tmpFile $appFileBody
|
||||||
|
|
||||||
|
Remove-Item $tmpFile -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-AndroidLOB
|
||||||
|
{
|
||||||
|
param($pkgFile, $appObj)
|
||||||
|
|
||||||
|
if(-not $pkgFile -or (Test-Path $pkgFile) -eq $false)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$appId = $appObj.Id
|
||||||
|
$appType = $appObj.'@odata.type'.Trim('#')
|
||||||
|
|
||||||
|
$tmpFile = [IO.Path]::GetTempFileName()
|
||||||
|
|
||||||
|
$fileEncryptionInfo = New-IntuneEncryptedFile $pkgFile $tmpFile
|
||||||
|
|
||||||
|
[xml]$manifestXML = '<?xml version="1.0" encoding="utf-8"?><AndroidManifestProperties xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Package>com.leadapps.android.radio.ncp</Package><PackageVersionCode>10</PackageVersionCode><PackageVersionName>1.0.5.4</PackageVersionName><ApplicationName>A_Online_Radio_1.0.5.4.apk</ApplicationName><MinSdkVersion>3</MinSdkVersion><AWTVersion></AWTVersion></AndroidManifestProperties>'
|
||||||
|
|
||||||
|
$manifestXML.AndroidManifestProperties.Package = $appObj.identityName
|
||||||
|
$manifestXML.AndroidManifestProperties.PackageVersionCode = $appObj.versionCode
|
||||||
|
$manifestXML.AndroidManifestProperties.PackageVersionName = $appObj.versionName
|
||||||
|
$manifestXML.AndroidManifestProperties.ApplicationName = [IO.Path]::GetFileName($pkgFile)
|
||||||
|
|
||||||
|
$appFileBody = @{
|
||||||
|
"@odata.type" = "#microsoft.graph.mobileAppContentFile"
|
||||||
|
name = [IO.Path]::GetFileName($pkgFile)
|
||||||
|
size = (Get-Item $pkgFile).Length
|
||||||
|
sizeEncrypted = (Get-Item $tmpFile).Length
|
||||||
|
manifest = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($manifestXML.OuterXml))
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-FileToIntuneApp $appId $appType $tmpFile $appFileBody
|
||||||
|
|
||||||
|
Remove-Item $tmpFile -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-Win32LOBPackage
|
||||||
|
{
|
||||||
|
param($intunewinFile, $appObj)
|
||||||
|
|
||||||
|
if(-not $intunewinFile -or (Test-Path $intunewinFile) -eq $false)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$appId = $appObj.Id
|
||||||
|
$appType = $appObj.'@odata.type'.Trim('#')
|
||||||
|
|
||||||
|
#Extract the detection.xml from the intunewin file
|
||||||
|
|
||||||
|
$tmpFile = [IO.Path]::GetTempFileName()
|
||||||
|
|
||||||
|
Export-IntunewinFileObject $intunewinFile "detection.xml" $tmpFile
|
||||||
|
|
||||||
|
[xml]$DetectionXML = Get-Content $tmpFile
|
||||||
|
|
||||||
|
Remove-Item -Path $tmpFile
|
||||||
|
|
||||||
|
# Get encryption info from detection.xml and build encryptionInfo object
|
||||||
|
|
||||||
|
$encryptionInfo = @{}
|
||||||
|
$encryptionInfo.encryptionKey = $DetectionXML.ApplicationInfo.EncryptionInfo.EncryptionKey
|
||||||
|
$encryptionInfo.macKey = $DetectionXML.ApplicationInfo.EncryptionInfo.macKey
|
||||||
|
$encryptionInfo.initializationVector = $DetectionXML.ApplicationInfo.EncryptionInfo.initializationVector
|
||||||
|
$encryptionInfo.mac = $DetectionXML.ApplicationInfo.EncryptionInfo.mac
|
||||||
|
$encryptionInfo.profileIdentifier = "ProfileVersion1"
|
||||||
|
$encryptionInfo.fileDigest = $DetectionXML.ApplicationInfo.EncryptionInfo.fileDigest
|
||||||
|
$encryptionInfo.fileDigestAlgorithm = $DetectionXML.ApplicationInfo.EncryptionInfo.fileDigestAlgorithm
|
||||||
|
|
||||||
|
$tmpIntunewinPath = ([IO.Path]::GetTempPath() + [Guid]::NewGuid().ToString("n"))
|
||||||
|
mkdir $tmpIntunewinPath | Out-Null
|
||||||
|
$tmpIntunewinFile = $tmpIntunewinPath + "\" + $DetectionXML.ApplicationInfo.FileName
|
||||||
|
|
||||||
|
# Extract the encrypted file from the intunewin file
|
||||||
|
Export-IntunewinFileObject $intunewinFile $DetectionXML.ApplicationInfo.FileName $tmpIntunewinFile
|
||||||
|
|
||||||
|
# Create mobileAppContentFile object for the file
|
||||||
|
$fileEncryptionInfo = @{}
|
||||||
|
$fileEncryptionInfo.fileEncryptionInfo = $encryptionInfo
|
||||||
|
|
||||||
|
$fileBody = @{
|
||||||
|
"@odata.type" = "#microsoft.graph.mobileAppContentFile"
|
||||||
|
name = $DetectionXML.ApplicationInfo.FileName
|
||||||
|
size = [int64]$DetectionXML.ApplicationInfo.UnencryptedContentSize
|
||||||
|
sizeEncrypted = (Get-Item $tmpIntunewinFile).Length
|
||||||
|
manifest = $null
|
||||||
|
isDependency = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-FileToIntuneApp $appId $appType $tmpIntunewinFile $fileBody
|
||||||
|
|
||||||
|
# Remove extracted inintunewin file
|
||||||
|
Remove-Item $tmpIntunewinPath -Force -Recurse
|
||||||
|
}
|
||||||
|
|
||||||
|
function Add-FileToIntuneApp
|
||||||
|
{
|
||||||
|
param($appId, $appType, $appFile, $fileBody)
|
||||||
|
|
||||||
|
$contentVersion = Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions"
|
||||||
|
$contentVersionId = $contentVersion.value[0].id
|
||||||
|
$fileObj = Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVersionId/files" -HttpMethod POST -Content (ConvertTo-Json $fileBody -Depth 5)
|
||||||
|
|
||||||
|
if(-not $fileObj)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
# Wait for Azure storage URI
|
||||||
|
$fileObj = Wait-IntuneFileState "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVersionId/files/$($fileObj.Id)" "AzureStorageUriRequest"
|
||||||
|
if(-not $fileObj)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
# Upload file
|
||||||
|
Send-IntuneFileToAzureStorage $fileObj.azureStorageUri $appFile "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVersionId/files/$($fileObj.Id)"
|
||||||
|
|
||||||
|
# Commit the file
|
||||||
|
$reponse = Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVersionId/files/$($fileObj.Id)/commit" -HttpMethod POST -Content (ConvertTo-Json $fileEncryptionInfo -Depth 5)
|
||||||
|
|
||||||
|
Wait-IntuneFileState "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVersionId/files/$($fileObj.Id)" "CommitFile"
|
||||||
|
|
||||||
|
# Commit the content version
|
||||||
|
$commitAppBody = @{
|
||||||
|
"@odata.type" = "#$appType"
|
||||||
|
committedContentVersion = $contentVersionId
|
||||||
|
}
|
||||||
|
|
||||||
|
$reponse = Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$appId" -HttpMethod PATCH -Content (ConvertTo-Json $commitAppBody -Depth 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Wait-IntuneFileState
|
||||||
|
{
|
||||||
|
param($fileUri, $state, $maxWait = 60)
|
||||||
|
|
||||||
|
Write-Status "Wait for state $state"
|
||||||
|
|
||||||
|
$endWait = (Get-Date).AddMinutes($maxWait)
|
||||||
|
|
||||||
|
$successState = "$($state)Success"
|
||||||
|
$pendingState = "$($state)Pending"
|
||||||
|
$failedState = "$($state)Failed"
|
||||||
|
$timedOutState = "$($state)TimedOut"
|
||||||
|
|
||||||
|
$file = $null
|
||||||
|
$succes = $false
|
||||||
|
|
||||||
|
while ((Get-Date) -lt $endWait)
|
||||||
|
{
|
||||||
|
$file = Invoke-GraphRequest -Url $fileUri
|
||||||
|
|
||||||
|
if ($file.uploadState -eq $successState)
|
||||||
|
{
|
||||||
|
$succes = $true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
elseif ($file.uploadState -ne $pendingState)
|
||||||
|
{
|
||||||
|
Write-Log "Failed to upload file. State: $($file.uploadState)" 3
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Start-Sleep -s 5
|
||||||
|
}
|
||||||
|
|
||||||
|
if($succes -eq $false)
|
||||||
|
{
|
||||||
|
Write-Log "Wait for state operation timed out" 3
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$file
|
||||||
|
}
|
||||||
|
|
||||||
|
function Send-IntuneFileToAzureStorage
|
||||||
|
{
|
||||||
|
param($sasUri, $filepath, $fileUri)
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$chunkSizeInBytes = 5MB
|
||||||
|
|
||||||
|
# Start the timer for SAS URI renewal.
|
||||||
|
$sasRenewalTimer = [System.Diagnostics.Stopwatch]::StartNew()
|
||||||
|
|
||||||
|
# Find the file size and open the file.
|
||||||
|
$fileSize = (Get-Item $filepath).length
|
||||||
|
$chunks = [Math]::Ceiling($fileSize / $chunkSizeInBytes)
|
||||||
|
$reader = New-Object System.IO.BinaryReader([System.IO.File]::Open($filepath, [System.IO.FileMode]::Open))
|
||||||
|
$position = $reader.BaseStream.Seek(0, [System.IO.SeekOrigin]::Begin)
|
||||||
|
|
||||||
|
# Upload each chunk. Check whether a SAS URI renewal is required after each chunk is uploaded and renew if needed.
|
||||||
|
$ids = @()
|
||||||
|
|
||||||
|
for ($chunk = 0; $chunk -lt $chunks; $chunk++)
|
||||||
|
{
|
||||||
|
|
||||||
|
$id = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($chunk.ToString("0000")))
|
||||||
|
$ids += $id
|
||||||
|
|
||||||
|
$start = $chunk * $chunkSizeInBytes
|
||||||
|
$length = [Math]::Min($chunkSizeInBytes, $fileSize - $start)
|
||||||
|
$bytes = $reader.ReadBytes($length)
|
||||||
|
|
||||||
|
$currentChunk = $chunk + 1
|
||||||
|
|
||||||
|
Write-Status "Uploading file to Azure Storage`n`nUploading chunk $currentChunk of $chunks ($(($currentChunk / $chunks*100))%)"
|
||||||
|
|
||||||
|
Write-AzureStorageChunk $sasUri $id $bytes
|
||||||
|
|
||||||
|
if ($currentChunk -lt $chunks -and $sasRenewalTimer.ElapsedMilliseconds -ge 450000)
|
||||||
|
{
|
||||||
|
Request-RenewAzureStorageUpload $fileUri
|
||||||
|
$sasRenewalTimer.Restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$reader.Close()
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if ($reader -ne $null) { $reader.Dispose() }
|
||||||
|
}
|
||||||
|
|
||||||
|
# Finalize the upload.
|
||||||
|
$uploadResponse = Set-FinalizeAzureStorageUpload $sasUri $ids
|
||||||
|
}
|
||||||
|
|
||||||
|
function Request-RenewAzureStorageUpload
|
||||||
|
{
|
||||||
|
param($fileUri)
|
||||||
|
|
||||||
|
$fileObj = Invoke-GraphRequest -Url "$fileUri/renewUpload" -HttpMethod POST
|
||||||
|
|
||||||
|
$file = Wait-IntuneFileState $fileUri "AzureStorageUriRenewal" $azureStorageRenewSasUriBackOffTimeInSeconds
|
||||||
|
}
|
||||||
|
|
||||||
|
function Set-FinalizeAzureStorageUpload
|
||||||
|
{
|
||||||
|
param($sasUri, $ids)
|
||||||
|
|
||||||
|
$uri = "$sasUri&comp=blocklist"
|
||||||
|
|
||||||
|
if(($uri -notmatch "^http://|^https://"))
|
||||||
|
{
|
||||||
|
$uri = $global:graphURL + "/" + $uri.TrimStart('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
$request = "PUT $uri"
|
||||||
|
|
||||||
|
$xml = '<?xml version="1.0" encoding="utf-8"?><BlockList>'
|
||||||
|
foreach ($id in $ids)
|
||||||
|
{
|
||||||
|
$xml += "<Latest>$id</Latest>"
|
||||||
|
}
|
||||||
|
$xml += '</BlockList>'
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Invoke-RestMethod $uri -Method Put -Body $xml
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Write-Log "Failed to finilize upload. $($_.Exception.Message)" 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-AzureStorageChunk
|
||||||
|
{
|
||||||
|
param($sasUri, $id, $body)
|
||||||
|
|
||||||
|
$uri = "$sasUri&comp=block&blockid=$id"
|
||||||
|
|
||||||
|
if(($uri -notmatch "^http://|^https://"))
|
||||||
|
{
|
||||||
|
$uri = $global:graphURL + "/" + $uri.TrimStart('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
$request = "PUT $uri"
|
||||||
|
|
||||||
|
$iso = [System.Text.Encoding]::GetEncoding("iso-8859-1")
|
||||||
|
$encodedBody = $iso.GetString($body)
|
||||||
|
$headers = @{
|
||||||
|
"x-ms-blob-type" = "BlockBlob"
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$response = Invoke-WebRequest $uri -Method Put -Headers $headers -Body $encodedBody
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Write-Log "Failed to upload file chunk. $($_.Exception.Message)" 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-IntuneKey
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$aes = [System.Security.Cryptography.Aes]::Create()
|
||||||
|
$aesProvider = New-Object System.Security.Cryptography.AesCryptoServiceProvider
|
||||||
|
$aesProvider.GenerateKey()
|
||||||
|
$aesProvider.Key
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if ($aesProvider -ne $null) { $aesProvider.Dispose() }
|
||||||
|
if ($aes -ne $null) { $aes.Dispose() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-IntuneKeyIV
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$aes = [System.Security.Cryptography.Aes]::Create()
|
||||||
|
$aes.IV
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if ($aes -ne $null) { $aes.Dispose() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Start-EncryptFileWithIV
|
||||||
|
{
|
||||||
|
param($sourceFile, $targetFile, $encryptionKey, $hmacKey, $initializationVector)
|
||||||
|
|
||||||
|
$bufferBlockSize = 1024 * 4
|
||||||
|
$computedMac = $null
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$aes = [System.Security.Cryptography.Aes]::Create()
|
||||||
|
$hmacSha256 = New-Object System.Security.Cryptography.HMACSHA256
|
||||||
|
$hmacSha256.Key = $hmacKey
|
||||||
|
$hmacLength = $hmacSha256.HashSize / 8
|
||||||
|
|
||||||
|
$buffer = New-Object byte[] $bufferBlockSize
|
||||||
|
$bytesRead = 0
|
||||||
|
|
||||||
|
$targetStream = [System.IO.File]::Open($targetFile, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write, [System.IO.FileShare]::Read)
|
||||||
|
$targetStream.Write($buffer, 0, $hmacLength + $initializationVector.Length)
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$encryptor = $aes.CreateEncryptor($encryptionKey, $initializationVector)
|
||||||
|
$sourceStream = [System.IO.File]::Open($sourceFile, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::Read)
|
||||||
|
$cryptoStream = New-Object System.Security.Cryptography.CryptoStream -ArgumentList @($targetStream, $encryptor, [System.Security.Cryptography.CryptoStreamMode]::Write)
|
||||||
|
|
||||||
|
$targetStream = $null
|
||||||
|
while (($bytesRead = $sourceStream.Read($buffer, 0, $bufferBlockSize)) -gt 0)
|
||||||
|
{
|
||||||
|
$cryptoStream.Write($buffer, 0, $bytesRead)
|
||||||
|
$cryptoStream.Flush()
|
||||||
|
}
|
||||||
|
$cryptoStream.FlushFinalBlock()
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if ($cryptoStream -ne $null) { $cryptoStream.Dispose() }
|
||||||
|
if ($sourceStream -ne $null) { $sourceStream.Dispose() }
|
||||||
|
if ($encryptor -ne $null) { $encryptor.Dispose() }
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$finalStream = [System.IO.File]::Open($targetFile, [System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::Read)
|
||||||
|
|
||||||
|
$finalStream.Seek($hmacLength, [System.IO.SeekOrigin]::Begin) > $null
|
||||||
|
$finalStream.Write($initializationVector, 0, $initializationVector.Length)
|
||||||
|
$finalStream.Seek($hmacLength, [System.IO.SeekOrigin]::Begin) > $null
|
||||||
|
|
||||||
|
$hmac = $hmacSha256.ComputeHash($finalStream)
|
||||||
|
$computedMac = $hmac
|
||||||
|
|
||||||
|
$finalStream.Seek(0, [System.IO.SeekOrigin]::Begin) > $null
|
||||||
|
$finalStream.Write($hmac, 0, $hmac.Length)
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if ($finalStream -ne $null) { $finalStream.Dispose() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if ($targetStream -ne $null) { $targetStream.Dispose() }
|
||||||
|
if ($aes -ne $null) { $aes.Dispose() }
|
||||||
|
}
|
||||||
|
|
||||||
|
$computedMac
|
||||||
|
}
|
||||||
|
|
||||||
|
function New-IntuneEncryptedFile
|
||||||
|
{
|
||||||
|
param($sourceFile, $targetFile)
|
||||||
|
|
||||||
|
$encryptionKey = Get-IntuneKey
|
||||||
|
$hmacKey = Get-IntuneKey
|
||||||
|
$initializationVector = Get-IntuneKeyIV
|
||||||
|
|
||||||
|
# Create the encrypted target file and compute the HMAC value.
|
||||||
|
$mac = Start-EncryptFileWithIV $sourceFile $targetFile $encryptionKey $hmacKey $initializationVector
|
||||||
|
|
||||||
|
# Compute the SHA256 hash of the source file and convert the result to bytes.
|
||||||
|
$fileDigest = (Get-FileHash $sourceFile -Algorithm SHA256).Hash
|
||||||
|
$fileDigestBytes = New-Object byte[] ($fileDigest.Length / 2)
|
||||||
|
for ($i = 0; $i -lt $fileDigest.Length; $i += 2)
|
||||||
|
{
|
||||||
|
$fileDigestBytes[$i / 2] = [System.Convert]::ToByte($fileDigest.Substring($i, 2), 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Return an object that will serialize correctly to the file commit Graph API.
|
||||||
|
$encryptionInfo = @{}
|
||||||
|
$encryptionInfo.encryptionKey = [System.Convert]::ToBase64String($encryptionKey)
|
||||||
|
$encryptionInfo.macKey = [System.Convert]::ToBase64String($hmacKey)
|
||||||
|
$encryptionInfo.initializationVector = [System.Convert]::ToBase64String($initializationVector)
|
||||||
|
$encryptionInfo.mac = [System.Convert]::ToBase64String($mac)
|
||||||
|
$encryptionInfo.profileIdentifier = "ProfileVersion1"
|
||||||
|
$encryptionInfo.fileDigest = [System.Convert]::ToBase64String($fileDigestBytes)
|
||||||
|
$encryptionInfo.fileDigestAlgorithm = "SHA256"
|
||||||
|
|
||||||
|
$fileEncryptionInfo = @{}
|
||||||
|
$fileEncryptionInfo.fileEncryptionInfo = $encryptionInfo
|
||||||
|
|
||||||
|
$fileEncryptionInfo
|
||||||
|
}
|
||||||
238
Extensions/AutoPilot.psm1
Normal file
238
Extensions/AutoPilot.psm1
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Common module functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Add-ModuleMenuItems
|
||||||
|
{
|
||||||
|
Add-MenuItem (New-Object PSObject -Property @{
|
||||||
|
Title = (Get-AutoPilotName)
|
||||||
|
MenuID = "IntuneGraphAPI"
|
||||||
|
Script = [ScriptBlock]{Get-AutoPilots}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedImportObjects
|
||||||
|
{
|
||||||
|
$global:importObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-AutoPilotName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Import all AutoPilot policies"
|
||||||
|
Import-AllAutoPilotObjects (Join-Path $rootFolder (Get-AutoPilotFolderName))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedExportObjects
|
||||||
|
{
|
||||||
|
$global:exportObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-AutoPilotName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Export all AutoPilot policies"
|
||||||
|
Get-AutoPilotObjects | ForEach-Object { Export-SingleAutoPilotObject $PSItem.Object (Join-Path $rootFolder (Get-AutoPilotFolderName)) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllObjects
|
||||||
|
{
|
||||||
|
param($addObjectSubfolder)
|
||||||
|
|
||||||
|
$subFolder = ""
|
||||||
|
if($addObjectSubfolder) { $subFolder = Get-AutoPilotFolderName }
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Object specific functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Get-AutoPilotName
|
||||||
|
{
|
||||||
|
(Get-AutoPilotFolderName)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-AutoPilotFolderName
|
||||||
|
{
|
||||||
|
"AutoPilot"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-AutoPilots
|
||||||
|
{
|
||||||
|
Write-Status "Loading AutoPilot profiles"
|
||||||
|
$dgObjects.ItemsSource = @(Get-AutoPilotObjects)
|
||||||
|
|
||||||
|
#Scriptblocks that will perform the export tasks. empty by default
|
||||||
|
$script:exportParams = @{}
|
||||||
|
$script:exportParams.Add("ExportAllScript", [ScriptBlock]{
|
||||||
|
Export-AllAutoPilots $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
$script:exportParams.Add("ExportSelectedScript", [ScriptBlock]{
|
||||||
|
Export-SelectedAutoPilotObject $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import all files
|
||||||
|
$script:importAll = [ScriptBlock]{
|
||||||
|
Import-AllAutoPilotObjects $global:txtImportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import of selected files
|
||||||
|
$script:importSelected = [ScriptBlock]{
|
||||||
|
Import-AutoPilotObjects $global:lstFiles.ItemsSource -Selected
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will read json files
|
||||||
|
$script:getImportFiles = [ScriptBlock]{
|
||||||
|
Show-FileListBox
|
||||||
|
$global:lstFiles.ItemsSource = @(Get-JsonFileObjects $global:txtImportPath.Text -Exclude "*_Settings.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-DefaultObjectButtons -export ([scriptblock]{Show-DefaultExportGrid @script:exportParams}) -import ([scriptblock]{Show-DefaultImportGrid -ImportAll $script:importAll -ImportSelected $script:importSelected -GetFiles $script:getImportFiles}) -copy ([scriptblock]{Copy-AutoPilot})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-AutoPilotObjects
|
||||||
|
{
|
||||||
|
Get-GraphObjects -Url "/deviceManagement/windowsAutopilotDeploymentProfiles"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllAutoPilots
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
foreach($objTmp in ($global:dgObjects.ItemsSource))
|
||||||
|
{
|
||||||
|
Export-SingleAutoPilotObject $objTmp.Object $path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SelectedAutoPilotObject
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Export-SingleAutoPilotObject $global:dgObjects.SelectedItem.Object $path
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SingleAutoPilotObject
|
||||||
|
{
|
||||||
|
param($psObj, $path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not $psObj) { return }
|
||||||
|
|
||||||
|
if($global:runningBulkExport -ne $true)
|
||||||
|
{
|
||||||
|
if($global:chkAddCompanyName.IsChecked) { $path = Join-Path $path $global:organization.displayName }
|
||||||
|
if($global:chkAddObjectType.IsChecked) { $path = Join-Path $path (Get-AutoPilotFolderName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
Write-Status "Export $($psObj.displayName)"
|
||||||
|
$obj = Invoke-GraphRequest -Url "/deviceManagement/windowsAutopilotDeploymentProfiles/$($psObj.id)?`$expand=assignments"
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
$fileName = "$path\$((Remove-InvalidFileNameChars $obj.displayName)).json"
|
||||||
|
ConvertTo-Json $obj -Depth 5 | Out-File $fileName -Force
|
||||||
|
|
||||||
|
Add-MigrationInfo $obj.assignments
|
||||||
|
}
|
||||||
|
$global:exportedObjects++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-AutoPilot
|
||||||
|
{
|
||||||
|
if(-not $dgObjects.SelectedItem)
|
||||||
|
{
|
||||||
|
[System.Windows.MessageBox]::Show("No object selected`n`nSelect AutoPilot item you want to copy", "Error", "OK", "Error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = Show-InputDialog "Copy AutoPilot" "Select name for the new object" "$($dgObjects.SelectedItem.displayName) Copy"
|
||||||
|
|
||||||
|
if($ret)
|
||||||
|
{
|
||||||
|
# Export profile
|
||||||
|
Write-Status "Export $($dgObjects.SelectedItem.displayName)"
|
||||||
|
# Convert to Json and back to clone the object
|
||||||
|
$obj = ConvertTo-Json $dgObjects.SelectedItem.Object -Depth 5 | ConvertFrom-Json
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
# Import new profile
|
||||||
|
$obj.displayName = Remove-InvalidFileNameChars $ret
|
||||||
|
Import-AutoPilot $obj | Out-Null
|
||||||
|
|
||||||
|
$dgObjects.ItemsSource = @(Get-AutoPilotObjects)
|
||||||
|
}
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
|
$dgObjects.Focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AutoPilot
|
||||||
|
{
|
||||||
|
param($obj)
|
||||||
|
|
||||||
|
Write-Status "Import $($obj.displayName)"
|
||||||
|
|
||||||
|
Invoke-GraphRequest -Url "/deviceManagement/windowsAutopilotDeploymentProfiles" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AllAutoPilotObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$path = "$env:Temp"
|
||||||
|
)
|
||||||
|
|
||||||
|
Import-AutoPilotObjects (Get-JsonFileObjects $path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AutoPilotObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$Objects,
|
||||||
|
|
||||||
|
[switch]
|
||||||
|
$Selected
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Status "Import AutoPilot profiles"
|
||||||
|
|
||||||
|
foreach($obj in $objects)
|
||||||
|
{
|
||||||
|
if($Selected -and $obj.Selected -ne $true) { continue }
|
||||||
|
|
||||||
|
Write-Log "Import AutoPilot profile: $($obj.Object.displayName)"
|
||||||
|
|
||||||
|
$assignments = Get-GraphAssignmentsObject $obj.Object ($obj.FileInfo.DirectoryName + "\" + $obj.FileInfo.BaseName + "_assignments.json")
|
||||||
|
|
||||||
|
$response = Import-AutoPilot $obj.Object
|
||||||
|
|
||||||
|
if($response)
|
||||||
|
{
|
||||||
|
$global:importedObjects++
|
||||||
|
Import-GraphAssignments2 $assignments "/deviceManagement/windowsAutopilotDeploymentProfiles/$($response.Id)/assignments"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$dgObjects.ItemsSource = @(Get-AutoPilotObjects)
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
310
Extensions/AzureBranding.psm1
Normal file
310
Extensions/AzureBranding.psm1
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Common module functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Add-ModuleMenuItems
|
||||||
|
{
|
||||||
|
Add-MenuItem (New-Object PSObject -Property @{
|
||||||
|
Title = (Get-AZBrandingName)
|
||||||
|
MenuID = "IntuneGraphAPI"
|
||||||
|
Script = [ScriptBlock]{Get-AZBrandings}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedImportObjects
|
||||||
|
{
|
||||||
|
$global:importObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-AZBrandingName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Import all Azure branding"
|
||||||
|
Import-AllAZBrandingObjects (Join-Path $rootFolder (Get-AZBrandingFolderName))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedExportObjects
|
||||||
|
{
|
||||||
|
$global:exportObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-AZBrandingName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Export all Azure branding"
|
||||||
|
Get-AZBrandingObjects | ForEach-Object { Export-SingleAZBranding $PSItem.Object (Join-Path $rootFolder (Get-AZBrandingFolderName)) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllObjects
|
||||||
|
{
|
||||||
|
param($addObjectSubfolder)
|
||||||
|
|
||||||
|
$subFolder = ""
|
||||||
|
if($addObjectSubfolder) { $subFolder = Get-AZBrandingFolderName }
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Object specific functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Get-AZBrandingName
|
||||||
|
{
|
||||||
|
return "Azure Branding"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-AZBrandingFolderName
|
||||||
|
{
|
||||||
|
return "AZBranding"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-AZBrandings
|
||||||
|
{
|
||||||
|
Write-Status "Loading Azure brandings"
|
||||||
|
$dgObjects.ItemsSource = @(Get-AZBrandingObjects)
|
||||||
|
|
||||||
|
#Scriptblocks that will perform the export tasks. empty by default
|
||||||
|
$script:exportParams = @{}
|
||||||
|
$script:exportParams.Add("ExportAllScript", [ScriptBlock]{
|
||||||
|
Export-AllAZBrandings $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
$script:exportParams.Add("ExportSelectedScript", [ScriptBlock]{
|
||||||
|
Export-SelectedAZBranding $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
$script:exportParams.Add("DisplayColumn", "localeDisplayName")
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import all files
|
||||||
|
$script:importAll = [ScriptBlock]{
|
||||||
|
Import-AllAZBrandingObjects $global:txtImportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import of selected files
|
||||||
|
$script:importSelected = [ScriptBlock]{
|
||||||
|
Import-AZBrandingObjects $global:lstFiles.ItemsSource -Selected
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will read json files
|
||||||
|
$script:getImportFiles = [ScriptBlock]{
|
||||||
|
Show-FileListBox
|
||||||
|
$global:lstFiles.ItemsSource = @(Get-JsonFileObjects $global:txtImportPath.Text -Exclude "*_Settings.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-DefaultObjectButtons -export ([scriptblock]{Show-DefaultExportGrid @script:exportParams}) -import ([scriptblock]{Show-DefaultImportGrid -ImportAll $script:importAll -ImportSelected $script:importSelected -GetFiles $script:getImportFiles})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-AZBrandingObjects
|
||||||
|
{
|
||||||
|
$response = Get-AzureNativeObjects "LoginTenantBrandings" -property @('locale', 'localeDisplayName')
|
||||||
|
if($response)
|
||||||
|
{
|
||||||
|
$response | Where { $_.Object.isConfigured -eq $true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllAZBrandings
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
foreach($objTmp in ($global:dgObjects.ItemsSource))
|
||||||
|
{
|
||||||
|
Export-SingleAZBranding $objTmp.Object $path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SelectedAZBranding
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Export-SingleAZBranding $global:dgObjects.SelectedItem.Object $path
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SingleAZBranding
|
||||||
|
{
|
||||||
|
param($psObj, $path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not $psObj) { return }
|
||||||
|
|
||||||
|
if($global:runningBulkExport -ne $true)
|
||||||
|
{
|
||||||
|
if($global:chkAddCompanyName.IsChecked) { $path = Join-Path $path $global:organization.displayName }
|
||||||
|
if($global:chkAddObjectType.IsChecked) { $path = Join-Path $path (Get-AZBrandingFolderName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
Write-Status "Export $($psObj.localeDisplayName)"
|
||||||
|
$obj = Invoke-AzureNativeRequest "LoginTenantBrandings/$($psObj.locale)"
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
$fileName = "$path\$((Remove-InvalidFileNameChars $obj.localeDisplayName)).json"
|
||||||
|
ConvertTo-Json $obj -Depth 5 | Out-File $fileName -Force
|
||||||
|
|
||||||
|
Save-AzureBrandingFile $obj "tileLogoUrl" $path
|
||||||
|
Save-AzureBrandingFile $obj "bannerLogoUrl" $path
|
||||||
|
Save-AzureBrandingFile $obj "illustrationUrl" $path
|
||||||
|
Save-AzureBrandingFile $obj "squareLogoDarkUrl" $path
|
||||||
|
|
||||||
|
$global:exportedObjects++
|
||||||
|
}
|
||||||
|
Set-ObjectPath $global:txtExportPath.Text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Save-AzureBrandingFile
|
||||||
|
{
|
||||||
|
param($obj, $prop, $path)
|
||||||
|
|
||||||
|
if(-not $obj.$prop) { return }
|
||||||
|
|
||||||
|
$arr=$obj.$prop.Split('.')
|
||||||
|
if($arr.Length -ne 1)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$fileType = "jpg" # Assume...not OK. $arr[0] contains information about what kind of file it is
|
||||||
|
|
||||||
|
$fileName = "$path\$((Remove-InvalidFileNameChars "$($obj.localeDisplayName).$prop.$fileType"))"
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(Test-Path $fileName)
|
||||||
|
{
|
||||||
|
Remove-Item -Path $fileName -Force
|
||||||
|
}
|
||||||
|
[IO.File]::WriteAllBytes($fileName, [System.Convert]::FromBase64String($arr[1]))
|
||||||
|
}
|
||||||
|
catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AZBranding
|
||||||
|
{
|
||||||
|
param($obj)
|
||||||
|
|
||||||
|
if($global:runningBulkImport -eq $true)
|
||||||
|
{
|
||||||
|
# Update Default and create the rest...
|
||||||
|
$createNew = $obj.locale -ne 0
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$curObj = $global:lstFiles.ItemsSource | Where { $_.Object.locale -eq $obj.locale }
|
||||||
|
|
||||||
|
if($curObj -and $obj.locale -ne 0)
|
||||||
|
{
|
||||||
|
return # Do not update existing object except default
|
||||||
|
}
|
||||||
|
elseif(-not $curObj)
|
||||||
|
{
|
||||||
|
$createNew = $true
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$createNew = $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = "{"
|
||||||
|
|
||||||
|
if($createNew) { $json += "`"locale`":`"$($obj.locale)`"," }
|
||||||
|
|
||||||
|
if($obj.signInUserIdLabel) { $json += "`"userIdLabel`": `"$($obj.signInUserIdLabel)`"," }
|
||||||
|
if($obj.signInPageText) { $json += "`"boilerPlateText`": `"$($obj.signInPageText)`"," }
|
||||||
|
if($obj.signInBackColor) { $json += "`"backgroundColor`": `"$($obj.signInBackColor)`"," }
|
||||||
|
if($obj.tileLogoUrl) { $json += "`"tileLogoUrl`": `"$($obj.tileLogoUrl)`"," }
|
||||||
|
if($obj.bannerLogoUrl) { $json += "`"bannerLogoUrl`": `"$($obj.bannerLogoUrl)`"," }
|
||||||
|
if($obj.illustrationUrl) { $json += "`"illustrationUrl`": `"$($obj.illustrationUrl)`"," }
|
||||||
|
if($obj.squareLogoDarkUrl) { $json += "`"squareLogoDarkUrl`": `"$($obj.squareLogoDarkUrl)`"," }
|
||||||
|
|
||||||
|
if($obj.hideKeepMeSignedIn -and $obj.locale -eq 0) { $json += "`"keepMeSignedInDisabled`": $($obj.hideKeepMeSignedIn.ToString().ToLower())," }
|
||||||
|
|
||||||
|
if($createNew)
|
||||||
|
{
|
||||||
|
if($curObj.bannerLogoUrl -ne $curObj.bannerLogoUrl)
|
||||||
|
{
|
||||||
|
$json += "`"isTileLogoUpdated`":true,"
|
||||||
|
}
|
||||||
|
|
||||||
|
if($curObj.illustrationUrl -ne $curObj.illustrationUrl)
|
||||||
|
{
|
||||||
|
$json += "`"isIllustrationImageUpdated`":true,"
|
||||||
|
}
|
||||||
|
|
||||||
|
if($curObj.squareLogoDarkUrl -ne $curObj.squareLogoDarkUrl)
|
||||||
|
{
|
||||||
|
$json += "`"isSquareDarkLogoUpdated`":true,"
|
||||||
|
}
|
||||||
|
|
||||||
|
if($curObj.bannerLogoUrl -ne $curObj.bannerLogoUrl)
|
||||||
|
{
|
||||||
|
$json += "`"isBannerLogoUpdated`":true,"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = $json.TrimEnd(',')
|
||||||
|
$json += "}"
|
||||||
|
|
||||||
|
Write-Status "Import $($obj.localeDisplayName)"
|
||||||
|
|
||||||
|
if($createNew)
|
||||||
|
{
|
||||||
|
Invoke-AzureNativeRequest "LoginTenantBrandings" -Method POST -Body $json | Out-Null
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Invoke-AzureNativeRequest "LoginTenantBrandings/$($obj.locale)" -Method PATCH -Body $json | Out-Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AllAZBrandingObjects
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Import-AZBrandingObjects (Get-JsonFileObjects $path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AZBrandingObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$Objects,
|
||||||
|
|
||||||
|
[switch]
|
||||||
|
$Selected
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Status "Import Azure brandings"
|
||||||
|
|
||||||
|
foreach($obj in $objects)
|
||||||
|
{
|
||||||
|
if($Selected -and $obj.Selected -ne $true) { continue }
|
||||||
|
|
||||||
|
Write-Log "Import Azure branding"
|
||||||
|
|
||||||
|
$response = Import-AZBranding $obj.Object
|
||||||
|
|
||||||
|
if($response)
|
||||||
|
{
|
||||||
|
$global:importedObjects++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$dgObjects.ItemsSource = @(Get-AZBrandingObjects)
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
169
Extensions/AzureNative.psm1
Normal file
169
Extensions/AzureNative.psm1
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
#
|
||||||
|
# Azure functions are based on: ???
|
||||||
|
#
|
||||||
|
function Invoke-AzureNativeRequest {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Runs a command against the Azure Portal API
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding(SupportsShouldProcess)]
|
||||||
|
param (
|
||||||
|
#The target of your request. This is appended to the Portal API URI. Example: Permissions
|
||||||
|
[Parameter(Mandatory)]$Target,
|
||||||
|
|
||||||
|
#The command you wish to execute. Example: GetUserSystemRoleTemplateIds
|
||||||
|
[Parameter()]$Action,
|
||||||
|
|
||||||
|
#The body of your request. This is usually in JSON format
|
||||||
|
$Body,
|
||||||
|
|
||||||
|
#Specify the HTTP Method you wish to use. Defaults to GET
|
||||||
|
[ValidateSet("GET","POST","OPTIONS","DELETE", "PATCH", "PUT")]
|
||||||
|
$Method = "GET",
|
||||||
|
|
||||||
|
#The base URI for the Portal API. Typically you don't need to change this
|
||||||
|
[Uri]$baseURI = 'https://main.iam.ad.ext.azure.com/api/',
|
||||||
|
|
||||||
|
[URI]$requestOrigin = 'https://iam.hosting.portal.azure.net',
|
||||||
|
|
||||||
|
#The request ID for the session. You can generate one with [guid]::NewGuid().guid.
|
||||||
|
#Typically you only specify this if you're trying to retry an operation and don't want to duplicate the request, such as for a POST operation
|
||||||
|
$requestID = [guid]::NewGuid().guid
|
||||||
|
)
|
||||||
|
|
||||||
|
#Combine the BaseURI and Target
|
||||||
|
[String]$ApiAction = $Target.TrimStart('/')
|
||||||
|
|
||||||
|
if ($Action)
|
||||||
|
{
|
||||||
|
$ApiAction = $ApiAction + '/' + $Action
|
||||||
|
}
|
||||||
|
|
||||||
|
if($global:tokresponse -and [DateTimeOffset]::Now.ToUnixTimeSeconds() -gt $global:tokresponse.expires_on)
|
||||||
|
{
|
||||||
|
$global:tokresponse = $null
|
||||||
|
}
|
||||||
|
|
||||||
|
$Context = Get-AzureRmContext
|
||||||
|
|
||||||
|
if(-not $context -or -not $global:tokresponse)
|
||||||
|
{
|
||||||
|
if($Context)
|
||||||
|
{
|
||||||
|
if($global:Me -and $global:Organization)
|
||||||
|
{
|
||||||
|
$refreshToken = ($Context.TokenCache.ReadItems() | Where { $_.DisplayableId -eq $global:Me.userPrincipalName -and $_.TenantId -eq $global:Organization.Id }).RefreshToken
|
||||||
|
if($refreshToken -and $refreshToken.ExpiresOn -lt (Get-Date))
|
||||||
|
{
|
||||||
|
# Expired...force login
|
||||||
|
# $refreshToken = $null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-not $refreshToken)
|
||||||
|
{
|
||||||
|
$user = Connect-AzureRmAccount
|
||||||
|
if(-not $user) { return }
|
||||||
|
$Context = Get-AzureRmContext
|
||||||
|
if(-not $Context) { return }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
if(-not $global:tokresponse)
|
||||||
|
{
|
||||||
|
$refreshToken = $null # Fore read again in case of login
|
||||||
|
if($global:Me -and $global:Organization)
|
||||||
|
{
|
||||||
|
$refreshToken = ($Context.TokenCache.ReadItems() | Where { $_.DisplayableId -eq $global:Me.userPrincipalName -and $_.TenantId -eq $global:Organization.Id }).RefreshToken
|
||||||
|
}
|
||||||
|
# Make sure we are using the same user as Intune login
|
||||||
|
if(-not $refreshToken)
|
||||||
|
{
|
||||||
|
[System.Windows.MessageBox]::Show("Failed to find login token for AzureRM", "Invalid AzureRM login!", "OK", "Error")
|
||||||
|
return $global:tokresponse
|
||||||
|
}
|
||||||
|
|
||||||
|
$curToken = $Context.TokenCache.ReadItems() | Where { $_.DisplayableId -eq $global:Me.userPrincipalName -and $_.TenantId -eq $global:Organization.Id }
|
||||||
|
$tenantid = $curToken.TenantId
|
||||||
|
$refreshToken = $curToken.RefreshToken
|
||||||
|
$loginUrl = "https://login.windows.net/$tenantid/oauth2/token"
|
||||||
|
$bodyTmp = "grant_type=refresh_token&refresh_token=$($refreshToken)" #&resource=74658136-14ec-4630-ad9b-26e160ff0fc6"
|
||||||
|
$response = Invoke-RestMethod $loginUrl -Method POST -Body $bodyTmp -ContentType 'application/x-www-form-urlencoded'
|
||||||
|
|
||||||
|
$global:tokresponse = Invoke-RestMethod $loginUrl -Method POST -Body ($bodyTmp + "&resource=74658136-14ec-4630-ad9b-26e160ff0fc6")
|
||||||
|
|
||||||
|
if(-not $global:tokresponse) { return }
|
||||||
|
}
|
||||||
|
|
||||||
|
$InvokeRestMethodParams = @{
|
||||||
|
Uri = [Uri]::New($baseURI,$ApiAction)
|
||||||
|
Method = $Method
|
||||||
|
Header = [ordered]@{
|
||||||
|
Authorization = 'Bearer ' + $global:tokresponse.access_token
|
||||||
|
'Content-Type' = 'application/json'
|
||||||
|
'x-ms-client-request-id' = $requestID
|
||||||
|
'Host' = $baseURI.Host
|
||||||
|
'Origin' = $requestOrigin
|
||||||
|
}
|
||||||
|
Body = $Body
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Invoke-RestMethod @InvokeRestMethodParams
|
||||||
|
if($? -eq $false)
|
||||||
|
{
|
||||||
|
throw $global:error[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Write-LogError "Failed to invoke Invoke-RestMethod for Azure" $_.Exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-AzureNativeObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
[Array]
|
||||||
|
$Target,
|
||||||
|
[Array]
|
||||||
|
$property,
|
||||||
|
[Array]
|
||||||
|
$exclude,
|
||||||
|
$SortProperty = "")
|
||||||
|
|
||||||
|
$objects = @()
|
||||||
|
$nativeObjects =Invoke-AzureNativeRequest $Target
|
||||||
|
|
||||||
|
if(($nativeObjects | GM -Name "items"))
|
||||||
|
{
|
||||||
|
$objectList = $nativeObjects.Items
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$objectList = $nativeObjects
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($nativeObject in $objectList)
|
||||||
|
{
|
||||||
|
$params = @{}
|
||||||
|
if($property) { $params.Add("Property", $property) }
|
||||||
|
if($exclude) { $params.Add("ExcludeProperty", $exclude) }
|
||||||
|
foreach($objTmp in ($nativeObject | select @params))
|
||||||
|
{
|
||||||
|
$objTmp | Add-Member -NotePropertyName "Object" -NotePropertyValue $nativeObject
|
||||||
|
$objects += $objTmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($objects.Count -gt 0 -and $SortProperty -and ($objects[0] | GM -MemberType NoteProperty -Name $SortProperty))
|
||||||
|
{
|
||||||
|
$objects = $objects | sort -Property $SortProperty
|
||||||
|
}
|
||||||
|
$objects
|
||||||
|
}
|
||||||
294
Extensions/Baseline.psm1
Normal file
294
Extensions/Baseline.psm1
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Common module functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Add-ModuleMenuItems
|
||||||
|
{
|
||||||
|
Add-MenuItem (New-Object PSObject -Property @{
|
||||||
|
Title = (Get-BaselineTemplatesName)
|
||||||
|
MenuID = "IntuneGraphAPI"
|
||||||
|
Script = [ScriptBlock]{Get-BaselineTemplates}
|
||||||
|
})
|
||||||
|
|
||||||
|
Add-MenuItem (New-Object PSObject -Property @{
|
||||||
|
Title = (Get-BaselineName)
|
||||||
|
MenuID = "IntuneGraphAPI"
|
||||||
|
Script = [ScriptBlock]{Get-BaselineProfiles}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedImportObjects
|
||||||
|
{
|
||||||
|
$global:importObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-AppProtectionName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Import all baseline policies"
|
||||||
|
Import-AllBaselineProfileObjects (Join-Path $rootFolder (Get-BaselineFolderName))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedExportObjects
|
||||||
|
{
|
||||||
|
$global:exportObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-AppProtectionName)
|
||||||
|
Folder = (Get-BaselineFolderName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Import all baseline policies"
|
||||||
|
Get-BaselineProfileObjects | ForEach-Object { Export-SingleBaselineProfile $PSItem.Object (Join-Path $rootFolder (Get-BaselineFolderName)) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllObjects
|
||||||
|
{
|
||||||
|
param($addObjectSubfolder)
|
||||||
|
|
||||||
|
$subFolder = ""
|
||||||
|
if($addObjectSubfolder) { $subFolder = Get-BaselineFolderName }
|
||||||
|
|
||||||
|
}
|
||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Object specific functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Get-BaselineTemplatesName
|
||||||
|
{
|
||||||
|
return "Baseline Templates"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Get-BaselineName
|
||||||
|
{
|
||||||
|
return "Baseline Profiles"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-BaselineFolderName
|
||||||
|
{
|
||||||
|
return "Baseline"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-BaselineTemplates
|
||||||
|
{
|
||||||
|
Write-Status "Loading baseline templates" -SkipLog
|
||||||
|
$dgObjects.ItemsSource = @(Get-BaselineTemplateObjects)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-BaselineTemplateObjects
|
||||||
|
{
|
||||||
|
Get-GraphObjects -Url "/deviceManagement/templates"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-BaselineProfiles
|
||||||
|
{
|
||||||
|
Write-Status "Loading banding profiles" -SkipLog
|
||||||
|
$dgObjects.ItemsSource = @(Get-BaselineProfileObjects)
|
||||||
|
|
||||||
|
#Scriptblocks that will perform the export tasks. empty by default
|
||||||
|
$script:exportParams = @{}
|
||||||
|
$script:exportParams.Add("ExportAllScript", [ScriptBlock]{
|
||||||
|
Export-AllBaselineProfiles $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
$script:exportParams.Add("ExportSelectedScript", [ScriptBlock]{
|
||||||
|
Export-SelectedBaselineProfile $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import all files
|
||||||
|
$script:importAll = [ScriptBlock]{
|
||||||
|
Import-AllBaselineProfileObjects $global:txtImportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import of selected files
|
||||||
|
$script:importSelected = [ScriptBlock]{
|
||||||
|
Import-BaselineProfileObjects $global:lstFiles.ItemsSource -Selected
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will read json files
|
||||||
|
$script:getImportFiles = [ScriptBlock]{
|
||||||
|
Show-FileListBox
|
||||||
|
$global:lstFiles.ItemsSource = @(Get-JsonFileObjects $global:txtImportPath.Text -Exclude @("*_Settings.json","*_assignments.json"))
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-DefaultObjectButtons -export ([scriptblock]{Show-DefaultExportGrid @script:exportParams}) -import ([scriptblock]{Show-DefaultImportGrid -ImportAll $script:importAll -ImportSelected $script:importSelected -GetFiles $script:getImportFiles}) -copy ([scriptblock]{Copy-BaselineProfile})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-BaselineProfileObjects
|
||||||
|
{
|
||||||
|
Get-GraphObjects -Url "/deviceManagement/intents"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllBaselineProfiles
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
foreach($objTmp in ($global:dgObjects.ItemsSource))
|
||||||
|
{
|
||||||
|
Export-SingleBaselineProfile $objTmp.Object $path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SelectedBaselineProfile
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Export-SingleBaselineProfile $global:dgObjects.SelectedItem.Object $path
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SingleBaselineProfile
|
||||||
|
{
|
||||||
|
param($psObj, $path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not $psObj) { return }
|
||||||
|
|
||||||
|
if($global:runningBulkExport -ne $true)
|
||||||
|
{
|
||||||
|
if($global:chkAddCompanyName.IsChecked) { $path = Join-Path $path $global:organization.displayName }
|
||||||
|
if($global:chkAddObjectType.IsChecked) { $path = Join-Path $path (Get-BaselineFolderName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
Write-Status "Export $($psObj.displayName)"
|
||||||
|
$obj = $psObj
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
$fileName = "$path\$((Remove-InvalidFileNameChars $obj.displayName)).json"
|
||||||
|
ConvertTo-Json $obj -Depth 5 | Out-File $fileName -Force
|
||||||
|
$settings = Invoke-GraphRequest -Url "/deviceManagement/intents/$($obj.id)/settings"
|
||||||
|
ConvertTo-Json $settings.value -Depth 5 | Out-File "$path\$($obj.displayName)_Settings.json" -Force
|
||||||
|
}
|
||||||
|
$assignments = Invoke-GraphRequest -Url "/deviceManagement/intents/$($obj.id)/assignments"
|
||||||
|
if(($assignments.Value | measure).Count -gt 0)
|
||||||
|
{
|
||||||
|
ConvertTo-Json $assignments.value -Depth 5| Out-File "$path\$($obj.displayName)_assignments.json" -Force
|
||||||
|
}
|
||||||
|
$global:exportedObjects++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-BaselineProfile
|
||||||
|
{
|
||||||
|
if(-not $dgObjects.SelectedItem)
|
||||||
|
{
|
||||||
|
[System.Windows.MessageBox]::Show("No object selected`n`nSelect baseline profile you want to copy", "Error", "OK", "Error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = Show-InputDialog "Copy baseline profiles" "Select name for the new object" "$($dgObjects.SelectedItem.displayName) - Copy"
|
||||||
|
|
||||||
|
if($ret)
|
||||||
|
{
|
||||||
|
# Export profile
|
||||||
|
Write-Status "Export $($dgObjects.SelectedItem.displayName)"
|
||||||
|
# Convert to Json and back to clone the object
|
||||||
|
$obj = ConvertTo-Json $dgObjects.SelectedItem.Object -Depth 5 | ConvertFrom-Json
|
||||||
|
$settings = Invoke-GraphRequest -Url "/deviceManagement/intents/$($obj.id)/settings"
|
||||||
|
$intentSettings = ConvertTo-Json $settings.value -Depth 5
|
||||||
|
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
# Import new profile
|
||||||
|
$obj.displayName = $ret
|
||||||
|
Import-BaselineProfile $obj $intentSettings | Out-null
|
||||||
|
|
||||||
|
$dgObjects.ItemsSource = @(Get-BaselineProfileObjects)
|
||||||
|
}
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
|
$dgObjects.Focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-BaselineProfile
|
||||||
|
{
|
||||||
|
param($obj, $intentSettings, $templateId)
|
||||||
|
|
||||||
|
$json = @"
|
||||||
|
{
|
||||||
|
"displayName": "$($obj.displayName)",
|
||||||
|
"description": "$($obj.description)",
|
||||||
|
"settingsDelta":
|
||||||
|
$($intentSettings)
|
||||||
|
|
||||||
|
}
|
||||||
|
"@
|
||||||
|
|
||||||
|
if($templateId)
|
||||||
|
{
|
||||||
|
$tempId = $templateId
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$tempId = $obj.templateId
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Status "Import $($obj.displayName)"
|
||||||
|
|
||||||
|
return Invoke-GraphRequest -Url "/deviceManagement/templates/$($tempId)/createInstance" -Content $json -HttpMethod POST
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AllBaselineProfileObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$path = "$env:Temp"
|
||||||
|
)
|
||||||
|
|
||||||
|
Import-BaselineProfileObjects (Get-JsonFileObjects $path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-BaselineProfileObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$Objects,
|
||||||
|
|
||||||
|
[switch]
|
||||||
|
$Selected
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Status "Import terms and conditions"
|
||||||
|
|
||||||
|
foreach($obj in $objects)
|
||||||
|
{
|
||||||
|
if($Selected -and $obj.Selected -ne $true) { continue }
|
||||||
|
|
||||||
|
Write-Log "Import security baseline: $($obj.Object.displayName)"
|
||||||
|
|
||||||
|
$assignments = Get-GraphAssignmentsObject $obj.Object ($obj.FileInfo.DirectoryName + "\" + $obj.FileInfo.BaseName + "_assignments.json")
|
||||||
|
|
||||||
|
$settingsFile = $obj.FileInfo.DirectoryName + "\" + $obj.FileInfo.BaseName + "_settings.json"
|
||||||
|
if(-not (Test-Path $settingsFile)) { continue }
|
||||||
|
|
||||||
|
$intentSettings = Get-Content $settingsFile -Raw
|
||||||
|
|
||||||
|
$response = Import-BaselineProfile $obj.Object $intentSettings
|
||||||
|
if($response)
|
||||||
|
{
|
||||||
|
$global:importedObjects++
|
||||||
|
Import-GraphAssignments $assignments "assignments" "/deviceManagement/intents/$($response.Id)/assign"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$dgObjects.ItemsSource = @(Get-BaselineProfileObjects)
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
236
Extensions/Branding.psm1
Normal file
236
Extensions/Branding.psm1
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Common module functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Add-ModuleMenuItems
|
||||||
|
{
|
||||||
|
Add-MenuItem (New-Object PSObject -Property @{
|
||||||
|
Title = (Get-IntuneBrandingName)
|
||||||
|
MenuID = "IntuneGraphAPI"
|
||||||
|
Script = [ScriptBlock]{Get-IntuneBrandings}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedImportObjects
|
||||||
|
{
|
||||||
|
$global:importObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-IntuneBrandingName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Import all Intune branding objects"
|
||||||
|
Import-AllIntuneBrandingObjects (Join-Path $rootFolder (Get-IntuneBrandingFolderName))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedExportObjects
|
||||||
|
{
|
||||||
|
$global:exportObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-IntuneBrandingName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Export all Intune branding objects"
|
||||||
|
Get-IntuneBrandingObjects | ForEach-Object { Export-SingleIntuneBranding $PSItem.Object (Join-Path $rootFolder (Get-IntuneBrandingFolderName)) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllObjects
|
||||||
|
{
|
||||||
|
param($addObjectSubfolder)
|
||||||
|
|
||||||
|
$subFolder = ""
|
||||||
|
if($addObjectSubfolder) { $subFolder = Get-IntuneBrandingFolderName }
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Object specific functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Get-IntuneBrandingName
|
||||||
|
{
|
||||||
|
return "Intune Branding"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-IntuneBrandingFolderName
|
||||||
|
{
|
||||||
|
return "IntuneBranding"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-IntuneBrandings
|
||||||
|
{
|
||||||
|
Write-Status "Loading banding profiles"
|
||||||
|
$dgObjects.ItemsSource = @(Get-IntuneBrandingObjects)
|
||||||
|
|
||||||
|
#Scriptblocks that will perform the export tasks. empty by default
|
||||||
|
$script:exportParams = @{}
|
||||||
|
$script:exportParams.Add("ExportAllScript", [ScriptBlock]{
|
||||||
|
Export-AllIntuneBrandings $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
# Same as ExportAllScript since only one object is supported
|
||||||
|
$script:exportParams.Add("ExportSelectedScript", [ScriptBlock]{
|
||||||
|
Export-AllIntuneBrandings $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import all files
|
||||||
|
$script:importAll = [ScriptBlock]{
|
||||||
|
Import-AllIntuneBrandingObjects $global:txtImportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import of selected files
|
||||||
|
$script:importSelected = [ScriptBlock]{
|
||||||
|
Import-IntuneBrandingObjects $global:lstFiles.ItemsSource -Selected
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will read json files
|
||||||
|
$script:getImportFiles = [ScriptBlock]{
|
||||||
|
Show-FileListBox
|
||||||
|
$global:lstFiles.ItemsSource = @(Get-JsonFileObjects $global:txtImportPath.Text -Exclude "*_Settings.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-DefaultObjectButtons -export ([scriptblock]{Show-DefaultExportGrid @script:exportParams}) -import ([scriptblock]{Show-DefaultImportGrid -ImportAll $script:importAll -ImportSelected $script:importSelected -GetFiles $script:getImportFiles})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-IntuneBrandingObjects
|
||||||
|
{
|
||||||
|
Get-GraphObjects -Url "/deviceManagement/intuneBrand" -property @("displayName")
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllIntuneBrandings
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
foreach($objTmp in ($global:dgObjects.ItemsSource))
|
||||||
|
{
|
||||||
|
Export-SingleIntuneBranding $objTmp.Object $path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SingleIntuneBranding
|
||||||
|
{
|
||||||
|
param($psObj, $path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not $psObj) { return }
|
||||||
|
|
||||||
|
if($global:runningBulkExport -ne $true)
|
||||||
|
{
|
||||||
|
if($global:chkAddCompanyName.IsChecked) { $path = Join-Path $path $global:organization.displayName }
|
||||||
|
if($global:chkAddObjectType.IsChecked) { $path = Join-Path $path (Get-IntuneBrandingFolderName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
Write-Status "Export $($psObj.displayName)"
|
||||||
|
|
||||||
|
$obj = $psObj
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
$fileName = "$path\$((Remove-InvalidFileNameChars $obj.displayName)).json"
|
||||||
|
ConvertTo-Json $obj -Depth 5 | Out-File $fileName -Force
|
||||||
|
Save-IntuneBrandingFile $obj "lightBackgroundLogo" $path
|
||||||
|
Save-IntuneBrandingFile $obj "darkBackgroundLogo" $path
|
||||||
|
Save-IntuneBrandingFile $obj "landingPageCustomizedImage" $path
|
||||||
|
}
|
||||||
|
$global:exportedObjects++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Save-IntuneBrandingFile
|
||||||
|
{
|
||||||
|
param($obj, $prop, $path)
|
||||||
|
|
||||||
|
if(-not $obj.$prop.type) { return }
|
||||||
|
|
||||||
|
$arr=$obj.$prop.type.Split('/')
|
||||||
|
if($arr.Length -gt 1)
|
||||||
|
{
|
||||||
|
$fileType = $arr[1]
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$fileType = ".jpg" # assume...
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileName = "$path\$((Remove-InvalidFileNameChars "$($obj.displayName).$prop.$fileType"))"
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(Test-Path $fileName)
|
||||||
|
{
|
||||||
|
Remove-Item -Path $fileName -Force
|
||||||
|
}
|
||||||
|
[IO.File]::WriteAllBytes($fileName, [System.Convert]::FromBase64String($obj.$prop.value))
|
||||||
|
}
|
||||||
|
catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Import-IntuneBranding
|
||||||
|
{
|
||||||
|
param($obj)
|
||||||
|
|
||||||
|
Remove-ObjectProperty $obj "@odata.context"
|
||||||
|
|
||||||
|
$newObject = @"
|
||||||
|
{
|
||||||
|
"intuneBrand":$((ConvertTo-Json $obj -Depth 5))
|
||||||
|
}
|
||||||
|
|
||||||
|
"@
|
||||||
|
Write-Status "Import $($obj.displayName)"
|
||||||
|
|
||||||
|
# Note: Branding is imported to deviceManagement with JSON parent object intuneBrand
|
||||||
|
Invoke-GraphRequest -Url "$URL/deviceManagement" -Content $newObject -HttpMethod PATCH
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AllIntuneBrandingObjects
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Import-IntuneBrandingObjects (Get-JsonFileObjects $path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-IntuneBrandingObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$Objects,
|
||||||
|
|
||||||
|
[switch]
|
||||||
|
$Selected
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Status "Import Intune branding"
|
||||||
|
|
||||||
|
foreach($obj in $objects)
|
||||||
|
{
|
||||||
|
if($Selected -and $obj.Selected -ne $true) { continue }
|
||||||
|
|
||||||
|
Write-Log "Import Intune branding"
|
||||||
|
|
||||||
|
$response = Import-IntuneBranding $obj.Object
|
||||||
|
|
||||||
|
# Note: No assignments for branding. This is default branding for everyone
|
||||||
|
|
||||||
|
}
|
||||||
|
$dgObjects.ItemsSource = @(Get-IntuneBrandingObjects)
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
244
Extensions/CompliancePolicies.psm1
Normal file
244
Extensions/CompliancePolicies.psm1
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Common module functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Add-ModuleMenuItems
|
||||||
|
{
|
||||||
|
Add-MenuItem (New-Object PSObject -Property @{
|
||||||
|
Title = (Get-CompliancePolicyName)
|
||||||
|
MenuID = "IntuneGraphAPI"
|
||||||
|
Script = [ScriptBlock]{Get-CompliancePolicies}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedImportObjects
|
||||||
|
{
|
||||||
|
$global:importObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-CompliancePolicyName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Import all Intune compliance policies"
|
||||||
|
Import-AllCompliancePolicyObjects (Join-Path $rootFolder (Get-CompliancePolicyFolderName))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedExportObjects
|
||||||
|
{
|
||||||
|
$global:exportObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-CompliancePolicyName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Export all compliance policies"
|
||||||
|
Get-CompliancePolicyObjects | ForEach-Object { Export-SingleCompliancePolicy $PSItem.Object (Join-Path $rootFolder (Get-CompliancePolicyFolderName)) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllObjects
|
||||||
|
{
|
||||||
|
param($addObjectSubfolder)
|
||||||
|
|
||||||
|
$subFolder = ""
|
||||||
|
if($addObjectSubfolder) { $subFolder = Get-CompliancePolicyFolderName }
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Object specific functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Get-CompliancePolicyName
|
||||||
|
{
|
||||||
|
return "Compliance Policies"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-CompliancePolicyFolderName
|
||||||
|
{
|
||||||
|
return "CompliancePolicies"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-CompliancePolicies
|
||||||
|
{
|
||||||
|
Write-Status "Loading compliance policies"
|
||||||
|
$dgObjects.ItemsSource = @(Get-CompliancePolicyObjects)
|
||||||
|
|
||||||
|
#Scriptblocks that will perform the export tasks. empty by default
|
||||||
|
$script:exportParams = @{}
|
||||||
|
$script:exportParams.Add("ExportAllScript", [ScriptBlock]{
|
||||||
|
Export-AllCompliancePolicies $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
$script:exportParams.Add("ExportSelectedScript", [ScriptBlock]{
|
||||||
|
Export-SelectedCompliancePolicy $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import all files
|
||||||
|
$script:importAll = [ScriptBlock]{
|
||||||
|
Import-AllCompliancePolicyObjects $global:txtImportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import of selected files
|
||||||
|
$script:importSelected = [ScriptBlock]{
|
||||||
|
Import-CompliancePolicyObjects $global:lstFiles.ItemsSource -Selected
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will read json files
|
||||||
|
$script:getImportFiles = [ScriptBlock]{
|
||||||
|
Show-FileListBox
|
||||||
|
$global:lstFiles.ItemsSource = @(Get-JsonFileObjects $global:txtImportPath.Text -Exclude "*_Settings.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-DefaultObjectButtons -export ([scriptblock]{Show-DefaultExportGrid @script:exportParams}) -import ([scriptblock]{Show-DefaultImportGrid -ImportAll $script:importAll -ImportSelected $script:importSelected -GetFiles $script:getImportFiles}) -copy ([scriptblock]{Copy-CompliancePolicy})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-CompliancePolicyObjects
|
||||||
|
{
|
||||||
|
Get-GraphObjects -Url "/deviceManagement/deviceCompliancePolicies"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllCompliancePolicies
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
foreach($objTmp in ($global:dgObjects.ItemsSource))
|
||||||
|
{
|
||||||
|
Export-SingleCompliancePolicy $objTmp.Object $path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SelectedCompliancePolicy
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Export-SingleCompliancePolicy $global:dgObjects.SelectedItem.Object $path
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SingleCompliancePolicy
|
||||||
|
{
|
||||||
|
param($psObj, $path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not $psObj) { return }
|
||||||
|
|
||||||
|
if($global:runningBulkExport -ne $true)
|
||||||
|
{
|
||||||
|
if($global:chkAddCompanyName.IsChecked) { $path = Join-Path $path $global:organization.displayName }
|
||||||
|
if($global:chkAddObjectType.IsChecked) { $path = Join-Path $path (Get-CompliancePolicyFolderName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
Write-Status "Export $($psObj.displayName)"
|
||||||
|
$obj = Invoke-GraphRequest -Url "/deviceManagement/deviceCompliancePolicies/$($psObj.id)?`$expand=assignments"
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
$fileName = "$path\$((Remove-InvalidFileNameChars $obj.displayName)).json"
|
||||||
|
ConvertTo-Json $obj -Depth 5 | Out-File $fileName -Force
|
||||||
|
|
||||||
|
Add-MigrationInfo $obj.assignments
|
||||||
|
}
|
||||||
|
$global:exportedObjects++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-CompliancePolicy
|
||||||
|
{
|
||||||
|
if(-not $dgObjects.SelectedItem)
|
||||||
|
{
|
||||||
|
[System.Windows.MessageBox]::Show("No object selected`n`nSelect compliance policy item you want to copy", "Error", "OK", "Error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = Show-InputDialog "Copy compliance policy" "Select name for the new object" "$($dgObjects.SelectedItem.displayName) - Copy"
|
||||||
|
|
||||||
|
if($ret)
|
||||||
|
{
|
||||||
|
# Export profile
|
||||||
|
Write-Status "Export $($dgObjects.SelectedItem.displayName)"
|
||||||
|
# Convert to Json and back to clone the object
|
||||||
|
$obj = ConvertTo-Json $dgObjects.SelectedItem.Object -Depth 5 | ConvertFrom-Json
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
# Import new profile
|
||||||
|
$obj.displayName = $ret
|
||||||
|
Import-CompliancePolicy $obj | Out-null
|
||||||
|
|
||||||
|
$dgObjects.ItemsSource = @(Get-CompliancePolicyObjects)
|
||||||
|
}
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
|
$dgObjects.Focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-CompliancePolicy
|
||||||
|
{
|
||||||
|
param($obj)
|
||||||
|
|
||||||
|
$json = ConvertTo-Json $obj -Depth 5
|
||||||
|
$json = $json.Trim().TrimEnd('}').Trim()
|
||||||
|
$json += @"
|
||||||
|
,
|
||||||
|
"scheduledActionsForRule":[{"ruleName":"PasswordRequired","scheduledActionConfigurations":[{"actionType":"block","gracePeriodHours":0,"notificationTemplateId":"","notificationMessageCCList":[]}]}]
|
||||||
|
}
|
||||||
|
|
||||||
|
"@
|
||||||
|
|
||||||
|
Write-Status "Import $($obj.displayName)"
|
||||||
|
|
||||||
|
Invoke-GraphRequest -Url "/deviceManagement/deviceCompliancePolicies" -Content $json -HttpMethod POST
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AllCompliancePolicyObjects
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Import-CompliancePolicyObjects (Get-JsonFileObjects $path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-CompliancePolicyObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$Objects,
|
||||||
|
|
||||||
|
[switch]
|
||||||
|
$Selected
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Status "Import compliance policies"
|
||||||
|
|
||||||
|
foreach($obj in $objects)
|
||||||
|
{
|
||||||
|
if($Selected -and $obj.Selected -ne $true) { continue }
|
||||||
|
|
||||||
|
Write-Log "Import Compliance Policy: $($obj.Object.displayName)"
|
||||||
|
|
||||||
|
$assignments = Get-GraphAssignmentsObject $obj.Object ($obj.FileInfo.DirectoryName + "\" + $obj.FileInfo.BaseName + "_assignments.json")
|
||||||
|
|
||||||
|
$response = Import-CompliancePolicy $obj.Object
|
||||||
|
if($response)
|
||||||
|
{
|
||||||
|
$global:importedObjects++
|
||||||
|
Import-GraphAssignments $assignments "assignments" "/deviceManagement/deviceCompliancePolicies/$($response.Id)/assign"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$dgObjects.ItemsSource = @(Get-CompliancePolicyObjects)
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
273
Extensions/ConditionalAccess.psm1
Normal file
273
Extensions/ConditionalAccess.psm1
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Common module functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Add-ModuleMenuItems
|
||||||
|
{
|
||||||
|
Add-MenuItem (New-Object PSObject -Property @{
|
||||||
|
Title = (Get-ConditionalAccessName)
|
||||||
|
MenuID = "IntuneGraphAPI"
|
||||||
|
Script = [ScriptBlock]{Get-ConditionalAccess}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedImportObjects
|
||||||
|
{
|
||||||
|
$global:importObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-ConditionalAccessName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Import all conditional access policies"
|
||||||
|
Import-AllConditionalAccessObjects (Join-Path $rootFolder (Get-ConditionalAccessFolderName))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedExportObjects
|
||||||
|
{
|
||||||
|
$global:exportObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-ConditionalAccessName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Export all conditional access policies"
|
||||||
|
Get-ConditionalAccessObjects | ForEach-Object { Export-SingleConditionalAccess $PSItem.Object (Join-Path $rootFolder (Get-ConditionalAccessFolderName)) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllObjects
|
||||||
|
{
|
||||||
|
param($addObjectSubfolder)
|
||||||
|
|
||||||
|
$subFolder = ""
|
||||||
|
if($addObjectSubfolder) { $subFolder = Get-ConditionalAccessFolderName }
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Object specific functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Get-ConditionalAccessName
|
||||||
|
{
|
||||||
|
return "Conditional Access"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ConditionalAccessFolderName
|
||||||
|
{
|
||||||
|
return "ConditionalAccess"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ConditionalAccess
|
||||||
|
{
|
||||||
|
Write-Status "Loading conditional access objects"
|
||||||
|
$dgObjects.ItemsSource = @(Get-ConditionalAccessObjects)
|
||||||
|
|
||||||
|
#Scriptblocks that will perform the export tasks. empty by default
|
||||||
|
$script:exportParams = @{}
|
||||||
|
$script:exportParams.Add("ExportAllScript", [ScriptBlock]{
|
||||||
|
Export-AllConditionalAccess $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
$script:exportParams.Add("ExportSelectedScript", [ScriptBlock]{
|
||||||
|
Export-SelectedConditionalAccess $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
#Scriptblock that will perform the import all files
|
||||||
|
$script:importAll = [ScriptBlock]{
|
||||||
|
Import-AllConditionalAccessObjects $global:txtImportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import of selected files
|
||||||
|
$script:importSelected = [ScriptBlock]{
|
||||||
|
Import-ConditionalAccessObjects $global:lstFiles.ItemsSource -Selected
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will read json files
|
||||||
|
$script:getImportFiles = [ScriptBlock]{
|
||||||
|
Show-FileListBox
|
||||||
|
$global:lstFiles.ItemsSource = @(Get-JsonFileObjects $global:txtImportPath.Text -Exclude "*_Settings.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-DefaultObjectButtons -export ([scriptblock]{Show-DefaultExportGrid @script:exportParams}) -import ([scriptblock]{Show-DefaultImportGrid -ImportAll $script:importAll -ImportSelected $script:importSelected -GetFiles $script:getImportFiles})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ConditionalAccessObjects
|
||||||
|
{
|
||||||
|
#https://main.iam.ad.ext.azure.com/api/Policies/Policies?top=10&nextLink=null&appId=&includeBaseline=true
|
||||||
|
Get-AzureNativeObjects "Policies/Policies?top=10&nextLink=null&appId=&includeBaseline=true" -property @('policyName')
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllConditionalAccess
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
foreach($objTmp in ($global:dgObjects.ItemsSource))
|
||||||
|
{
|
||||||
|
Export-SingleConditionalAccess $objTmp.Object $path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SelectedConditionalAccess
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Export-SingleConditionalAccess $global:dgObjects.SelectedItem.Object $path
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SingleConditionalAccess
|
||||||
|
{
|
||||||
|
param($psObj, $path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not $psObj) { return }
|
||||||
|
|
||||||
|
if($global:runningBulkExport -ne $true)
|
||||||
|
{
|
||||||
|
if($global:chkAddCompanyName.IsChecked) { $path = Join-Path $path $global:organization.displayName }
|
||||||
|
if($global:chkAddObjectType.IsChecked) { $path = Join-Path $path (Get-ConditionalAccessFolderName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
Write-Status "Export $($psObj.policyName)"
|
||||||
|
|
||||||
|
if($psObj.baselineType -eq 0)
|
||||||
|
{
|
||||||
|
$obj = Invoke-AzureNativeRequest "Policies/$($psObj.policyId)"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$obj = Invoke-AzureNativeRequest "BaselinePolicies/$($psObj.policyId)"
|
||||||
|
}
|
||||||
|
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
$fileName = "$path\$((Remove-InvalidFileNameChars $psObj.policyName)).json"
|
||||||
|
ConvertTo-Json $obj -Depth 5 | Out-File $fileName -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
if($jsonObj.usersV2.included.groupIds)
|
||||||
|
{
|
||||||
|
$jsonObj.usersV2.included.groupIds | ForEach-Object { Add-GroupMigrationObject $PSItem }
|
||||||
|
}
|
||||||
|
|
||||||
|
if($jsonObj.usersV2.excluded.groupIds)
|
||||||
|
{
|
||||||
|
$jsonObj.usersV2.excluded.groupIds | ForEach-Object { Add-GroupMigrationObject $PSItem }
|
||||||
|
}
|
||||||
|
|
||||||
|
if($jsonObj.usersV2.included.userIds -or $jsonObj.usersV2.excluded.userIds)
|
||||||
|
{
|
||||||
|
Write-Log "Users are specified in $($psObj.policyName). User are not supported in this version. This conditional access policy might not be imported" 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if($jsonObj.usersV2.included.roleIds -or $jsonObj.usersV2.excluded.roleIds)
|
||||||
|
{
|
||||||
|
Write-Log "Roles are specified in $($psObj.policyName). Roles are not supported in this version. This conditional access policy might not be imported" 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if($jsonObj.conditions.namedNetworks.includedNetworkIds -or $jsonObj.conditions.namedNetworks.excludedNetworkIds)
|
||||||
|
{
|
||||||
|
Write-Log "Networks are specified in $($psObj.policyName). Named networks are not supported in this version. This conditional access policy might not be imported" 2
|
||||||
|
}
|
||||||
|
|
||||||
|
# There might be a lot more to check here...
|
||||||
|
|
||||||
|
$global:exportedObjects++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-ConditionalAccess
|
||||||
|
{
|
||||||
|
param($obj)
|
||||||
|
|
||||||
|
$json = Update-JsonForEnvironment $json
|
||||||
|
|
||||||
|
if($obj.baselineType -eq 0)
|
||||||
|
{
|
||||||
|
$obj.policyId = ""
|
||||||
|
$obj.isAllProtocolsEnabled = $true
|
||||||
|
$json = ConvertTo-Json $obj -Depth 10
|
||||||
|
$json = Update-JsonForEnvironment $json
|
||||||
|
|
||||||
|
if((Invoke-AzureNativeRequest "Policies/Validate" -Method POST -Body $json) -eq 11)
|
||||||
|
{
|
||||||
|
Invoke-AzureNativeRequest "Policies" -Method POST -Body $json | Out-Null
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Log "Policy validation of json data failed" 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Log "Conditional Access Baseline Policies does not support import"
|
||||||
|
#Invoke-AzureNativeRequest "BaselinePolicies/$($obj.id)" -Method PUT -Body (ConvertTo-Json $obj -Depth 5) | Out-Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AllConditionalAccessObjects
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Import-ConditionalAccessObjects (Get-JsonFileObjects $path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-ConditionalAccessObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$Objects,
|
||||||
|
|
||||||
|
[switch]
|
||||||
|
$Selected
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Status "Import conditional access policies"
|
||||||
|
|
||||||
|
foreach($obj in $objects)
|
||||||
|
{
|
||||||
|
if($Selected -and $obj.Selected -ne $true) { continue }
|
||||||
|
|
||||||
|
Write-Log "Import Conditional Access: $($obj.Object.policyName)"
|
||||||
|
|
||||||
|
$response = Import-ConditionalAccess $obj.Object
|
||||||
|
|
||||||
|
if($response)
|
||||||
|
{
|
||||||
|
$global:importedObjects++
|
||||||
|
}
|
||||||
|
# No additionl assignments on conditional access policies
|
||||||
|
}
|
||||||
|
$dgObjects.ItemsSource = @(Get-ConditionalAccessObjects)
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
|
|
||||||
|
<#
|
||||||
|
# Get all networks
|
||||||
|
Get-AzureNativeObjects "NamedNetworksV2"
|
||||||
|
|
||||||
|
# Network example
|
||||||
|
#{"networkName":"Australia","cidrIpRanges":[],"categories":[],"applyToUnknownCountry":false,"countryIsoCodes":["AU"],"isTrustedLocation":false,"namedLocationsType":2}
|
||||||
|
|
||||||
|
Get-AzureNativeObjects "NamedNetworksV2" -Method POST -Body $json | Out-Nul
|
||||||
|
|
||||||
|
# Get all contry codes
|
||||||
|
NamedNetworksV2/CountryCodes
|
||||||
|
#>
|
||||||
246
Extensions/ConfigurationItems.psm1
Normal file
246
Extensions/ConfigurationItems.psm1
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Common module functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Add-ModuleMenuItems
|
||||||
|
{
|
||||||
|
Add-MenuItem (New-Object PSObject -Property @{
|
||||||
|
Title = (Get-DeviceConfigurationName)
|
||||||
|
MenuID = "IntuneGraphAPI"
|
||||||
|
Script = [ScriptBlock]{Get-DeviceConfigurations}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedImportObjects
|
||||||
|
{
|
||||||
|
$global:importObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-DeviceConfigurationName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Import all device configuration objects"
|
||||||
|
Import-AllDeviceConfigurationObjects (Join-Path $rootFolder (Get-DeviceConfigurationFolderName))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedExportObjects
|
||||||
|
{
|
||||||
|
$global:exportObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-DeviceConfigurationName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Export all device configuration objects"
|
||||||
|
Get-DeviceConfigurationObjects | ForEach-Object { Export-SingleDeviceConfiguration $PSItem.Object (Join-Path $rootFolder (Get-DeviceConfigurationFolderName)) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllObjects
|
||||||
|
{
|
||||||
|
param($addObjectSubfolder)
|
||||||
|
|
||||||
|
$subFolder = ""
|
||||||
|
if($addObjectSubfolder) { $subFolder = Get-DeviceConfigurationFolderName }
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Object specific functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Get-DeviceConfigurationName
|
||||||
|
{
|
||||||
|
return "Device Configurations"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-DeviceConfigurationFolderName
|
||||||
|
{
|
||||||
|
return "DeviceConfigurations"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-DeviceConfigurations
|
||||||
|
{
|
||||||
|
Write-Status "Loading device configurations"
|
||||||
|
$dgObjects.ItemsSource = @(Get-DeviceConfigurationObjects)
|
||||||
|
|
||||||
|
#Scriptblocks that will perform the export tasks. empty by default
|
||||||
|
$script:exportParams = @{}
|
||||||
|
$script:exportParams.Add("ExportAllScript", [ScriptBlock]{
|
||||||
|
Export-AllDeviceConfigurations $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
$script:exportParams.Add("ExportSelectedScript", [ScriptBlock]{
|
||||||
|
Export-SelectedDeviceConfiguration $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
#Scriptblock that will perform the import all files
|
||||||
|
$script:importAll = [ScriptBlock]{
|
||||||
|
Import-AllDeviceConfigurationObjects $global:txtImportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import of selected files
|
||||||
|
$script:importSelected = [ScriptBlock]{
|
||||||
|
Import-DeviceConfigurationObjects $global:lstFiles.ItemsSource -Selected
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will read json files
|
||||||
|
$script:getImportFiles = [ScriptBlock]{
|
||||||
|
Show-FileListBox
|
||||||
|
$global:lstFiles.ItemsSource = @(Get-JsonFileObjects $global:txtImportPath.Text -Exclude "*_Settings.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-DefaultObjectButtons -export ([scriptblock]{Show-DefaultExportGrid @script:exportParams}) -import ([scriptblock]{Show-DefaultImportGrid -ImportAll $script:importAll -ImportSelected $script:importSelected -GetFiles $script:getImportFiles}) -copy ([scriptblock]{Copy-DeviceConfiguration})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-DeviceConfigurationObjects
|
||||||
|
{
|
||||||
|
Get-GraphObjects -Url "/deviceManagement/deviceConfigurations"#,"/deviceManagement/groupPolicyConfigurations"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllDeviceConfigurations
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
foreach($objTmp in ($global:dgObjects.ItemsSource))
|
||||||
|
{
|
||||||
|
Export-SingleDeviceConfiguration $objTmp.Object $path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SelectedDeviceConfiguration
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Export-SingleDeviceConfiguration $global:dgObjects.SelectedItem.Object $path
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SingleDeviceConfiguration
|
||||||
|
{
|
||||||
|
param($psObj, $path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not $psObj) { return }
|
||||||
|
|
||||||
|
if($global:runningBulkExport -ne $true)
|
||||||
|
{
|
||||||
|
if($global:chkAddCompanyName.IsChecked) { $path = Join-Path $path $global:organization.displayName }
|
||||||
|
if($global:chkAddObjectType.IsChecked) { $path = Join-Path $path (Get-DeviceConfigurationFolderName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
Write-Status "Export $($psObj.displayName)"
|
||||||
|
$obj = Invoke-GraphRequest -Url "/deviceManagement/deviceConfigurations/$($psObj.id)?`$expand=assignments"
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
$fileName = "$path\$((Remove-InvalidFileNameChars $obj.displayName)).json"
|
||||||
|
ConvertTo-Json $obj -Depth 5 | Out-File $fileName -Force
|
||||||
|
if($script:chkExportScript.IsChecked)
|
||||||
|
{
|
||||||
|
$fileName = "$path\$($obj.FileName)"
|
||||||
|
[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($obj.scriptContent)) | Out-File $fileName -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-MigrationInfo $obj.assignments
|
||||||
|
|
||||||
|
$global:exportedObjects++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-DeviceConfiguration
|
||||||
|
{
|
||||||
|
if(-not $dgObjects.SelectedItem)
|
||||||
|
{
|
||||||
|
[System.Windows.MessageBox]::Show("No object selected`n`nSelect device configuration item you want to copy", "Error", "OK", "Error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = Show-InputDialog "Copy device configuration" "Select name for the new object" "$($dgObjects.SelectedItem.displayName) - Copy"
|
||||||
|
|
||||||
|
if($ret)
|
||||||
|
{
|
||||||
|
# Export profile
|
||||||
|
Write-Status "Export $($dgObjects.SelectedItem.displayName)"
|
||||||
|
# Convert to Json and back to clone the object
|
||||||
|
$obj = ConvertTo-Json $dgObjects.SelectedItem.Object -Depth 5 | ConvertFrom-Json
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
# Import new profile
|
||||||
|
$obj.displayName = $ret
|
||||||
|
Import-DeviceConfiguration $obj | Out-Null
|
||||||
|
|
||||||
|
$dgObjects.ItemsSource = @(Get-DeviceConfigurationObjects)
|
||||||
|
}
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
|
$dgObjects.Focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-DeviceConfiguration
|
||||||
|
{
|
||||||
|
param($obj)
|
||||||
|
|
||||||
|
if(($obj | GM -MemberType NoteProperty -Name "supportsScopeTags"))
|
||||||
|
{
|
||||||
|
# Remove read-only property
|
||||||
|
$obj.PSObject.Properties.Remove('supportsScopeTags')
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Status "Import $($obj.displayName)"
|
||||||
|
|
||||||
|
Invoke-GraphRequest -Url "/deviceManagement/deviceConfigurations" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AllDeviceConfigurationObjects
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Import-DeviceConfigurationObjects (Get-JsonFileObjects $path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-DeviceConfigurationObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$Objects,
|
||||||
|
|
||||||
|
[switch]
|
||||||
|
$Selected
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Status "Import device configuration profiles"
|
||||||
|
|
||||||
|
foreach($obj in $objects)
|
||||||
|
{
|
||||||
|
if($Selected -and $obj.Selected -ne $true) { continue }
|
||||||
|
|
||||||
|
Write-Log "Import device configuration policy: $($obj.Object.displayName)"
|
||||||
|
|
||||||
|
$assignments = Get-GraphAssignmentsObject $obj.Object ($obj.FileInfo.DirectoryName + "\" + $obj.FileInfo.BaseName + "_assignments.json")
|
||||||
|
|
||||||
|
$response = Import-DeviceConfiguration $obj.Object
|
||||||
|
if($response)
|
||||||
|
{
|
||||||
|
$global:importedObjects++
|
||||||
|
Import-GraphAssignments $assignments "assignments" "/deviceManagement/deviceConfigurations/$($response.Id)/assign"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$dgObjects.ItemsSource = @(Get-DeviceConfigurationObjects)
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
277
Extensions/EnrollmentStatusPage.psm1
Normal file
277
Extensions/EnrollmentStatusPage.psm1
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Common module functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Add-ModuleMenuItems
|
||||||
|
{
|
||||||
|
Add-MenuItem (New-Object PSObject -Property @{
|
||||||
|
Title = (Get-ESPName)
|
||||||
|
MenuID = "IntuneGraphAPI"
|
||||||
|
Script = [ScriptBlock]{Get-ESPs}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedImportObjects
|
||||||
|
{
|
||||||
|
$global:importObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-ESPName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Import all enrollment status page settings"
|
||||||
|
Import-AllESPObjects (Join-Path $rootFolder (Get-ESPFolderName))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedExportObjects
|
||||||
|
{
|
||||||
|
$global:exportObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-ESPName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Export all enrollment status page settings"
|
||||||
|
Get-ESPObjects | ForEach-Object { Export-SingleESP $PSItem.Object (Join-Path $rootFolder (Get-ESPFolderName)) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllObjects
|
||||||
|
{
|
||||||
|
param($addObjectSubfolder)
|
||||||
|
|
||||||
|
$subFolder = ""
|
||||||
|
if($addObjectSubfolder) { $subFolder = Get-ESPFolderName }
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Object specific functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Get-ESPName
|
||||||
|
{
|
||||||
|
return "Enrollment Status Page"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Get-ESPFolderName
|
||||||
|
{
|
||||||
|
return "EnrollmentStatusPage"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ESPs
|
||||||
|
{
|
||||||
|
Write-Status "Loading enrollment status page objects"
|
||||||
|
$dgObjects.ItemsSource = @(Get-ESPObjects)
|
||||||
|
|
||||||
|
#Scriptblocks that will perform the export tasks. empty by default
|
||||||
|
$script:exportParams = @{}
|
||||||
|
$script:exportParams.Add("ExportAllScript", [ScriptBlock]{
|
||||||
|
Export-AllESPs $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
$script:exportParams.Add("ExportSelectedScript", [ScriptBlock]{
|
||||||
|
Export-SelectedESP $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
#Scriptblock that will perform the import all files
|
||||||
|
$script:importAll = [ScriptBlock]{
|
||||||
|
Import-AllESPObjects $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import of selected files
|
||||||
|
$script:importSelected = [ScriptBlock]{
|
||||||
|
Import-ESPObjects $global:lstFiles.ItemsSource -Selected
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will read json files
|
||||||
|
$script:getImportFiles = [ScriptBlock]{
|
||||||
|
Show-FileListBox
|
||||||
|
$global:lstFiles.ItemsSource = @(Get-JsonFileObjects $global:txtImportPath.Text -Exclude "*_Settings.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-DefaultObjectButtons -export ([scriptblock]{Show-DefaultExportGrid @script:exportParams}) -import ([scriptblock]{Show-DefaultImportGrid -ImportAll $script:importAll -ImportSelected $script:importSelected -GetFiles $script:getImportFiles}) -copy ([scriptblock]{Copy-ESP})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ESPObjects
|
||||||
|
{
|
||||||
|
Get-GraphObjects -Url "/deviceManagement/deviceEnrollmentConfigurations"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllESPs
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
foreach($objTmp in ($global:dgObjects.ItemsSource))
|
||||||
|
{
|
||||||
|
Export-SingleESP $objTmp.Object $path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SelectedESP
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Export-SingleESP $global:dgObjects.SelectedItem.Object $path
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SingleESP
|
||||||
|
{
|
||||||
|
param($psObj, $path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not $psObj) { return }
|
||||||
|
|
||||||
|
if($global:runningBulkExport -ne $true)
|
||||||
|
{
|
||||||
|
if($global:chkAddCompanyName.IsChecked) { $path = Join-Path $path $global:organization.displayName }
|
||||||
|
if($global:chkAddObjectType.IsChecked) { $path = Join-Path $path (Get-ESPFolderName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
Write-Status "Export $($psObj.displayName)"
|
||||||
|
$obj = Invoke-GraphRequest -Url "/deviceManagement/deviceEnrollmentConfigurations/$($psObj.id)" #?`$expand=assignments"
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
if($obj.id -like "*_default*")
|
||||||
|
{
|
||||||
|
$idx = $obj.id.ToLower().IndexOf("_default")
|
||||||
|
$baseName = "Default_" + $obj.id.SubString($idx + "_default".Length)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# ?`$expand=assignments is not working so get assignments
|
||||||
|
$assignments = Invoke-GraphRequest -Url "/deviceManagement/deviceEnrollmentConfigurations/$($obj.id)/assignments"
|
||||||
|
if($assignments.value)
|
||||||
|
{
|
||||||
|
$obj | Add-Member -NotePropertyName "assignments" -NotePropertyValue $assignments.value
|
||||||
|
}
|
||||||
|
$baseName = Remove-InvalidFileNameChars $obj.displayName
|
||||||
|
}
|
||||||
|
$fileName = "$path\$baseName.json"
|
||||||
|
ConvertTo-Json $obj -Depth 5 | Out-File $fileName -Force
|
||||||
|
|
||||||
|
Add-MigrationInfo $obj.assignments
|
||||||
|
}
|
||||||
|
$global:exportedObjects++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-ESP
|
||||||
|
{
|
||||||
|
if(-not $dgObjects.SelectedItem)
|
||||||
|
{
|
||||||
|
[System.Windows.MessageBox]::Show("No object selected`n`nSelect enrollment status page item you want to copy", "Error", "OK", "Error") | Out-Null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if($dgObjects.SelectedItem.Object.id -like "*_default*")
|
||||||
|
{
|
||||||
|
[System.Windows.MessageBox]::Show("You cannot copy default items`n`nSelect custom entrollment status page item", "Error", "OK", "Error") | Out-Null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = Show-InputDialog "Copy enrollment status page" "Select name for the new object" "$($dgObjects.SelectedItem.displayName) - Copy"
|
||||||
|
|
||||||
|
if($ret)
|
||||||
|
{
|
||||||
|
# Export profile
|
||||||
|
Write-Status "Export $($dgObjects.SelectedItem.displayName)"
|
||||||
|
# Convert to Json and back to clone the object
|
||||||
|
$obj = ConvertTo-Json $dgObjects.SelectedItem.Object -Depth 5 | ConvertFrom-Json
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
# Import new profile
|
||||||
|
$obj.displayName = $ret
|
||||||
|
Import-ESP $obj | Out-Null
|
||||||
|
|
||||||
|
$dgObjects.ItemsSource = @(Get-ESPObjects)
|
||||||
|
}
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
|
$dgObjects.Focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-ESP
|
||||||
|
{
|
||||||
|
param($obj)
|
||||||
|
|
||||||
|
if($obj.id -like "*_default*")
|
||||||
|
{
|
||||||
|
Write-Status "Update $($obj.displayName)"
|
||||||
|
|
||||||
|
Invoke-GraphRequest -Url "/deviceManagement/deviceEnrollmentConfigurations/$($obj.id)" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod PATCH
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Status "Import $($obj.displayName)"
|
||||||
|
|
||||||
|
Invoke-GraphRequest -Url "/deviceManagement/deviceEnrollmentConfigurations" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AllESPObjects
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Import-ESPObjects (Get-JsonFileObjects $path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-ESPObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$Objects,
|
||||||
|
|
||||||
|
[switch]
|
||||||
|
$Selected
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Status "Import enrollment status page"
|
||||||
|
|
||||||
|
foreach($obj in $Objects)
|
||||||
|
{
|
||||||
|
if($Selected -and $obj.Selected -ne $true) { continue }
|
||||||
|
|
||||||
|
if($obj.Object.id -like "*_default*")
|
||||||
|
{
|
||||||
|
$idx = $obj.Object.id.ToLower().IndexOf("_default")
|
||||||
|
$extInfo = " ($($obj.Object.id.SubString($idx + "_default".Length)))"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$extInfo = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "Import Enrollment Status Page: $($obj.Object.displayName)$extInfo"
|
||||||
|
|
||||||
|
$assignments = Get-GraphAssignmentsObject $obj.Object ($obj.FileInfo.DirectoryName + "\" + $obj.FileInfo.BaseName + "_assignments.json")
|
||||||
|
|
||||||
|
$response = Import-ESP $obj.Object
|
||||||
|
|
||||||
|
if($response)
|
||||||
|
{
|
||||||
|
$global:importedObjects++
|
||||||
|
Import-GraphAssignments $assignments "enrollmentConfigurationAssignments" "/deviceManagement/deviceEnrollmentConfigurations/$($response.Id)/assign"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$dgObjects.ItemsSource = @(Get-ESPObjects)
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
322
Extensions/GroupPolicy.psm1
Normal file
322
Extensions/GroupPolicy.psm1
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Common module functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Add-ModuleMenuItems
|
||||||
|
{
|
||||||
|
Add-MenuItem (New-Object PSObject -Property @{
|
||||||
|
Title = (Get-GPOSettingName)
|
||||||
|
MenuID = "IntuneGraphAPI"
|
||||||
|
Script = [ScriptBlock]{Get-GPOSettings}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedImportObjects
|
||||||
|
{
|
||||||
|
$global:importObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-GPOSettingName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Import all administrative templates"
|
||||||
|
Import-AllGPOSettingObjects (Join-Path $rootFolder (Get-GPOSettingFolderName))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedExportObjects
|
||||||
|
{
|
||||||
|
$global:exportObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-GPOSettingName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Export all administrative templates"
|
||||||
|
Get-GPOSettingObjects | ForEach-Object { Export-SingleGPOSetting $PSItem.Object (Join-Path $rootFolder (Get-GPOSettingFolderName)) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllObjects
|
||||||
|
{
|
||||||
|
param($addObjectSubfolder)
|
||||||
|
|
||||||
|
$subFolder = ""
|
||||||
|
if($addObjectSubfolder) { $subFolder = Get-GPOSettingFolderName }
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Object specific functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Get-GPOSettingName
|
||||||
|
{
|
||||||
|
return "Administrative Templates"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Get-GPOSettingFolderName
|
||||||
|
{
|
||||||
|
return "AdministrativeTemplates"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-GPOSettings
|
||||||
|
{
|
||||||
|
Write-Status "Loading administrative templates"
|
||||||
|
$dgObjects.ItemsSource = @(Get-GPOSettingObjects)
|
||||||
|
|
||||||
|
#Scriptblocks that will perform the export tasks. empty by default
|
||||||
|
$script:exportParams = @{}
|
||||||
|
$script:exportParams.Add("ExportAllScript", [ScriptBlock]{
|
||||||
|
Export-AllGPOSettings $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
$script:exportParams.Add("ExportSelectedScript", [ScriptBlock]{
|
||||||
|
Export-SelectedGPOSetting $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import all files
|
||||||
|
$script:importAll = [ScriptBlock]{
|
||||||
|
Import-AllGPOSettingObjects $global:txtImportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import of selected files
|
||||||
|
$script:importSelected = [ScriptBlock]{
|
||||||
|
Import-GPOSettingObjects $global:lstFiles.ItemsSource -Selected
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will read json files
|
||||||
|
$script:getImportFiles = [ScriptBlock]{
|
||||||
|
Show-FileListBox
|
||||||
|
$global:lstFiles.ItemsSource = @(Get-JsonFileObjects $global:txtImportPath.Text -Exclude "*_Settings.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-DefaultObjectButtons -export ([scriptblock]{Show-DefaultExportGrid @script:exportParams}) -import ([scriptblock]{Show-DefaultImportGrid -ImportAll $script:importAll -ImportSelected $script:importSelected -GetFiles $script:getImportFiles}) -copy ([scriptblock]{Copy-GPOSetting})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-GPOSettingObjects
|
||||||
|
{
|
||||||
|
Get-GraphObjects -Url "/deviceManagement/groupPolicyConfigurations"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllGPOSettings
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
foreach($objTmp in ($global:dgObjects.ItemsSource))
|
||||||
|
{
|
||||||
|
Export-SingleGPOSetting $objTmp.Object $path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SelectedGPOSetting
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Export-SingleGPOSetting $global:dgObjects.SelectedItem.Object $path
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-GPOObjectSettings
|
||||||
|
{
|
||||||
|
param($GPOObj)
|
||||||
|
|
||||||
|
$gpoSettings = @()
|
||||||
|
|
||||||
|
# Get all configured policies in the Administrative Templates profile
|
||||||
|
$GPODefinitionValues = Invoke-GraphRequest -Url "/deviceManagement/groupPolicyConfigurations/$($GPOObj.id)/definitionValues?`$expand=definition"
|
||||||
|
foreach($definitionValue in $GPODefinitionValues.value)
|
||||||
|
{
|
||||||
|
# Get presentation values for the current settings (with presentation object included)
|
||||||
|
$presentationValues = Invoke-GraphRequest -Url "/deviceManagement/groupPolicyConfigurations/$($GPOObj.id)/definitionValues/$($definitionValue.id)/presentationValues?`$expand=presentation"
|
||||||
|
|
||||||
|
# Set base policy settings
|
||||||
|
$obj = @{
|
||||||
|
"enabled" = $definitionValue.enabled
|
||||||
|
"definition@odata.bind" = "$($global:graphURL)/deviceManagement/groupPolicyDefinitions('$($definitionValue.definition.id)')"
|
||||||
|
}
|
||||||
|
|
||||||
|
if($presentationValues.value)
|
||||||
|
{
|
||||||
|
# Policy presentation values set e.g. a drop down list, check box, text box etc.
|
||||||
|
$obj.presentationValues = @()
|
||||||
|
|
||||||
|
$presentations = $null
|
||||||
|
foreach ($presentationValue in $presentationValues.value)
|
||||||
|
{
|
||||||
|
# Add presentation@odata.bind property that links the value to the presentation object
|
||||||
|
$presentationValue | Add-Member -MemberType NoteProperty -Name "presentation@odata.bind" -Value "$($global:graphURL)/deviceManagement/groupPolicyDefinitions('$($definitionValue.definition.id)')/presentations('$($presentationValue.presentation.id)')"
|
||||||
|
|
||||||
|
#Remove presentation object so it is not included in the export
|
||||||
|
Remove-ObjectProperty $presentationValue "presentation"
|
||||||
|
|
||||||
|
#Optional removes. Import will igonre them
|
||||||
|
Remove-ObjectProperty $presentationValue "id"
|
||||||
|
Remove-ObjectProperty $presentationValue "lastModifiedDateTime"
|
||||||
|
Remove-ObjectProperty $presentationValue "createdDateTime"
|
||||||
|
|
||||||
|
# Add presentation value to the list
|
||||||
|
$obj.presentationValues += $presentationValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$gpoSettings += $obj
|
||||||
|
}
|
||||||
|
$gpoSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SingleGPOSetting
|
||||||
|
{
|
||||||
|
param($psObj, $path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not $psObj) { return }
|
||||||
|
|
||||||
|
if($global:runningBulkExport -ne $true)
|
||||||
|
{
|
||||||
|
if($global:chkAddCompanyName.IsChecked) { $path = Join-Path $path $global:organization.displayName }
|
||||||
|
if($global:chkAddObjectType.IsChecked) { $path = Join-Path $path (Get-GPOSettingFolderName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
Write-Status "Export $($psObj.displayName)"
|
||||||
|
$obj = Invoke-GraphRequest -Url "deviceManagement/groupPolicyConfigurations/$($psObj.Id)?`$expand=assignments"
|
||||||
|
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
# Save Administrative Templates profile
|
||||||
|
ConvertTo-Json $obj -Depth 5 | Out-File "$path\$((Remove-InvalidFileNameChars $obj.displayName)).json" -Force
|
||||||
|
|
||||||
|
# Collect and save all the settings of the Administrative Templates profile
|
||||||
|
$gpoSettings = Get-GPOObjectSettings $obj
|
||||||
|
ConvertTo-Json $gpoSettings -Depth 5 | Out-File "$path\$($obj.displayName)_Settings.json" -Force
|
||||||
|
|
||||||
|
# Export assignment info
|
||||||
|
Add-MigrationInfo $obj.assignments
|
||||||
|
}
|
||||||
|
$global:exportedObjects++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-GPOSetting
|
||||||
|
{
|
||||||
|
if(-not $dgObjects.SelectedItem)
|
||||||
|
{
|
||||||
|
[System.Windows.MessageBox]::Show("No object selected`n`nSelect administrative templates profile you want to copy", "Error", "OK", "Error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = Show-InputDialog "Copy administrative template" "Select name for the new profile" "$($dgObjects.SelectedItem.displayName) - Copy"
|
||||||
|
|
||||||
|
if($ret)
|
||||||
|
{
|
||||||
|
# Export profile
|
||||||
|
Write-Status "Export $($dgObjects.SelectedItem.displayName)"
|
||||||
|
# Convert to Json and back to clone the object
|
||||||
|
$obj = ConvertTo-Json $dgObjects.SelectedItem.Object -Depth 5 | ConvertFrom-Json
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
# Get the settings of the profile
|
||||||
|
$gpoSettings = Get-GPOObjectSettings $obj
|
||||||
|
|
||||||
|
# Import the new profile
|
||||||
|
$obj.displayName = $ret
|
||||||
|
Import-GPOSetting $obj $gpoSettings | Out-Null
|
||||||
|
|
||||||
|
#Reload objects
|
||||||
|
$dgObjects.ItemsSource = @(Get-GPOSettingObjects)
|
||||||
|
}
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
|
$dgObjects.Focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-GPOSetting
|
||||||
|
{
|
||||||
|
param($obj, $settings)
|
||||||
|
|
||||||
|
Write-Status "Import $($obj.displayName)"
|
||||||
|
|
||||||
|
# Import Administrative Template profile
|
||||||
|
$response = Invoke-GraphRequest -Url "/deviceManagement/groupPolicyConfigurations" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST
|
||||||
|
|
||||||
|
if($response)
|
||||||
|
{
|
||||||
|
foreach($setting in $settings)
|
||||||
|
{
|
||||||
|
# Import each setting for the Administrative Template profile
|
||||||
|
$response2 = Invoke-GraphRequest -Url "/deviceManagement/groupPolicyConfigurations/$($response.id)/definitionValues" -Content (ConvertTo-Json $setting -Depth 5) -HttpMethod POST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$response
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AllGPOSettingObjects
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
# Read json files and import all objects
|
||||||
|
# Note: Each json file must match the object type being imported
|
||||||
|
Import-GPOSettingObjects (Get-JsonFileObjects $path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-GPOSettingObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$Objects,
|
||||||
|
|
||||||
|
[switch]
|
||||||
|
$Selected
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Status "Import administrative template profile"
|
||||||
|
|
||||||
|
foreach($obj in $objects)
|
||||||
|
{
|
||||||
|
if($Selected -and $obj.Selected -ne $true) { continue }
|
||||||
|
|
||||||
|
Write-Log "Import Administrative Template: $($obj.Object.displayName)"
|
||||||
|
|
||||||
|
$gpoSettings = $null
|
||||||
|
|
||||||
|
# Load settings from the <AdminTeplateName>_settings.json file
|
||||||
|
$settingsFile = ($obj.FileInfo.DirectoryName + "\" + $obj.FileInfo.BaseName + "_settings.json")
|
||||||
|
if(Test-Path $settingsFile)
|
||||||
|
{
|
||||||
|
$gpoSettings = (ConvertFrom-Json (Get-Content $settingsFile -Raw))
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get assignment settings
|
||||||
|
$assignments = Get-GraphAssignmentsObject $obj.Object ($obj.FileInfo.DirectoryName + "\" + $obj.FileInfo.BaseName + "_assignments.json")
|
||||||
|
|
||||||
|
# Import Administrative Template object
|
||||||
|
$response = Import-GPOSetting $obj.Object $gpoSettings
|
||||||
|
|
||||||
|
if($response)
|
||||||
|
{
|
||||||
|
$global:importedObjects++
|
||||||
|
# Import assignments
|
||||||
|
Import-GraphAssignments $assignments "assignments" "/deviceManagement/groupPolicyConfigurations/$($response.Id)/assign"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Reload list of objects
|
||||||
|
$dgObjects.ItemsSource = @(Get-GPOSettingObjects)
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
215
Extensions/MDM_MAM.psm1
Normal file
215
Extensions/MDM_MAM.psm1
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Common module functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Add-ModuleMenuItems
|
||||||
|
{
|
||||||
|
Add-MenuItem (New-Object PSObject -Property @{
|
||||||
|
Title = (Get-MDMMAMName)
|
||||||
|
MenuID = "IntuneGraphAPI"
|
||||||
|
Script = [ScriptBlock]{Get-MDMMAM}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedImportObjects
|
||||||
|
{
|
||||||
|
$global:importObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-MDMMAMName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Import all MDM/MAM setting"
|
||||||
|
Import-AllMDMMAMObjects (Join-Path $rootFolder (Get-MDMMAMFolderName))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedExportObjects
|
||||||
|
{
|
||||||
|
$global:exportObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-MDMMAMName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Export all MDM/MAM settings"
|
||||||
|
Get-MDMMAMObjects | ForEach-Object { Export-SingleMDMMAM $PSItem.Object (Join-Path $rootFolder (Get-MDMMAMFolderName)) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllObjects
|
||||||
|
{
|
||||||
|
param($addObjectSubfolder)
|
||||||
|
|
||||||
|
$subFolder = ""
|
||||||
|
if($addObjectSubfolder) { $subFolder = Get-MDMMAMFolderName }
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Object specific functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Get-MDMMAMName
|
||||||
|
{
|
||||||
|
return "MDM/MAM"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-MDMMAMFolderName
|
||||||
|
{
|
||||||
|
return "MDMMAM"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-MDMMAM
|
||||||
|
{
|
||||||
|
Write-Status "Loading MDM/MAM object"
|
||||||
|
$dgObjects.ItemsSource = @(Get-MDMMAMObjects)
|
||||||
|
|
||||||
|
#Scriptblocks that will perform the export tasks. empty by default
|
||||||
|
$script:exportParams = @{}
|
||||||
|
$script:exportParams.Add("ExportAllScript", [ScriptBlock]{
|
||||||
|
Export-AllMDMMAM $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
$script:exportParams.Add("ExportSelectedScript", [ScriptBlock]{
|
||||||
|
Export-SelectedMDMMAM $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
#Scriptblock that will perform the import all files
|
||||||
|
$script:importAll = [ScriptBlock]{
|
||||||
|
Import-AllMDMMAMObjects $global:txtImportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import of selected files
|
||||||
|
$script:importSelected = [ScriptBlock]{
|
||||||
|
Import-MDMMAMObjects $global:lstFiles.ItemsSource -Selected
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will read json files
|
||||||
|
$script:getImportFiles = [ScriptBlock]{
|
||||||
|
Show-FileListBox
|
||||||
|
$global:lstFiles.ItemsSource = @(Get-JsonFileObjects $global:txtImportPath.Text -Exclude "*_Settings.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-DefaultObjectButtons -export ([scriptblock]{Show-DefaultExportGrid @script:exportParams}) -import ([scriptblock]{Show-DefaultImportGrid -ImportAll $script:importAll -ImportSelected $script:importSelected -GetFiles $script:getImportFiles})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-MDMMAMObjects
|
||||||
|
{
|
||||||
|
Get-AzureNativeObjects "MdmApplications" -property @('appDisplayName')
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllMDMMAM
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
foreach($objTmp in ($global:dgObjects.ItemsSource))
|
||||||
|
{
|
||||||
|
Export-SingleMDMMAM $objTmp.Object $path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SelectedMDMMAM
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Export-SingleMDMMAM $global:dgObjects.SelectedItem.Object $path
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SingleMDMMAM
|
||||||
|
{
|
||||||
|
param($psObj, $path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not $psObj) { return }
|
||||||
|
|
||||||
|
if($global:runningBulkExport -ne $true)
|
||||||
|
{
|
||||||
|
if($global:chkAddCompanyName.IsChecked) { $path = Join-Path $path $global:organization.displayName }
|
||||||
|
if($global:chkAddObjectType.IsChecked) { $path = Join-Path $path (Get-MDMMAMFolderName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
Write-Status "Export $($psObj.appDisplayName)"
|
||||||
|
|
||||||
|
$obj = Invoke-AzureNativeRequest "MdmApplications/$($psObj.objectId)"
|
||||||
|
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
$fileName = "$path\$((Remove-InvalidFileNameChars $obj.appDisplayName)).json"
|
||||||
|
ConvertTo-Json $obj -Depth 5 | Out-File $fileName -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
if($obj.mdmAppliesToGroups)
|
||||||
|
{
|
||||||
|
$obj.mdmAppliesToGroups | ForEach-Object { Add-GroupMigrationObject $PSItem.objectId }
|
||||||
|
}
|
||||||
|
|
||||||
|
if($obj.mamAppliesToGroups)
|
||||||
|
{
|
||||||
|
$obj.mamAppliesToGroups | ForEach-Object { Add-GroupMigrationObject $PSItem.objectId }
|
||||||
|
}
|
||||||
|
|
||||||
|
$global:exportedObjects++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-MDMMAM
|
||||||
|
{
|
||||||
|
param($obj)
|
||||||
|
|
||||||
|
$argStr = "?"
|
||||||
|
if($obj.enrollmentUrl) { $argStr += "mdmAppliesToChanged=true" }
|
||||||
|
else{ $argStr += "mdmAppliesToChanged=false" }
|
||||||
|
if($obj.mamEnrollmentUrl) { $argStr += "&mamAppliesToChanged=true" }
|
||||||
|
else{ $argStr += "&mamAppliesToChanged=false" }
|
||||||
|
|
||||||
|
$response = Invoke-AzureNativeRequest "MdmApplications/$($obj.objectId)$argStr" -Method PUT -Body (Update-JsonForEnvironment (ConvertTo-Json $obj -Depth 5))
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AllMDMMAMObjects
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Import-MDMMAMObjects (Get-JsonFileObjects $path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-MDMMAMObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$Objects,
|
||||||
|
|
||||||
|
[switch]
|
||||||
|
$Selected
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Status "Import MDM/MAM settings"
|
||||||
|
|
||||||
|
foreach($obj in $objects)
|
||||||
|
{
|
||||||
|
if($Selected -and $obj.Selected -ne $true) { continue }
|
||||||
|
|
||||||
|
Write-Log "Import MDM/MAM app settings: $($obj.Object.appDisplayName)"
|
||||||
|
|
||||||
|
Import-MDMMAM $obj.Object
|
||||||
|
|
||||||
|
# No assignments for MDM/MAM
|
||||||
|
}
|
||||||
|
$dgObjects.ItemsSource = @(Get-MDMMAMObjects)
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
1438
Extensions/MSGraphIntune.psm1
Normal file
1438
Extensions/MSGraphIntune.psm1
Normal file
File diff suppressed because it is too large
Load Diff
306
Extensions/PowerShellScripts.psm1
Normal file
306
Extensions/PowerShellScripts.psm1
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Common module functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Add-ModuleMenuItems
|
||||||
|
{
|
||||||
|
Add-MenuItem (New-Object PSObject -Property @{
|
||||||
|
Title = (Get-PowerShellScriptName)
|
||||||
|
MenuID = "IntuneGraphAPI"
|
||||||
|
Script = [ScriptBlock]{Get-PowerShellScripts}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedImportObjects
|
||||||
|
{
|
||||||
|
$global:importObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-PowerShellScriptName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Import all powershell scripts"
|
||||||
|
Import-AllPowerShellScriptObjects (Join-Path $rootFolder (Get-PowerShellScriptFolderName))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedExportObjects
|
||||||
|
{
|
||||||
|
$global:exportObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-PowerShellScriptName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
Write-Status "Export all powershell scripts"
|
||||||
|
Get-PowerShellScriptObjects | ForEach-Object { Export-SinglePowerShellScript $PSItem.Object (Join-Path $rootFolder (Get-PowerShellScriptFolderName)) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllObjects
|
||||||
|
{
|
||||||
|
param($addObjectSubfolder)
|
||||||
|
|
||||||
|
$subFolder = ""
|
||||||
|
if($addObjectSubfolder) { $subFolder = Get-PowerShellScriptFolderName }
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Object specific functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Get-PowerShellScriptName
|
||||||
|
{
|
||||||
|
return "PowerShell Script"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-PowerShellScriptFolderName
|
||||||
|
{
|
||||||
|
return "PowerShell"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-PowerShellScripts
|
||||||
|
{
|
||||||
|
Write-Status "Loading PowerShell objects"
|
||||||
|
$dgObjects.ItemsSource = @(Get-PowerShellScriptObjects)
|
||||||
|
|
||||||
|
#Scriptblocks that will perform the export tasks. empty by default
|
||||||
|
$script:exportParams = @{}
|
||||||
|
$script:exportParams.Add("ExportAllScript", [ScriptBlock]{
|
||||||
|
Export-AllPowerShellScripts $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
$script:exportParams.Add("ExportSelectedScript", [ScriptBlock]{
|
||||||
|
Export-SelectedPowerShellScript $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import all files
|
||||||
|
$script:importAll = [ScriptBlock]{
|
||||||
|
Import-AllPowerShellScriptObjects $global:txtImportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import of selected files
|
||||||
|
$script:importSelected = [ScriptBlock]{
|
||||||
|
Import-PowerShellScriptObjects $global:lstFiles.ItemsSource -Selected
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will read json files
|
||||||
|
$script:getImportFiles = [ScriptBlock]{
|
||||||
|
Show-FileListBox
|
||||||
|
$global:lstFiles.ItemsSource = @(Get-JsonFileObjects $global:txtImportPath.Text -Exclude "*_Settings.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
$exportExtension = (New-Object PSObject -Property @{
|
||||||
|
Xaml = @"
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<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 script" />
|
||||||
|
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="Export the powershell script to a ps1 file" />
|
||||||
|
</StackPanel>
|
||||||
|
<CheckBox Grid.Column='1' Name='chkExportScript' VerticalAlignment="Center" IsChecked="true" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
"@
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($form)
|
||||||
|
$script:chkExportScript = $form.FindName("chkExportScript")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$script:exportParams.Add("Extension", $exportExtension)
|
||||||
|
|
||||||
|
Add-DefaultObjectButtons -export ([scriptblock]{Show-DefaultExportGrid @script:exportParams}) -import ([scriptblock]{Show-DefaultImportGrid -ImportAll $script:importAll -ImportSelected $script:importSelected -GetFiles $script:getImportFiles}) -copy ([scriptblock]{Copy-PowerShellScript})
|
||||||
|
|
||||||
|
#Add download button
|
||||||
|
$btnDownload = New-Object System.Windows.Controls.Button
|
||||||
|
$btnDownload.Content = 'Download'
|
||||||
|
$btnDownload.Name = 'btnDownload'
|
||||||
|
$btnDownload.Margin = "5,0,0,0"
|
||||||
|
$btnDownload.Width = "100"
|
||||||
|
$spSubMenu.Children.Insert(0, $btnDownload)
|
||||||
|
|
||||||
|
$btnDownload.Add_Click({
|
||||||
|
Invoke-DownloadScript
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-PowerShellScriptObjects
|
||||||
|
{
|
||||||
|
Get-GraphObjects -Url "/deviceManagement/deviceManagementScripts"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllPowerShellScripts
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
foreach($objTmp in ($global:dgObjects.ItemsSource))
|
||||||
|
{
|
||||||
|
Export-SinglePowerShellScript $objTmp.Object $path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SelectedPowerShellScript
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Export-SinglePowerShellScript $global:dgObjects.SelectedItem.Object $path
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SinglePowerShellScript
|
||||||
|
{
|
||||||
|
param($psObj, $path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not $psObj) { return }
|
||||||
|
|
||||||
|
if($global:runningBulkExport -ne $true)
|
||||||
|
{
|
||||||
|
if($global:chkAddCompanyName.IsChecked) { $path = Join-Path $path $global:organization.displayName }
|
||||||
|
if($global:chkAddObjectType.IsChecked) { $path = Join-Path $path (Get-PowerShellScriptFolderName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
Write-Status "Export $($psObj.displayName)"
|
||||||
|
$obj = Invoke-GraphRequest -Url "/deviceManagement/deviceManagementScripts/$($psObj.id)?`$expand=assignments"
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
$fileName = "$path\$((Remove-InvalidFileNameChars $obj.displayName)).json"
|
||||||
|
ConvertTo-Json $obj -Depth 5 | Out-File $fileName -Force
|
||||||
|
if($script:chkExportScript.IsChecked)
|
||||||
|
{
|
||||||
|
$fileName = "$path\$($obj.FileName)"
|
||||||
|
[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($obj.scriptContent)) | Out-File $fileName -Force
|
||||||
|
}
|
||||||
|
Add-MigrationInfo $obj.assignments
|
||||||
|
$global:exportedObjects++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-PowerShellScript
|
||||||
|
{
|
||||||
|
if(-not $dgObjects.SelectedItem)
|
||||||
|
{
|
||||||
|
[System.Windows.MessageBox]::Show("No object selected`n`nSelect PowerShell script you want to copy", "Error", "OK", "Error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = Show-InputDialog "Copy PowerShell script" "Select name for the new object" "$($global:dgObjects.SelectedItem.displayName) - Copy"
|
||||||
|
|
||||||
|
if($ret)
|
||||||
|
{
|
||||||
|
# Export profile
|
||||||
|
Write-Status "Export $($dgObjects.SelectedItem.displayName)"
|
||||||
|
$obj = Invoke-GraphRequest -Url "/deviceManagement/deviceManagementScripts/$($dgObjects.SelectedItem.id)"
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
# Import new profile
|
||||||
|
$obj.displayName = $ret
|
||||||
|
Import-PowerShellScript $obj | Out-null
|
||||||
|
|
||||||
|
$dgObjects.ItemsSource = @(Get-PowerShellScriptObjects)
|
||||||
|
}
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
|
$dgObjects.Focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-PowerShellScript
|
||||||
|
{
|
||||||
|
param($obj)
|
||||||
|
|
||||||
|
Remove-ObjectProperty $obj "id"
|
||||||
|
Remove-ObjectProperty $obj "createdDateTime"
|
||||||
|
Remove-ObjectProperty $obj "lastModifiedDateTime"
|
||||||
|
Remove-ObjectProperty $obj "assignments@odata.context"
|
||||||
|
Remove-ObjectProperty $obj "assignments"
|
||||||
|
|
||||||
|
Write-Status "Import $($obj.displayName)"
|
||||||
|
|
||||||
|
Invoke-GraphRequest -Url "/deviceManagement/deviceManagementScripts" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AllPowerShellScriptObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$path = "$env:Temp"
|
||||||
|
)
|
||||||
|
|
||||||
|
Import-PowerShellScriptObjects (Get-JsonFileObjects $path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-PowerShellScriptObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$Objects,
|
||||||
|
|
||||||
|
[switch]
|
||||||
|
$Selected
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Status "Import PowerShell scripts"
|
||||||
|
|
||||||
|
foreach($obj in $objects)
|
||||||
|
{
|
||||||
|
if($Selected -and $obj.Selected -ne $true) { continue }
|
||||||
|
|
||||||
|
Write-Log "Import PowerShell script: $($obj.Object.displayName)"
|
||||||
|
|
||||||
|
$assignments = Get-GraphAssignmentsObject $obj.Object ($obj.FileInfo.DirectoryName + "\" + $obj.FileInfo.BaseName + "_assignments.json")
|
||||||
|
|
||||||
|
$response = Import-PowerShellScript $obj.Object
|
||||||
|
|
||||||
|
if($response)
|
||||||
|
{
|
||||||
|
$global:importedObjects++
|
||||||
|
Import-GraphAssignments $assignments "deviceManagementScriptAssignments" "/deviceManagement/deviceManagementScripts/$($response.Id)/assign"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$dgObjects.ItemsSource = @(Get-PowerShellScriptObjects)
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-DownloadScript
|
||||||
|
{
|
||||||
|
if(-not $global:dgObjects.SelectedItem.Object.id) { return }
|
||||||
|
$obj = Invoke-GraphRequest -Url "/deviceManagement/deviceManagementScripts/$($global:dgObjects.SelectedItem.Object.id)"
|
||||||
|
if($obj.scriptContent)
|
||||||
|
{
|
||||||
|
Write-Log "Download PowerShell script '$($obj.FileName)' from $($obj.displayName)"
|
||||||
|
$fileName = "$path\$($obj.FileName)"
|
||||||
|
|
||||||
|
$dlgSave = New-Object -Typename System.Windows.Forms.SaveFileDialog
|
||||||
|
$dlgSave.InitialDirectory = Get-SettingValue "IntuneRootFolder" $env:Temp
|
||||||
|
$dlgSave.FileName = $obj.FileName
|
||||||
|
if($dlgSave.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK -and $dlgSave.Filename)
|
||||||
|
{
|
||||||
|
[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($obj.scriptContent)) | Out-File $dlgSave.Filename -Force
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
243
Extensions/TermsAndConditions.psm1
Normal file
243
Extensions/TermsAndConditions.psm1
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Common module functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Add-ModuleMenuItems
|
||||||
|
{
|
||||||
|
Add-MenuItem (New-Object PSObject -Property @{
|
||||||
|
Title = (Get-TermsAndConditionName)
|
||||||
|
MenuID = "IntuneGraphAPI"
|
||||||
|
Script = [ScriptBlock]{Get-TermsAndConditions}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SupportedImportObjects
|
||||||
|
{
|
||||||
|
$global:importObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-TermsAndConditionName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Import all terms and conditions"
|
||||||
|
Import-AllTermsAndConditionObjects (Join-Path $rootFolder (Get-TermsAndConditionFolderName))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Get-SupportedExportObjects
|
||||||
|
{
|
||||||
|
$global:exportObjects += (New-Object PSObject -Property @{
|
||||||
|
Selected = $true
|
||||||
|
Title = (Get-TermsAndConditionName)
|
||||||
|
Script = [ScriptBlock]{
|
||||||
|
param($rootFolder)
|
||||||
|
|
||||||
|
Write-Status "Export all Intune terms and conditions"
|
||||||
|
Get-TermsAndConditionObjects | ForEach-Object { Export-SingleTermsAndCondition $PSItem.Object (Join-Path $rootFolder (Get-TermsAndConditionFolderName)) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllObjects
|
||||||
|
{
|
||||||
|
param($addObjectSubfolder)
|
||||||
|
|
||||||
|
$subFolder = ""
|
||||||
|
if($addObjectSubfolder) { $subFolder = Get-TermsAndConditionFolderName }
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# Object specific functions
|
||||||
|
#
|
||||||
|
########################################################
|
||||||
|
function Get-TermsAndConditionName
|
||||||
|
{
|
||||||
|
return "Terms and Conditions"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-TermsAndConditionFolderName
|
||||||
|
{
|
||||||
|
return "TermsAndConditions"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-TermsAndConditions
|
||||||
|
{
|
||||||
|
Write-Status "Loading terms and conditions"
|
||||||
|
$dgObjects.ItemsSource = @(Get-TermsAndConditionObjects)
|
||||||
|
|
||||||
|
#Scriptblocks that will perform the export tasks. empty by default
|
||||||
|
$script:exportParams = @{}
|
||||||
|
$script:exportParams.Add("ExportAllScript", [ScriptBlock]{
|
||||||
|
Export-AllTermsAndConditions $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
$script:exportParams.Add("ExportSelectedScript", [ScriptBlock]{
|
||||||
|
Export-SelectedTermsAndCondition $global:txtExportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
Write-Status ""
|
||||||
|
})
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import all files
|
||||||
|
$script:importAll = [ScriptBlock]{
|
||||||
|
Import-AllTermsAndConditionObjects $global:txtImportPath.Text
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will perform the import of selected files
|
||||||
|
$script:importSelected = [ScriptBlock]{
|
||||||
|
Import-TermsAndConditionObjects $global:lstFiles.ItemsSource -Selected
|
||||||
|
Set-ObjectGrid
|
||||||
|
}
|
||||||
|
|
||||||
|
#Scriptblock that will read json files
|
||||||
|
$script:getImportFiles = [ScriptBlock]{
|
||||||
|
Show-FileListBox
|
||||||
|
$global:lstFiles.ItemsSource = @(Get-JsonFileObjects $global:txtImportPath.Text -Exclude "*_Settings.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
Add-DefaultObjectButtons -export ([scriptblock]{Show-DefaultExportGrid @script:exportParams}) -import ([scriptblock]{Show-DefaultImportGrid -ImportAll $script:importAll -ImportSelected $script:importSelected -GetFiles $script:getImportFiles}) -copy ([scriptblock]{Copy-TermsAndCondition})
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-TermsAndConditionObjects
|
||||||
|
{
|
||||||
|
Get-GraphObjects -Url "/deviceManagement/termsAndConditions"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-AllTermsAndConditions
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
foreach($objTmp in ($global:dgObjects.ItemsSource))
|
||||||
|
{
|
||||||
|
Export-SingleTermsAndCondition $objTmp.Object $path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SelectedTermsAndCondition
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Export-SingleTermsAndCondition $global:dgObjects.SelectedItem.Object $path
|
||||||
|
}
|
||||||
|
|
||||||
|
function Export-SingleTermsAndCondition
|
||||||
|
{
|
||||||
|
param($psObj, $path = "$env:Temp")
|
||||||
|
|
||||||
|
if(-not $psObj) { return }
|
||||||
|
|
||||||
|
if($global:runningBulkExport -ne $true)
|
||||||
|
{
|
||||||
|
if($global:chkAddCompanyName.IsChecked) { $path = Join-Path $path $global:organization.displayName }
|
||||||
|
if($global:chkAddObjectType.IsChecked) { $path = Join-Path $path (Get-TermsAndConditionFolderName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if(-not (Test-Path $path)) { mkdir -Path $path -Force -ErrorAction SilentlyContinue | Out-Null }
|
||||||
|
|
||||||
|
if(Test-Path $path)
|
||||||
|
{
|
||||||
|
Write-Status "Export $($psObj.displayName)"
|
||||||
|
$obj = Invoke-GraphRequest -Url "/deviceManagement/termsAndConditions/$($psObj.id)" #?`$expand=assignments"
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
# ?`$expand=assignments is not working so get assignments
|
||||||
|
$assignments = Invoke-GraphRequest -Url "/deviceManagement/termsAndConditions/$($obj.id)/assignments"
|
||||||
|
if($assignments.value)
|
||||||
|
{
|
||||||
|
$obj | Add-Member -NotePropertyName "assignments" -NotePropertyValue $assignments.value
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileName = "$path\$((Remove-InvalidFileNameChars $obj.displayName)).json"
|
||||||
|
ConvertTo-Json $obj -Depth 5 | Out-File $fileName -Force
|
||||||
|
|
||||||
|
Add-MigrationInfo $obj.assignments
|
||||||
|
}
|
||||||
|
$global:exportedObjects++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Copy-TermsAndCondition
|
||||||
|
{
|
||||||
|
if(-not $dgObjects.SelectedItem)
|
||||||
|
{
|
||||||
|
[System.Windows.MessageBox]::Show("No object selected`n`nSelect terms and conditions item you want to copy", "Error", "OK", "Error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = Show-InputDialog "Copy terms and conditions" "Select name for the new object" "$($dgObjects.SelectedItem.displayName) - Copy"
|
||||||
|
|
||||||
|
if($ret)
|
||||||
|
{
|
||||||
|
# Export profile
|
||||||
|
Write-Status "Export $($dgObjects.SelectedItem.displayName)"
|
||||||
|
# Get full object for export
|
||||||
|
$obj = Invoke-GraphRequest -Url "/deviceManagement/termsAndConditions/$($dgObjects.SelectedItem.Object.id)"
|
||||||
|
if($obj)
|
||||||
|
{
|
||||||
|
# Import new profile
|
||||||
|
$obj.displayName = $ret
|
||||||
|
Import-TermsAndCondition $obj | Out-Null
|
||||||
|
|
||||||
|
$dgObjects.ItemsSource = @(Get-TermsAndConditionObjects)
|
||||||
|
}
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
|
$dgObjects.Focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-TermsAndCondition
|
||||||
|
{
|
||||||
|
param($obj)
|
||||||
|
|
||||||
|
Write-Status "Import $($obj.displayName)"
|
||||||
|
|
||||||
|
Invoke-GraphRequest -Url "/deviceManagement/termsAndConditions" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-AllTermsAndConditionObjects
|
||||||
|
{
|
||||||
|
param($path = "$env:Temp")
|
||||||
|
|
||||||
|
Import-TermsAndConditionObjects (Get-JsonFileObjects $path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-TermsAndConditionObjects
|
||||||
|
{
|
||||||
|
param(
|
||||||
|
$Objects,
|
||||||
|
|
||||||
|
[switch]
|
||||||
|
$Selected
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Status "Import terms and conditions"
|
||||||
|
|
||||||
|
foreach($obj in $objects)
|
||||||
|
{
|
||||||
|
if($Selected -and $obj.Selected -ne $true) { continue }
|
||||||
|
|
||||||
|
Write-Log "Import Terms and Conditions: $($obj.Object.displayName)"
|
||||||
|
|
||||||
|
$assignments = Get-GraphAssignmentsObject $obj.Object ($obj.FileInfo.DirectoryName + "\" + $obj.FileInfo.BaseName + "_assignments.json")
|
||||||
|
|
||||||
|
$response = Import-TermsAndCondition $obj.Object
|
||||||
|
if($response)
|
||||||
|
{
|
||||||
|
$global:importedObjects++
|
||||||
|
Import-GraphAssignments2 $assignments "/deviceManagement/termsAndConditions/$($response.Id)/assignments"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$dgObjects.ItemsSource = @(Get-TermsAndConditionObjects)
|
||||||
|
Write-Status ""
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user