Removed support for AzureRM

Az module is required for Native Azure objects (Conditional access, Azure branding and MDM/MAM settings)
Fixed limit on Conditional Access objects
Remove properties before import (date etc.)
Added WIP policies
Added support for installing Intune module for user only
This commit is contained in:
Micke
2021-02-03 12:50:22 +11:00
parent bcc178ac99
commit b4c737dd6d
15 changed files with 340 additions and 140 deletions

View File

@@ -198,6 +198,16 @@ function Get-AppProtectionObjectType
{ {
"androidManagedAppProtections" "androidManagedAppProtections"
} }
elseif($odataType -like "*mdmWindowsInformationProtectionPolicy*")
{
# Win 10 - With enrollment e.g. Intune enrolled Win 10 devices
"mdmWindowsInformationProtectionPolicies"
}
elseif($odataType -like "*windowsInformationProtectionPolicy*")
{
# Win 10 - Without enrollment e.g. MAM polices for Win 10
"WindowsInformationProtectionPolicies"
}
} }
function Get-AppProtectionObjectForExport function Get-AppProtectionObjectForExport
@@ -257,10 +267,8 @@ function Import-AppProtection
if(($obj | GM -MemberType NoteProperty -Name "Apps")) if(($obj | GM -MemberType NoteProperty -Name "Apps"))
{ {
$apps = $obj.Apps $apps = $obj.Apps
# Remove apps properties
Remove-ObjectProperty $obj "apps"
Remove-ObjectProperty $obj "apps@odata.context"
} }
Start-PreImport $obj -RemoveProperties @("apps","apps@odata.context")
Write-Status "Import $($obj.displayName)" Write-Status "Import $($obj.displayName)"

View File

@@ -247,17 +247,7 @@ function Import-Application
{ {
param($obj) param($obj)
Remove-ObjectProperty $obj "uploadState" Start-PreImport $obj -RemoveProperties @("uploadState","publishingState","isAssigned","roleScopeTagIds","dependentAppCount","committedContentVersion","id","isFeatured","size")
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)" Write-Status "Import $($obj.displayName)"

View File

@@ -203,6 +203,8 @@ function Import-AutoPilot
Write-Status "Import $($obj.displayName)" Write-Status "Import $($obj.displayName)"
Start-PreImport $obj
Invoke-GraphRequest -Url "/deviceManagement/windowsAutopilotDeploymentProfiles" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST Invoke-GraphRequest -Url "/deviceManagement/windowsAutopilotDeploymentProfiles" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST
} }

View File

@@ -1,6 +1,82 @@
#Requires -module Az.Accounts
function Invoke-InitializeModule
{
if(-not $global:AzToken)
{
# Only allow re-logging if it failed the first time
$global:AuthenticatedToAzure = $false
}
#!!! - Used for testing login
#Disconnect-AzAccount -Username admin@delematelab2.onmicrosoft.com
}
function Connect-AzureNative
{
<#
.SYNOPSIS
Tries to connect to Azure with existing token
Uses Connect-AZAccount if no token found in cache
#>
param($user)
Write-Log "Authenticate to Azure (Az module). Try from cache with user $user"
$Context = (Get-AzContext -ListAvailable | Where { $_.Account.Id -eq $user } | select -first 1)
if (-not $Context)
{
$user | Clip # Copy login id to clipboard
# Run Connect-AZAccount in a separate runspace or it will hang
$Runspace = [runspacefactory]::CreateRunspace()
$PowerShell = [powershell]::Create()
$PowerShell.Runspace = $Runspace
$Runspace.Open()
$PowerShell.AddScript({Connect-AZAccount})
$PowerShell.Invoke()
[System.Windows.Forms.Application]::DoEvents()
$Context = (Get-AzContext -ListAvailable | Where { $_.Account.Id -eq $user } | select -first 1)
}
$global:AzToken = ""
try
{
$Resource = '74658136-14ec-4630-ad9b-26e160ff0fc6'
$global:AzToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id, $null, "Never", $null, $Resource)
}
catch
{
Write-LogError "Failed to authenticate with Instance.AuthenticationFactory.Authenticate" $_.Exception
}
if(-not $global:AzToken)
{
Write-Log "Failed to authenticate" 3
}
else
{
Write-Log "Authenticated as $($global:AzToken.UserId)"
}
$global:AuthenticatedToAzure = $true
Set-MainTitle
}
# Invoke-AzureNativeRequest is based on the following project
# https://github.com/JustinGrote/Az.PortalAPI/tree/master/Az.PortalAPI
# #
# Azure functions are based on: ??? # Some small changes:
# # - Get-AzContext is based on the same user as Intune user
# - Renamed Invoke-Request to Invoke-AzureNativeRequest
# - Added support for HTTP Method PATCH
# - Added support for paging with nextLink (Lazy solution...not fully tested but looks like it is working)
# - Removed Token parameter. Created the Connect-AzureNative to get token
# - Removed Context parameter
function Invoke-AzureNativeRequest { function Invoke-AzureNativeRequest {
<# <#
.SYNOPSIS .SYNOPSIS
@@ -19,7 +95,7 @@ function Invoke-AzureNativeRequest {
$Body, $Body,
#Specify the HTTP Method you wish to use. Defaults to GET #Specify the HTTP Method you wish to use. Defaults to GET
[ValidateSet("GET","POST","OPTIONS","DELETE", "PATCH", "PUT")] [ValidateSet("GET","POST","OPTIONS","DELETE","PATCH")]
$Method = "GET", $Method = "GET",
#The base URI for the Portal API. Typically you don't need to change this #The base URI for the Portal API. Typically you don't need to change this
@@ -29,101 +105,76 @@ function Invoke-AzureNativeRequest {
#The request ID for the session. You can generate one with [guid]::NewGuid().guid. #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 #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 $requestID = [guid]::NewGuid().guid,
[switch]$allowPaging
) )
if(-not $global:AzToken -and $global:AuthenticatedToAzure -eq $false)
{
Connect-AzureNative $global:me.userPrincipalName
}
if(-not $global:AzToken)
{
return
}
#Combine the BaseURI and Target #Combine the BaseURI and Target
[String]$ApiAction = $Target.TrimStart('/') [String]$ApiAction = $Target.TrimStart('/')
if ($Action) if ($Action) {
{
$ApiAction = $ApiAction + '/' + $Action $ApiAction = $ApiAction + '/' + $Action
} }
if($global:tokresponse -and [DateTimeOffset]::Now.ToUnixTimeSeconds() -gt $global:tokresponse.expires_on) $uriStr = "$baseURI$ApiAction"
if($allowPaging)
{ {
$global:tokresponse = $null $uri = [Uri]::New("$uriStr&nextLink=null")
}
else
{
$uri = [Uri]::New($baseURI,$ApiAction)
} }
$Context = Get-AzureRmContext if(-not $global:AzToken.AccessToken.tostring())
if(-not $context -or -not $global:tokresponse)
{ {
if($Context) Write-Log "No access token available" 3
{ return
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 = @{ $InvokeRestMethodParams = @{
Uri = [Uri]::New($baseURI,$ApiAction) Uri = $uri
Method = $Method Method = $Method
Header = [ordered]@{ Header = [ordered]@{
Authorization = 'Bearer ' + $global:tokresponse.access_token Authorization = 'Bearer ' + $global:AzToken.AccessToken.tostring()
'Content-Type' = 'application/json' 'Content-Type' = 'application/json'
'x-ms-client-request-id' = $requestID 'x-ms-client-request-id' = $requestID
'Host' = $baseURI.Host 'Host' = $baseURI.Host
'Origin' = $requestOrigin 'Origin' = 'https://iam.hosting.portal.azure.net'
} }
Body = $Body Body = $Body
} }
try $max = 100
$cur = 0
$retObj = Invoke-RestMethod @InvokeRestMethodParams
if(($retObj | GM -MemberType NoteProperty -Name "nextLink"))
{ {
Invoke-RestMethod @InvokeRestMethodParams while($retObj.nextLink)
if($? -eq $false)
{ {
throw $global:error[0] # Get more objects
} $InvokeRestMethodParams["Uri"] = [Uri]::New($uriStr + "&nextLink=" + $retObj.nextLink)
$retObj = Invoke-RestMethod @InvokeRestMethodParams
if($cur -ge $max) { break }
$cur++ # Loop gets stuck if nextLink=null is added to the command line so make sure it doesn't hang forever
}
} }
catch
{ $retObj
Write-LogError "Failed to invoke Invoke-RestMethod for Azure" $_.Exception
}
} }
function Get-AzureNativeObjects function Get-AzureNativeObjects
@@ -135,10 +186,11 @@ function Get-AzureNativeObjects
$property, $property,
[Array] [Array]
$exclude, $exclude,
$SortProperty = "") $SortProperty = "",
[switch]$allowPaging)
$objects = @() $objects = @()
$nativeObjects =Invoke-AzureNativeRequest $Target $nativeObjects = Invoke-AzureNativeRequest $Target -allowPaging:($allowPaging -eq $true)
if(($nativeObjects | GM -Name "items")) if(($nativeObjects | GM -Name "items"))
{ {

View File

@@ -188,7 +188,7 @@ function Import-IntuneBranding
{ {
param($obj) param($obj)
Remove-ObjectProperty $obj "@odata.context" Start-PreImport $obj -RemoveProperties @("@odata.context")
$newObject = @" $newObject = @"
{ {

View File

@@ -201,6 +201,8 @@ function Import-CompliancePolicy
{ {
param($obj) param($obj)
Start-PreImport $obj
$json = ConvertTo-Json $obj -Depth 5 $json = ConvertTo-Json $obj -Depth 5
$json = $json.Trim().TrimEnd('}').Trim() $json = $json.Trim().TrimEnd('}').Trim()
$json += @" $json += @"

View File

@@ -105,7 +105,7 @@ function Get-ConditionalAccess
function Get-ConditionalAccessObjects function Get-ConditionalAccessObjects
{ {
#https://main.iam.ad.ext.azure.com/api/Policies/Policies?top=10&nextLink=null&appId=&includeBaseline=true #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') Get-AzureNativeObjects "Policies/Policies?top=10&appId=&includeBaseline=true" -property @('policyName') -allowPaging
} }
function Get-ConditionalAccessObject function Get-ConditionalAccessObject
@@ -214,6 +214,8 @@ function Import-ConditionalAccess
{ {
param($obj) param($obj)
Start-PreImport $obj
$json = Update-JsonForEnvironment $json $json = Update-JsonForEnvironment $json
if($obj.baselineType -eq 0) if($obj.baselineType -eq 0)

View File

@@ -206,14 +206,10 @@ function Import-DeviceConfiguration
{ {
param($obj) param($obj)
if(($obj | GM -MemberType NoteProperty -Name "supportsScopeTags"))
{
# Remove read-only property
$obj.PSObject.Properties.Remove('supportsScopeTags')
}
Write-Status "Import $($obj.displayName)" Write-Status "Import $($obj.displayName)"
Start-PreImport $obj
Invoke-GraphRequest -Url "/deviceManagement/deviceConfigurations" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST Invoke-GraphRequest -Url "/deviceManagement/deviceConfigurations" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST
} }

View File

@@ -222,6 +222,8 @@ function Import-ESP
{ {
param($obj) param($obj)
Start-PreImport $obj
if($obj.id -like "*_default*") if($obj.id -like "*_default*")
{ {
Write-Status "Update $($obj.displayName)" Write-Status "Update $($obj.displayName)"

View File

@@ -261,6 +261,8 @@ function Import-GPOSetting
Write-Status "Import $($obj.displayName)" Write-Status "Import $($obj.displayName)"
Start-PreImport $obj
# Import Administrative Template profile # Import Administrative Template profile
$response = Invoke-GraphRequest -Url "/deviceManagement/groupPolicyConfigurations" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST $response = Invoke-GraphRequest -Url "/deviceManagement/groupPolicyConfigurations" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST
@@ -268,6 +270,8 @@ function Import-GPOSetting
{ {
foreach($setting in $settings) foreach($setting in $settings)
{ {
Start-PreImport $setting
# Import each setting for the Administrative Template profile # 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 $response2 = Invoke-GraphRequest -Url "/deviceManagement/groupPolicyConfigurations/$($response.id)/definitionValues" -Content (ConvertTo-Json $setting -Depth 5) -HttpMethod POST
} }

View File

@@ -3,33 +3,34 @@ function Invoke-InitializeModule
$module = Get-Module -Name Microsoft.Graph.Intune -ListAvailable $module = Get-Module -Name Microsoft.Graph.Intune -ListAvailable
if(-not $module) if(-not $module)
{ {
$ret = [System.Windows.MessageBox]::Show("Intune PowerShell module not found!`n`nDo you want to install it?`n`nYes = Install intune module (Requires admin or it will fail)`nNo = Contune without module (No Azure modules will be loaded)`nCancel = Quit", "Error", "YesNoCancel", "Error") $ret = [System.Windows.MessageBox]::Show("Intune PowerShell module not found!`n`nDo you want to install it as admin?`n`nYes = Install intune module as Admin (Requires admin or it will fail)`nNo = Install module for current user`nCancel = Quit", "Error", "YesNoCancel", "Error")
if($ret -eq "Yes") if($ret -eq "Cancel")
{
try
{
Install-Module -Name Microsoft.Graph.Intune -Force -ErrorAction SilentlyContinue
}
catch {}
if(-not (Get-Module -Name Microsoft.Graph.Intune -ListAvailable -Refresh))
{
[System.Windows.MessageBox]::Show("Failed to install Intune PowerShell module!`n`nRestart this as admin and try again`nor`nStart PowerShell as admin and run:`nInstall-Module -Name Microsoft.Graph.Intune", "Error", "OK", "Error")
exit
}
}
elseif($ret -eq "Cancel")
{ {
exit exit
} }
else
$params = @{}
if($ret -eq "No")
{ {
return $params.Add("Scope", "CurrentUser")
}
try
{
Install-Module -Name Microsoft.Graph.Intune -Force -ErrorAction SilentlyContinue @params
}
catch {}
if(-not (Get-Module -Name Microsoft.Graph.Intune -ListAvailable -Refresh))
{
[System.Windows.MessageBox]::Show("Failed to install Intune PowerShell module!`n`nRestart this as admin and try again`nor`nStart PowerShell as admin and run:`nInstall-Module -Name Microsoft.Graph.Intune", "Error", "OK", "Error")
exit
} }
} }
if(-not $global:authentication) if(-not $global:authentication)
{ {
if((Get-Command Connect-MSGraph)) if((Get-Command Connect-MSGraph -ErrorAction SilentlyContinue))
{ {
$global:authentication = Connect-MSGraph -PassThru $global:authentication = Connect-MSGraph -PassThru
} }
@@ -41,6 +42,7 @@ function Invoke-InitializeModule
return return
} }
Write-Log "Get current user"
$global:Me = Invoke-GraphRequest "ME" $global:Me = Invoke-GraphRequest "ME"
if(-not $global:Me) if(-not $global:Me)
@@ -48,6 +50,8 @@ function Invoke-InitializeModule
[System.Windows.MessageBox]::Show("Failed to get information about current logged on Azure user!`n`nVerify connection and try again`n`nNo Intune modules will be imported!", "Error", "OK", "Error") [System.Windows.MessageBox]::Show("Failed to get information about current logged on Azure user!`n`nVerify connection and try again`n`nNo Intune modules will be imported!", "Error", "OK", "Error")
return return
} }
Write-Log "Get organization info"
$global:Organization = (Invoke-GraphRequest "Organization").Value $global:Organization = (Invoke-GraphRequest "Organization").Value
$global:graphURL = "https://graph.microsoft.com/beta" $global:graphURL = "https://graph.microsoft.com/beta"
@@ -59,6 +63,8 @@ function Invoke-InitializeModule
Values = @() Values = @()
}) })
Write-Log "Add settings and menu items"
Add-SettingsObject (New-Object PSObject -Property @{ Add-SettingsObject (New-Object PSObject -Property @{
Title = "Root folder" Title = "Root folder"
Key = "IntuneRootFolder" Key = "IntuneRootFolder"
@@ -76,6 +82,7 @@ function Invoke-InitializeModule
Key = "AddObjectType" Key = "AddObjectType"
Type = "Boolean" Type = "Boolean"
DefaultValue = $true DefaultValue = $true
Description = "Default setting for adding object type to the export folder"
}) "IntuneAzure" }) "IntuneAzure"
Add-SettingsObject (New-Object PSObject -Property @{ Add-SettingsObject (New-Object PSObject -Property @{
@@ -83,6 +90,7 @@ function Invoke-InitializeModule
Key = "AddCompanyName" Key = "AddCompanyName"
Type = "Boolean" Type = "Boolean"
DefaultValue = $true DefaultValue = $true
Description = "Default setting for adding company name to the export folder"
}) "IntuneAzure" }) "IntuneAzure"
Add-SettingsObject (New-Object PSObject -Property @{ Add-SettingsObject (New-Object PSObject -Property @{
@@ -90,6 +98,7 @@ function Invoke-InitializeModule
Key = "ExportIntuneAssignments" Key = "ExportIntuneAssignments"
Type = "Boolean" Type = "Boolean"
DefaultValue = $true DefaultValue = $true
Description = "Default setting for exporting assignments"
}) "IntuneAzure" }) "IntuneAzure"
Add-SettingsObject (New-Object PSObject -Property @{ Add-SettingsObject (New-Object PSObject -Property @{
@@ -97,6 +106,7 @@ function Invoke-InitializeModule
Key = "CreateIntuneGroupOnImport" Key = "CreateIntuneGroupOnImport"
Type = "Boolean" Type = "Boolean"
DefaultValue = $true DefaultValue = $true
Description = "Default setting for creating groups during import"
}) "IntuneAzure" }) "IntuneAzure"
Add-SettingsObject (New-Object PSObject -Property @{ Add-SettingsObject (New-Object PSObject -Property @{
@@ -104,6 +114,7 @@ function Invoke-InitializeModule
Key = "ConvertIntuneSyncedGroupOnImport" Key = "ConvertIntuneSyncedGroupOnImport"
Type = "Boolean" Type = "Boolean"
DefaultValue = $true DefaultValue = $true
Description = "Convert AD synched groups to Azure AD group during import if the group does not exist"
}) "IntuneAzure" }) "IntuneAzure"
Add-SettingsObject (New-Object PSObject -Property @{ Add-SettingsObject (New-Object PSObject -Property @{
@@ -178,7 +189,6 @@ $xmlStr = @"
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="This will export all objects to a sub-directory of the export path with name based on object type" /> <Rectangle Style="{DynamicResource InfoIcon}" ToolTip="This will export all objects to a sub-directory of the export path with name based on object type" />
</StackPanel> </StackPanel>
<CheckBox Grid.Column='1' Grid.Row='1' Name='chkAddObjectType' VerticalAlignment="Center" IsEnabled="false" IsChecked="true" /> <CheckBox Grid.Column='1' Grid.Row='1' Name='chkAddObjectType' VerticalAlignment="Center" IsEnabled="false" IsChecked="true" />
<StackPanel Orientation="Horizontal" Grid.Row='2' Margin="0,0,5,0"> <StackPanel Orientation="Horizontal" Grid.Row='2' Margin="0,0,5,0">
<Label Content="Add company name to path" /> <Label Content="Add company name to path" />
@@ -1575,4 +1585,30 @@ function Get-GraphAssignmentsObject
} }
$tmpAssignments $tmpAssignments
} }
#region Policy Functions
function Start-PreImport
{
param($obj, $removeProperties = @())
foreach($removeProp in @('lastModifiedDateTime','createdDateTime','supportsScopeTags','id'))
{
if($removeProperties -notcontains $removeProp)
{
$removeProperties += $removeProp
}
}
foreach($prop in $removeProperties)
{
if(($obj | GM -MemberType NoteProperty -Name $prop))
{
Write-Log "Remove property $prop"
$obj.PSObject.Properties.Remove($prop)
}
}
}
#endregion

View File

@@ -243,11 +243,7 @@ function Import-PowerShellScript
{ {
param($obj) param($obj)
Remove-ObjectProperty $obj "id" Start-PreImport $obj -RemoveProperties @("id","assignments@odata.context","assignments")
Remove-ObjectProperty $obj "createdDateTime"
Remove-ObjectProperty $obj "lastModifiedDateTime"
Remove-ObjectProperty $obj "assignments@odata.context"
Remove-ObjectProperty $obj "assignments"
Write-Status "Import $($obj.displayName)" Write-Status "Import $($obj.displayName)"

View File

@@ -210,6 +210,8 @@ function Import-TermsAndCondition
Write-Status "Import $($obj.displayName)" Write-Status "Import $($obj.displayName)"
Start-PreImport $obj
Invoke-GraphRequest -Url "/deviceManagement/termsAndConditions" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST Invoke-GraphRequest -Url "/deviceManagement/termsAndConditions" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST
} }

View File

@@ -97,7 +97,7 @@ function global:Write-LogError
{ {
param($Text, $Exception) param($Text, $Exception)
if($Text) if($Text -and $Exception.message)
{ {
$Text += " Exception: $($Exception.message)" $Text += " Exception: $($Exception.message)"
} }
@@ -532,6 +532,12 @@ function global:Add-SettingValue
$settingObj = Add-SettingTextBox $id $value $settingObj = Add-SettingTextBox $id $value
} }
$descriptionInfo = ""
if($settingValue.Description)
{
$descriptionInfo = "<Rectangle Style=`"{DynamicResource InfoIcon}`" ToolTip=`"$($settingValue.Description)`" Margin=`"5,0,0,0`" />"
}
$xaml = @" $xaml = @"
<Border Margin="0,5,0,0" $wpfNS> <Border Margin="0,5,0,0" $wpfNS>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" > <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
@@ -544,8 +550,13 @@ function global:Add-SettingValue
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Margin="5,0,5,0">
<TextBlock Text="$($settingValue.Title)" VerticalAlignment="Center"/>
$descriptionInfo
</StackPanel>
<!--
<TextBlock Text="$($settingValue.Title)" VerticalAlignment="Center" Margin="5,0,0,0" /> <TextBlock Text="$($settingValue.Title)" VerticalAlignment="Center" Margin="5,0,0,0" />
-->
<Border Grid.Column="2" Name="border_$($id)" /> <Border Grid.Column="2" Name="border_$($id)" />
</Grid> </Grid>
@@ -873,15 +884,23 @@ function global:Add-MenuItem
function global:Invoke-ModuleFunction function global:Invoke-ModuleFunction
{ {
param($funtion) param($function)
Write-Log "Trigger function $function"
foreach($module in $global:loadedModules) foreach($module in $global:loadedModules)
{ {
# Get command with ExportedFunctions instead of Get-Command # Get command with ExportedFunctions instead of Get-Command
$cmd = $module.ExportedFunctions[$funtion] $cmd = $module.ExportedFunctions[$function]
if($cmd) if($cmd)
{ {
Write-Log "Trigger $function in $($module.Name)"
Invoke-Command -ScriptBlock $cmd.ScriptBlock Invoke-Command -ScriptBlock $cmd.ScriptBlock
} }
else
{
#Write-Log "$function not found in $($module.Name)" 2
}
} }
} }
@@ -905,6 +924,12 @@ function global:Initialize-Menu
Script = [ScriptBlock]{ Show-AboutDialog } Script = [ScriptBlock]{ Show-AboutDialog }
}) })
Add-MenuItem (New-Object PSObject -Property @{
Title = 'Reload'
MenuID = "General"
ShowForm = $false
Script = [ScriptBlock]{ Start-Reload }
})
Add-MenuItem (New-Object PSObject -Property @{ Add-MenuItem (New-Object PSObject -Property @{
Title = 'Exit' Title = 'Exit'
@@ -956,6 +981,38 @@ public static extern IntPtr GetConsoleWindow();
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow); public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
' '
function global:Set-MainTitle
{
if(-not $global:window) { return }
Write-Log "Set main title"
$mainTitle = $title
try
{
if($global:Me.userPrincipalName)
{
$IntuneId = $global:Me.userPrincipalName
$mainTitle += " - IntuneGraph: $($global:Me.userPrincipalName)"
}
}
catch {}
try
{
$ctx = Get-AzContext -ErrorAction SilentlyContinue
if($ctx.Account.Id)
{
$azureADId = $ctx.Account.Id
$mainTitle += " - AzureAD: $($ctx.Account.Id)"
}
}
catch {}
$global:window.Title = $mainTitle
}
function Show-Console function Show-Console
{ {
$consolePtr = [Console.Window]::GetConsoleWindow() $consolePtr = [Console.Window]::GetConsoleWindow()
@@ -984,6 +1041,68 @@ function Hide-Console
[Console.Window]::ShowWindow($consolePtr, 0) [Console.Window]::ShowWindow($consolePtr, 0)
} }
#endregion
#region Module functions
function Import-AllModules
{
foreach($file in (Get-Item -path "$modulesPath\*.psm1"))
{
$module = Import-Module $file -PassThru -Force -ErrorAction SilentlyContinue
if($module)
{
$global:loadedModules += $module
Write-Host "Module $($module.Name) loaded successfully"
}
else
{
Write-Warning "Failed to load module $file"
}
}
}
function Start-Reload
{
if([System.Windows.MessageBox]::Show("Are you sure you want to reload all modules and settings?", "Exit?", "YesNo", "Question") -eq "No")
{
return
}
Write-Status "Reloading modules"
$global:menuObjects = @()
$tmpList = @()
$spMenu.Children.Clear()
foreach($tmpModule in $global:loadedModules)
{
Remove-Module $tmpModule
$module = Import-Module $tmpModule.Path -PassThru -Force -ErrorAction SilentlyContinue
if($module)
{
$tmpList += $module
Write-Host "Module $($module.Name) loaded successfully"
}
else
{
Write-Warning "Failed to load module $file"
}
}
$global:loadedModules = $tmpList
Add-DefaultSettings
Invoke-ModuleFunction "Invoke-InitializeModule"
Initialize-Menu
Write-Status ""
}
#endregion #endregion
##################################################################################################### #####################################################################################################
@@ -1113,25 +1232,14 @@ $global:menuObjects = @()
# Load all modules in the Modules folder # Load all modules in the Modules folder
$modulesPath = [IO.Path]::GetDirectoryName($PSCommandPath) + "\Extensions" $modulesPath = [IO.Path]::GetDirectoryName($PSCommandPath) + "\Extensions"
if(Test-Path $modulesPath) if(Test-Path $modulesPath)
{ {
foreach($file in (Get-Item -path "$modulesPath\*.psm1")) Import-AllModules
{
$module = Import-Module $file -PassThru -Force -ErrorAction SilentlyContinue
if($module)
{
$global:loadedModules += $module
Write-Host "Module $($module.Name) loaded successfully"
}
else
{
Write-Warning "Failed to load module $file"
}
}
} }
else else
{ {
Write-Warning "Modules folder $modulesPath not wound. Aborting..." 3 Write-Warning "Modules folder $modulesPath not found. Aborting..." 3
exit 1 exit 1
} }
@@ -1149,6 +1257,8 @@ if($ShowConsoleWindow -ne $true)
Hide-Console Hide-Console
} }
Set-MainTitle
# Show main window # Show main window
# Workaround for ISE crash # Workaround for ISE crash
# https://gist.github.com/altrive/6227237 # https://gist.github.com/altrive/6227237

View File

@@ -57,8 +57,6 @@ The script will create a group if it is missing in the destination environment.
Download [Microsoft.WindowsAPICodePack](https://www.nuget.org/packages/WindowsAPICodePack-Core) and [Microsoft.WindowsAPICodePack.Shell](https://www.nuget.org/packages/WindowsAPICodePack-Shell) and copy the DLLs into the script folder to get a nicer folder dialog. Download [Microsoft.WindowsAPICodePack](https://www.nuget.org/packages/WindowsAPICodePack-Core) and [Microsoft.WindowsAPICodePack.Shell](https://www.nuget.org/packages/WindowsAPICodePack-Shell) and copy the DLLs into the script folder to get a nicer folder dialog.
If you want to test co-management and/or sync with on-prem AD, you can download [DeleMate](http://delemate.com). This tool can install anything from an AD-only environment to a complete SCCM environment in a quick and easy way. If you have a public domain registered, you can then install the AD domain with that name and add that domain to a test Azure environment and then configure AAD Sync and co-managment.
## License ## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details