Skip to content

Commit

Permalink
Removed support for AzureRM
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Micke-K committed Feb 3, 2021
1 parent bcc178a commit b4c737d
Show file tree
Hide file tree
Showing 15 changed files with 337 additions and 137 deletions.
14 changes: 11 additions & 3 deletions Extensions/AppProtection.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,16 @@ function Get-AppProtectionObjectType
{
"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
Expand Down Expand Up @@ -257,10 +267,8 @@ function Import-AppProtection
if(($obj | GM -MemberType NoteProperty -Name "Apps"))
{
$apps = $obj.Apps
# Remove apps properties
Remove-ObjectProperty $obj "apps"
Remove-ObjectProperty $obj "[email protected]"
}
Start-PreImport $obj -RemoveProperties @("apps","[email protected]")

Write-Status "Import $($obj.displayName)"

Expand Down
12 changes: 1 addition & 11 deletions Extensions/Apps.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -247,17 +247,7 @@ 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"
Start-PreImport $obj -RemoveProperties @("uploadState","publishingState","isAssigned","roleScopeTagIds","dependentAppCount","committedContentVersion","id","isFeatured","size")

Write-Status "Import $($obj.displayName)"

Expand Down
2 changes: 2 additions & 0 deletions Extensions/AutoPilot.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ function Import-AutoPilot

Write-Status "Import $($obj.displayName)"

Start-PreImport $obj

Invoke-GraphRequest -Url "/deviceManagement/windowsAutopilotDeploymentProfiles" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST
}

Expand Down
192 changes: 122 additions & 70 deletions Extensions/AzureNative.psm1
Original file line number Diff line number Diff line change
@@ -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 [email protected]
}

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 {
<#
.SYNOPSIS
Expand All @@ -19,7 +95,7 @@ function Invoke-AzureNativeRequest {
$Body,

#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",

#The base URI for the Portal API. Typically you don't need to change this
Expand All @@ -29,101 +105,76 @@ function Invoke-AzureNativeRequest {

#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
$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
[String]$ApiAction = $Target.TrimStart('/')

if ($Action)
{
if ($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")
}

$Context = Get-AzureRmContext

if(-not $context -or -not $global:tokresponse)
else
{
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 }
}
$uri = [Uri]::New($baseURI,$ApiAction)
}

#
if(-not $global:tokresponse)
if(-not $global:AzToken.AccessToken.tostring())
{
$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 }
Write-Log "No access token available" 3
return
}

$InvokeRestMethodParams = @{
Uri = [Uri]::New($baseURI,$ApiAction)
Uri = $uri
Method = $Method
Header = [ordered]@{
Authorization = 'Bearer ' + $global:tokresponse.access_token
Authorization = 'Bearer ' + $global:AzToken.AccessToken.tostring()
'Content-Type' = 'application/json'
'x-ms-client-request-id' = $requestID
'Host' = $baseURI.Host
'Origin' = $requestOrigin
'Origin' = 'https://iam.hosting.portal.azure.net'
}
Body = $Body
}

try
$max = 100
$cur = 0

$retObj = Invoke-RestMethod @InvokeRestMethodParams
if(($retObj | GM -MemberType NoteProperty -Name "nextLink"))
{
Invoke-RestMethod @InvokeRestMethodParams
if($? -eq $false)
while($retObj.nextLink)
{
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
{
Write-LogError "Failed to invoke Invoke-RestMethod for Azure" $_.Exception
}

$retObj
}

function Get-AzureNativeObjects
Expand All @@ -135,10 +186,11 @@ function Get-AzureNativeObjects
$property,
[Array]
$exclude,
$SortProperty = "")
$SortProperty = "",
[switch]$allowPaging)

$objects = @()
$nativeObjects =Invoke-AzureNativeRequest $Target
$nativeObjects = Invoke-AzureNativeRequest $Target -allowPaging:($allowPaging -eq $true)

if(($nativeObjects | GM -Name "items"))
{
Expand Down
2 changes: 1 addition & 1 deletion Extensions/Branding.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ function Import-IntuneBranding
{
param($obj)

Remove-ObjectProperty $obj "@odata.context"
Start-PreImport $obj -RemoveProperties @("@odata.context")

$newObject = @"
{
Expand Down
2 changes: 2 additions & 0 deletions Extensions/CompliancePolicies.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ function Import-CompliancePolicy
{
param($obj)

Start-PreImport $obj

$json = ConvertTo-Json $obj -Depth 5
$json = $json.Trim().TrimEnd('}').Trim()
$json += @"
Expand Down
4 changes: 3 additions & 1 deletion Extensions/ConditionalAccess.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ function Get-ConditionalAccess
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')
Get-AzureNativeObjects "Policies/Policies?top=10&appId=&includeBaseline=true" -property @('policyName') -allowPaging
}

function Get-ConditionalAccessObject
Expand Down Expand Up @@ -214,6 +214,8 @@ function Import-ConditionalAccess
{
param($obj)

Start-PreImport $obj

$json = Update-JsonForEnvironment $json

if($obj.baselineType -eq 0)
Expand Down
8 changes: 2 additions & 6 deletions Extensions/ConfigurationItems.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -206,14 +206,10 @@ 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)"

Start-PreImport $obj

Invoke-GraphRequest -Url "/deviceManagement/deviceConfigurations" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST
}

Expand Down
2 changes: 2 additions & 0 deletions Extensions/EnrollmentStatusPage.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ function Import-ESP
{
param($obj)

Start-PreImport $obj

if($obj.id -like "*_default*")
{
Write-Status "Update $($obj.displayName)"
Expand Down
4 changes: 4 additions & 0 deletions Extensions/GroupPolicy.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -261,13 +261,17 @@ function Import-GPOSetting

Write-Status "Import $($obj.displayName)"

Start-PreImport $obj

# Import Administrative Template profile
$response = Invoke-GraphRequest -Url "/deviceManagement/groupPolicyConfigurations" -Content (ConvertTo-Json $obj -Depth 5) -HttpMethod POST

if($response)
{
foreach($setting in $settings)
{
Start-PreImport $setting

# 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
}
Expand Down
Loading

0 comments on commit b4c737d

Please sign in to comment.