Version 0.5 of the third Intune tool for Window 10 in the WaaS Toolbox

So, it’s time. It’s with mixed feeling that I write (and publish) this post, but I feel that it’s necessary for me to let you have a look and gather your feedback. So, please, feel free to reach out to me with your comments, thoughts and advices. I’m publishing the code and my thoughts in a 0.5 version. I’m still waiting for some feedback from friends and colleagues, its not complete yet but I do think that you’ll get the point of the script and the logic behind it. Further down ill have a short to-do list with all the things I would like to include before considering this 1.0. However, feel free to try it (I do not recommend to try it in production – and if you do, its at your own risk).

So, here we go. The third tool is a tool that you run before starting your test-phase and compatibility-phase of every new Windows 10 release. The main point of it is to create a “randomized” (more on that later) group that could change between every release – even though many devices and persons probably still would be included every time. So why do this with a script?

  • The randomization, which some my not agree on, creates a better test group than if you would have the same group over and over again, or manually create one every time.
  • It saves time – obviously – to create test groups is (in my experience) time consuming.
  • It will keep the users happy – no one wants to be the poor user who always gets to try new things first.
  • It eases the process of Windows Servicing in your organization – just as with anything when it comes to automation, or processes in general, the more things/parts you can create a standardized step for – the better.
  • Lastly – its not bias. It cloud include the CEO, it could include that user that always gives you a hard time, it could exclude people that you never hear back from (and if you don’t hear anything its all good, right?)

So, currently the script consist of three parts:

  1. A bunch of functions from the Intune product group (available here) and Nickolaj Andersen, but also, something of (kind of) my own. I’ve modified one of the existing Powershell functions (Add-ApplicationAssignment) to be more inclusive and handle other assignments rather than group assignments. But it will also remove any group assignments where the Azure AD group has been deleted. I’ve split this into two functions/cmdlets of my own, where one is a part of this script (the other one is saved for a later blogpost). The included one will loop through all apps, prior to doing the selection of devices for the compatibility group, and remove “orphaned” assignments – while keeping all others (including exclusion). I’ve given it the rather boring name: “Remove-EmptyApplicationAssignments”. Ill, as with the other cmdlet, dig deeper into these in a follow up post.

    Born_To_Die_Group

    The function can be found below. I’ve done tweaks and added some parts, but the real work has been done by the Intune team.

    </span>
    
    Function Remove-EmptyApplicationAssignments(){
    
    
    
    [cmdletbinding()]
    
    $graphApiVersion = "Beta"
    
    try {
    
    $AllApps = (Get-IntuneApplication).id
    
    foreach ($ApplicationId in $AllApps) {
    
    $AssignedGroups = (Get-ApplicationAssignment -ApplicationId $ApplicationId).assignments
    
    if($AssignedGroups){
    
    $App_Count = @($AssignedGroups).count
    $i = 1
    
    # Creating header of JSON File
    $JSON = @"
    
    {
    "mobileAppAssignments": [
    
    "@
    
    # Looping through all existing assignments and adding them to the JSON object
    foreach($Assignment in $AssignedGroups){
    
    $ExistingTargetGroupId = $Assignment.target.GroupId
    $ExistingInstallIntent = $Assignment.intent
    # Finding out if the assignment is targeted to All User or All Devices and adding it to JSON object
    
    if(!$ExistingTargetGroupId){
    
    if ($Assignment.target.'@odata.type' -match '#microsoft.graph.allLicensedUsersAssignmentTarget') {
    
    $JSON += @"
    
    {
    "@odata.type": "#microsoft.graph.mobileAppAssignment",
    "target": {
    "@odata.type": "#microsoft.graph.allLicensedUsersAssignmentTarget"
    },
    "intent": "$ExistingInstallIntent"
    "@
    
    }
    
    elseif ($Assignment.target.'@odata.type' -match '#microsoft.graph.allDevicesAssignmentTarget') {
    
    
    $JSON += @"
    
    {
    "@odata.type": "#microsoft.graph.mobileAppAssignment",
    "target": {
    "@odata.type": "#microsoft.graph.allDevicesAssignmentTarget"
    },
    "intent": "$ExistingInstallIntent"
    "@
    
    
    }
    
    }
    
    # Testing the Azure AD group object ID to see if the group exist in Azure AD. If not, the foreach loop will exit without adding the assignment to JSON. Note, this will throw an error message created from Get-AADGroup, but will continue
    
    else{
    
    try {
    
    Get-AADGroup -id $ExistingTargetGroupId -ErrorAction SilentlyContinue | Out-Null
    
    }
    
    catch {
    
    'This group does not exist in Azure AD'
    }
    
    # Finding out if the assignment is an exclusion of an Azure AD Group and adding the assignment to JSON
    
    if($Assignment.target.'@odata.type' -match '#microsoft.graph.exclusionGroupAssignmentTarget'){
    
    $JSON += @"
    
    {
    "@odata.type": "#microsoft.graph.mobileAppAssignment",
    "target": {
    "@odata.type": "#microsoft.graph.exclusionGroupAssignmentTarget",
    "groupId": "$ExistingTargetGroupId"
    },
    "intent": "$ExistingInstallIntent"
    "@
    
    }
    
    # Adding the group assignment to JSON
    
    else{
    
    $JSON += @"
    
    {
    "@odata.type": "#microsoft.graph.mobileAppAssignment",
    "target": {
    "@odata.type": "#microsoft.graph.groupAssignmentTarget",
    "groupId": "$ExistingTargetGroupId"
    },
    "intent": "$ExistingInstallIntent"
    "@
    
    }
    
    }
    
    if($i -ne $App_Count){
    
    $JSON += @"
    
    },
    
    "@
    
    }
    
    else {
    
    $JSON += @"
    
    }
    
    "@
    
    }
    
    $i++
    
    }
    
    # Adding close of JSON object
    $JSON += @"
    
    ]
    }
    
    "@
    
    $Resource = "deviceAppManagement/mobileApps/$ApplicationId/assign"
    $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
    Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post -Body $JSON -ContentType "application/json"
    
    }
    
    }
    
    }
    
    # This will throw an error message if try fails - but not if the message is $null
    
    catch {
    
    $ex = $_.Exception
    if($ex) {
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Host "Response content:`n$responseBody" -f Red
    Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    write-host
    break
    
    }
    
    }
    
    }
    
    <span style="display:inline !important;float:none;background-color:transparent;color:#3d596d;cursor:text;font-family:'Noto Serif', Georgia, 'Times New Roman', Times, serif;font-size:16px;font-style:normal;font-variant:normal;font-weight:400;letter-spacing:normal;line-height:19.2px;orphans:2;text-align:left;text-decoration:none;text-indent:0;text-transform:none;white-space:normal;word-spacing:0;">

  2. The second part is the first part where things actually happens. The first part of the script focuses on hardware, and will use inventory data from Intune to get all existing hardware models. It will then choose one unique device and add that, its user and all of the users Windows devices to the application compatibility group. If you would like to know more about these groups, please read my blogpost on “The first tool in the toolbox”.
  3. The third part is probably the most complex one. This one looks at all applications that can be targeted to Windows devices (Apps, MSI & Office ProPlus deployments) and fetches all devices and users in each of the assigned groups. (Note! One of the things on the roadmap is to handle assignments of apps only to All Devices/All Users – which currently is ignored) It will then compare the already added users/devices in the compatibility group and thereafter choose two objects (users and/or devices) to add. It will then follow the same principle as with models. If a device is added, its user and that users other Windows devices will be added as well. If a user is added, all of that users Windows devices are added.

This is where the script ends currently. Ill first answer a question that I believe you are asking yourself – why all theses machines/users? There are many reasons for that, but my thinking goes like this:

Overall, the compatibility group should be about 10-15 % of your organization. We should aim to cover all: Models, Applications, Locations, Networks, Policies/OUs, which probably gets us to 10-15 % or close to it.

The reason for including all of a users devices is to ensure that we actually get a device with the application tested. Some devices may be old, some may not be used for a specific purpose etc. Also, this ensure a consistent user experience for the user. In the same way as my second tool (Self-Service deferral) deferred all devices when a user were added to the group, the same inclusion applies here.

Its also to do with reporting, ease of information, management and reporting. As Intune, by its own and currently (Power BI can help you a lot here) aren’t as extensive reporting-wise as ConfigMgr – I’ve choose to create groups that you would be able to report on and review easier. I’m however happy to get your feedback on this design decision.

I can keep going, but as this is a 0.5 post – ill leave you here with the to-do list and the script. I’m happy for all kinds of feedback and hope you either can use the script, or get inspiration from it and create something great on your own. The best thing about this particular script so far is that I get to learn a lot about Graph, GitHub and JSON – which will come in handy moving forward as well.

And the last note! Review the script closely – some variables are hardcoded atm and some are fetched from Azure Automation. As this is a work in progress, you may need to do some tweaks, add variables etc to make it work. The script will also throw an error message generated from the Get-AADGroup cmdlet when it finds a group that doesn’t exist. This is fine, but doesn’t look to good. I’m happy to assist if needed! Just reach out to me on Twitter or via comments.

AGAIN – DO NOT run this in production if you aren’t absolutely sure about what you are doing. I’m happy to help explaining, but I wont take any responsibility if it breaks something.

To-Do (before 1.0):

  • Complete the script with other variables (as much as possible at least): Location, Network, policies etc.
  • Support for all users/all devices in the app section.
  • Clean it up! Variables, indentation, work notes etc
  • Document it!
  • Extended error handling.

The code is of course also available on GitHub!

</span>

#Author: Simon Binder
#Blog: bindertech.se
#Twitter: @Bindertech
#Thanks to: @daltondhcp, @davefalkus, @NickolajA and the Intune product group.
#Most functions is copied from the Powershell Intune Samples: https://github.com/microsoftgraph/powershell-intune-samples
#Script requires the Azure AD Powershell module or the Azure AD Preview Powershell module to run

# Import required modules
try {
Import-Module -Name AzureAD -ErrorAction Stop
Import-Module -Name PSIntuneAuth -ErrorAction Stop
}
catch {
Write-Warning -Message "Failed to import modules"
}

function Get-AuthToken {



[cmdletbinding()]

param
(
[Parameter(Mandatory=$true)]
$User
)

$userUpn = New-Object "System.Net.Mail.MailAddress" -ArgumentList $User

$tenant = $userUpn.Host

Write-Host "Checking for AzureAD module..."

$AadModule = Get-Module -Name "AzureAD" -ListAvailable

if ($AadModule -eq $null) {

Write-Host "AzureAD PowerShell module not found, looking for AzureADPreview"
$AadModule = Get-Module -Name "AzureADPreview" -ListAvailable

}

if ($AadModule -eq $null) {
write-host
write-host "AzureAD Powershell module not installed..." -f Red
write-host "Install by running 'Install-Module AzureAD' or 'Install-Module AzureADPreview' from an elevated PowerShell prompt" -f Yellow
write-host "Script can't continue..." -f Red
write-host
exit
}

# Getting path to ActiveDirectory Assemblies
# If the module count is greater than 1 find the latest version

if($AadModule.count -gt 1){

$Latest_Version = ($AadModule | select version | Sort-Object)[-1]

$aadModule = $AadModule | ? { $_.version -eq $Latest_Version.version }

# Checking if there are multiple versions of the same module found

if($AadModule.count -gt 1){

$aadModule = $AadModule | select -Unique

}

$adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
$adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"

}

else {

$adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
$adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"

}

[System.Reflection.Assembly]::LoadFrom($adal) | Out-Null

[System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null

$clientId = "d1ddf0e4-d672-4dae-b554-9d5bdfd93547"

$redirectUri = "urn:ietf:wg:oauth:2.0:oob"

$resourceAppIdURI = "https://graph.microsoft.com"

$authority = "https://login.microsoftonline.com/$Tenant"

try {

$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority

# https://msdn.microsoft.com/en-us/library/azure/microsoft.identitymodel.clients.activedirectory.promptbehavior.aspx
# Change the prompt behaviour to force credentials each time: Auto, Always, Never, RefreshSession

$platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Auto"

$userId = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier" -ArgumentList ($User, "OptionalDisplayableId")

$authResult = $authContext.AcquireTokenAsync($resourceAppIdURI,$clientId,$redirectUri,$platformParameters,$userId).Result

# If the accesstoken is valid then create the authentication header

if($authResult.AccessToken){

# Creating header for Authorization token

$authHeader = @{
'Content-Type'='application/json'
'Authorization'="Bearer " + $authResult.AccessToken
'ExpiresOn'=$authResult.ExpiresOn
}

return $authHeader

}

else {

Write-Host
Write-Host "Authorization Access Token is null, please re-run authentication..." -ForegroundColor Red
Write-Host
break

}

}

catch {

write-host $_.Exception.Message -f Red
write-host $_.Exception.ItemName -f Red
write-host
break

}

}

####################################################

Function Get-AADDevice(){



[cmdletbinding()]

param
(
$DeviceID
)

# Defining Variables
$graphApiVersion = "v1.0"
$Resource = "devices"

try {

$uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)?`$filter=deviceId eq '$DeviceID'"

(Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).value

}

catch {

$ex = $_.Exception
$errorResponse = $ex.Response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($errorResponse)
$reader.BaseStream.Position = 0
$reader.DiscardBufferedData()
$responseBody = $reader.ReadToEnd();
Write-Host "Response content:`n$responseBody" -f Red
Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
write-host
break

}

}

####################################################

Function Get-ManagedDevices(){



[cmdletbinding()]

param
(
[switch]$IncludeEAS,
[switch]$ExcludeMDM
)

# Defining Variables
$graphApiVersion = "beta"
$Resource = "deviceManagement/managedDevices"

try {

$Count_Params = 0

if($IncludeEAS.IsPresent){ $Count_Params++ }
if($ExcludeMDM.IsPresent){ $Count_Params++ }

if($Count_Params -gt 1){

write-warning "Multiple parameters set, specify a single parameter -IncludeEAS, -ExcludeMDM or no parameter against the function"
Write-Host
break

}

elseif($IncludeEAS){

$uri = "https://graph.microsoft.com/$graphApiVersion/$Resource"

}

elseif($ExcludeMDM){

$uri = "https://graph.microsoft.com/$graphApiVersion/$Resource`?`$filter=managementAgent eq 'eas'"

}

else {

$uri = "https://graph.microsoft.com/$graphApiVersion/$Resource`?`$filter=managementAgent eq 'mdm' and managementAgent eq 'easmdm'"
Write-Warning "EAS Devices are excluded by default, please use -IncludeEAS if you want to include those devices"
Write-Host

}

(Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).Value

}

catch {

$ex = $_.Exception
$errorResponse = $ex.Response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($errorResponse)
$reader.BaseStream.Position = 0
$reader.DiscardBufferedData()
$responseBody = $reader.ReadToEnd();
Write-Host "Response content:`n$responseBody" -f Red
Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
write-host
break

}

}

####################################################

Function Get-ManagedDeviceUser(){



[cmdletbinding()]

param
(
[Parameter(Mandatory=$true,HelpMessage="DeviceID (guid) for the device on must be specified:")]
$DeviceID
)

# Defining Variables
$graphApiVersion = "beta"
$Resource = "deviceManagement/manageddevices('$DeviceID')?`$select=userId"

try {

$uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
Write-Verbose $uri
(Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).userId

}

catch {

$ex = $_.Exception
$errorResponse = $ex.Response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($errorResponse)
$reader.BaseStream.Position = 0
$reader.DiscardBufferedData()
$responseBody = $reader.ReadToEnd();
Write-Host "Response content:`n$responseBody" -f Red
Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
write-host
break

}

}

####################################################

Function Get-IntuneApplication(){



[cmdletbinding()]

$graphApiVersion = "Beta"
$Resource = "deviceAppManagement/mobileApps"

try {

$uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
(Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).Value | ? { (!($_.'@odata.type').Contains("managed")) }

}

catch {

$ex = $_.Exception
Write-Host "Request to $Uri failed with HTTP Status $([int]$ex.Response.StatusCode) $($ex.Response.StatusDescription)" -f Red
$errorResponse = $ex.Response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($errorResponse)
$reader.BaseStream.Position = 0
$reader.DiscardBufferedData()
$responseBody = $reader.ReadToEnd();
Write-Host "Response content:`n$responseBody" -f Red
Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
write-host
break

}

}

####################################################

Function Get-ApplicationAssignment(){



[cmdletbinding()]

param
(
$ApplicationId
)

$graphApiVersion = "Beta"
$Resource = "deviceAppManagement/mobileApps/$ApplicationId/?`$expand=categories,assignments"

try {

if(!$ApplicationId){

write-host "No Application Id specified, specify a valid Application Id" -f Red
break

}

else {

$uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
(Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get)

}

}

catch {

$ex = $_.Exception
$errorResponse = $ex.Response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($errorResponse)
$reader.BaseStream.Position = 0
$reader.DiscardBufferedData()
$responseBody = $reader.ReadToEnd();
Write-Host "Response content:`n$responseBody" -f Red
Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
write-host
break

}

}

####################################################

Function Remove-EmptyApplicationAssignments(){



[cmdletbinding()]

$graphApiVersion = "Beta"

try {

$AllApps = (Get-IntuneApplication).id

foreach ($ApplicationId in $AllApps) {

$AssignedGroups = (Get-ApplicationAssignment -ApplicationId $ApplicationId).assignments

if($AssignedGroups){

$App_Count = @($AssignedGroups).count
$i = 1

# Creating header of JSON File
$JSON = @"

{
"mobileAppAssignments": [

"@

# Looping through all existing assignments and adding them to the JSON object
foreach($Assignment in $AssignedGroups){

$ExistingTargetGroupId = $Assignment.target.GroupId
$ExistingInstallIntent = $Assignment.intent
# Finding out if the assignment is targeted to All User or All Devices and adding it to JSON object

if(!$ExistingTargetGroupId){

if ($Assignment.target.'@odata.type' -match '#microsoft.graph.allLicensedUsersAssignmentTarget') {

$JSON += @"

{
"@odata.type": "#microsoft.graph.mobileAppAssignment",
"target": {
"@odata.type": "#microsoft.graph.allLicensedUsersAssignmentTarget"
},
"intent": "$ExistingInstallIntent"
"@

}

elseif ($Assignment.target.'@odata.type' -match '#microsoft.graph.allDevicesAssignmentTarget') {


$JSON += @"

{
"@odata.type": "#microsoft.graph.mobileAppAssignment",
"target": {
"@odata.type": "#microsoft.graph.allDevicesAssignmentTarget"
},
"intent": "$ExistingInstallIntent"
"@


}

}

# Testing the Azure AD group object ID to see if the group exist in Azure AD. If not, the foreach loop will exit without adding the assignment to JSON. Note, this will throw an error message created from Get-AADGroup, but will continue

else{

try {

Get-AADGroup -id $ExistingTargetGroupId -ErrorAction SilentlyContinue | Out-Null

}

catch {

'This group does not exist in Azure AD'
}

# Finding out if the assignment is an exclusion of an Azure AD Group and adding the assignment to JSON

if($Assignment.target.'@odata.type' -match '#microsoft.graph.exclusionGroupAssignmentTarget'){

$JSON += @"

{
"@odata.type": "#microsoft.graph.mobileAppAssignment",
"target": {
"@odata.type": "#microsoft.graph.exclusionGroupAssignmentTarget",
"groupId": "$ExistingTargetGroupId"
},
"intent": "$ExistingInstallIntent"
"@

}

# Adding the group assignment to JSON

else{

$JSON += @"

{
"@odata.type": "#microsoft.graph.mobileAppAssignment",
"target": {
"@odata.type": "#microsoft.graph.groupAssignmentTarget",
"groupId": "$ExistingTargetGroupId"
},
"intent": "$ExistingInstallIntent"
"@

}

}

if($i -ne $App_Count){

$JSON += @"

},

"@

}

else {

$JSON += @"

}

"@

}

$i++

}

# Adding close of JSON object
$JSON += @"

]
}

"@

$Resource = "deviceAppManagement/mobileApps/$ApplicationId/assign"
$uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post -Body $JSON -ContentType "application/json"

}

}

}

# This will throw an error message if try fails - but not if the message is $null

catch {

$ex = $_.Exception
if($ex) {
$errorResponse = $ex.Response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($errorResponse)
$reader.BaseStream.Position = 0
$reader.DiscardBufferedData()
$responseBody = $reader.ReadToEnd();
Write-Host "Response content:`n$responseBody" -f Red
Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
write-host
break

}

}

}
####################################################

Function Get-AADGroup(){



[cmdletbinding()]

param
(
$GroupName,
$id,
[switch]$Members
)

# Defining Variables
$graphApiVersion = "v1.0"
$Group_resource = "groups"

try {

if($id){

$uri = "https://graph.microsoft.com/$graphApiVersion/$($Group_resource)?`$filter=id eq '$id'"
(Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).Value

}

elseif($GroupName -eq "" -or $GroupName -eq $null){

$uri = "https://graph.microsoft.com/$graphApiVersion/$($Group_resource)"
(Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).Value

}

else {

if(!$Members){

$uri = "https://graph.microsoft.com/$graphApiVersion/$($Group_resource)?`$filter=displayname eq '$GroupName'"
(Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).Value

}

elseif($Members){

$uri = "https://graph.microsoft.com/$graphApiVersion/$($Group_resource)?`$filter=displayname eq '$GroupName'"
$Group = (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).Value

if($Group){

$GID = $Group.id

$Group.displayName
write-host

$uri = "https://graph.microsoft.com/$graphApiVersion/$($Group_resource)/$GID/Members"
(Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).Value

}

}

}

}

catch {

$ex = $_.Exception
$errorResponse = $ex.Response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($errorResponse)
$reader.BaseStream.Position = 0
$reader.DiscardBufferedData()
$responseBody = $reader.ReadToEnd();
Write-Host "Response content:`n$responseBody" -f Red
Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
write-host
break

}

}

####################################################

# Read credentials and variables

$AADCredential = Get-AutomationPSCredential -Name "WaaSAccount"
$Credential = Get-AutomationPSCredential -Name "WaaSAccount"
$AppClientID = Get-AutomationVariable -Name "AppClientID"
$WindowsVersion = Get-AutomationVariable -Name "WindowsVersion"
$Tenantname = Get-AutomationVariable -Name "Tenantname"
$DefaultGroup = Get-AutomationVariable -Name "DefaultGroup"
$Groups = New-object Microsoft.Open.AzureAD.Model.GroupIdsForMembershipCheck
$TargetOSVersion = Get-AutomationVariable -name "TargetOSVersion"
#Replace with Automation Variables before production
$TechPilot = '758ba924-7961-4453-a3bf-da6040cea6d3'
$ApplicationPilot = '3141ba35-8f3d-41df-bd5c-4633c971af65'
$DeploymentRings = '836c8ef0-a604-4d5d-8d9b-ca92fedcd573','8f7b9464-4293-4929-92cd-a7cb28eeb8c3'
#Required for now, its the Windows 10 buildnumber without dots
$TargetOSVersion = 10017134165
# Acquire authentication token
try {
Write-Output -InputObject "Attempting to retrieve authentication token"
$AuthToken = Get-MSIntuneAuthToken -TenantName $Tenantname -ClientID $AppClientID -Credential $Credential
if ($AuthToken -ne $null) {
Write-Output -InputObject "Successfully retrieved authentication token"
}
}
catch {
Write-Warning -Message "Failed to retrieve authentication token"
}

Connect-AzureAD -Credential $AADCredential

#Getting all Windows Devices from Intune, removing already upgraded machines.

$AllWindowsDevices = Get-ManagedDevices | Where-Object -Property 'OperatingSystem' -EQ 'Windows'
$AllOldWindowsDevices = New-Object 'System.Collections.Generic.List[System.Object]'
foreach ($WindowsDevice in $AllWindowsDevices) {

$NewVersion = $WindowsDevice | Select-Object -ExpandProperty osVersion
$Newversion = $NewVersion -replace '\.',''
$WindowsDevice.osVersion="$NewVersion"

if ($WindowsDevice.osVersion -lt $TargetOSVersion) {

$AllOldWindowsDevices.Add($WindowsDevice)

}

}

#Populating the application pilot group.

#Getting all unique models, selecting machines at random. Finding the Intune-device owner and adding it and all of its devices to the Application Pilot group.

$AllModels = $AlloldWindowsDevices | Sort-Object -Property model -Unique | Select-Object Devicename, model, id, osVersion

foreach ($Model in $AllModels) {

$OwnerID = Get-ManagedDeviceUser -DeviceID $Model.id
$Owner = Get-AzureADUser -ObjectID $OwnerID
#Ensure to add device and user to group
try {
Add-AzureADGroupMember -ObjectId $ApplicationPilot -RefObjectId $Owner.ObjectId
}
catch {$DisplayName = $Owner.DisplayName

"$Displayname is already a member of this group"}

$MemberDevices = Get-AzureADUserOwnedDevice -ObjectId $OwnerID | Where-Object {$_.DeviceOSType -eq 'Windows'} | Select-Object -ExpandProperty ObjectID

foreach ($MemberDevice in $MemberDevices) {

try {
Add-AzureADGroupMember -ObjectId $Groups -RefObjectId $MemberDevice
}

catch {
"$MemberDevice is already a member of $Groups"

}

}

}
#Cleaning up all orphaned assignments prior to getting application groups.

Remove-EmptyApplicationAssignments

#Getting all Windows apps, LOB and O365 deployments, selecting machines at random. Finding the Intune-device owner and adding it and all of its devices to the Application Pilot group.

$Groups = $ApplicationPilot
$AllApps = Get-IntuneApplication
$AllWindowsApps = New-Object 'System.Collections.Generic.List[System.Object]'
foreach ($App in $AllApps) {

$StoreApp = $App | Where-Object -Property '@odata.type' -Match '#microsoft.graph.microsoftStoreForBusinessApp'

if ($StoreApp -ne $null) {

$AllWindowsApps.Add($StoreApp)

}

$MSI = $App | Where-Object -Property '@odata.type' -Match '#microsoft.graph.windowsMobileMSI'

if ($MSI -ne $null) {

$AllWindowsApps.Add($MSI)

}
$Office = $App | Where-Object -Property '@odata.type' -Match '#microsoft.graph.officeSuiteApp'

if ($Office -ne $null) {

$AllWindowsApps.Add($Office)

}

$Appx = $App | Where-Object -Property '@odata.type' -Match '#microsoft.graph.officeSuiteApp'

if ($Appx -ne $null) {

$AllWindowsApps.Add($Appx)

}

}

foreach ($WindowsApp in $AllWindowsApps) {

$Group = Get-ApplicationAssignment -ApplicationId $WindowsApp.id | Select-Object -ExpandProperty 'assignments' | Select-Object -ExpandProperty 'target'

if ($Group.'@odata.type' -eq $null) {"$Group This is not assigned to any group"}

elseif ($Group.'@odata.type' -match '#microsoft.graph.allLicensedUsersAssignmentTarget') {'This is assigned to all Users'}

elseif ($Group.'@odata.type' -match '#microsoft.graph.allDevicesAssignmentTarget') {'This is assigned to all Devices'}

elseif ($Group.'@odata.type' -match '#microsoft.graph.groupAssignmentTarget') {

try {
$AppGroupMembers = Get-AzureADGroupMember -ObjectId $group.groupId -All:$True | Group-Object 'ObjectType' -AsHashTable -AsString
}
Catch {"The group with ID $Group.groupid does no longer exist"}

if ($AppGroupMembers.user -ne $null) {

$AppGroupUserMembers = $AppGroupMembers
$CurrentMembers = Get-AzureADGroupMember -ObjectId $Groups -All:$True | Group-Object 'ObjectType' -AsHashTable -AsString

if ($CurrentMembers -ne $null) {

$Compared = Compare-Object -ReferenceObject $AppGroupUserMembers.user.objectid -DifferenceObject $CurrentMembers.user.objectid -IncludeEqual | Where-Object -Property 'SideIndicator' -Match '==' | Select-Object -ExpandProperty 'InputObject'

foreach ($Object in $Compared) {

if ($AppGroupUserMembers.user.objectid -contains $Object) {$AppGroupUserMembers = $AppGroupUserMembers.User.ObjectID -ne $Compared

}

$RandomMembers = $AppGroupUserMembers | Random -Count 2

foreach ($Member in $RandomMembers) {

$MemberDevices = Get-AzureADUserOwnedDevice -ObjectId $Member | Where-Object {$_.DeviceOSType -eq 'Windows'} | Select-Object -ExpandProperty ObjectID

foreach ($MemberDevice in $MemberDevices) {

try {

Add-AzureADGroupMember -ObjectId $Groups -RefObjectId $MemberDevice

}

catch {

"$MemberDevice is already a member of $Groups"

}
}

try {

Add-AzureADGroupMember -ObjectId $Groups -RefObjectId $Member

}

catch {

"$MemberDevice is already a member of $Groups"

}

}
}
}

}
if ($AppGroupMembers.Device -ne $null) {

$AppGroupDeviceMembers = $AppGroupMembers
$CurrentMembers = Get-AzureADGroupMember -ObjectId $Groups -All:$True | Group-Object 'ObjectType' -AsHashTable -AsString

if ($CurrentMembers -ne $null) {

$Compared = Compare-Object -ReferenceObject $AppGroupDeviceMembers.Device.objectid -DifferenceObject $CurrentMembers.Device.objectid -IncludeEqual | Where-Object -Property 'SideIndicator' -Match '==' | Select-Object -ExpandProperty 'InputObject'

foreach ($Object in $Compared) {

if ($AppGroupDeviceMembers.user.objectid -contains $Object) {$AppGroupDeviceMembers = $AppGroupDeviceMembers.User.ObjectID -ne $Compared

}
$RandomMembers = $AppGroupDeviceMembers | Random -Count 2

foreach ($Member in $RandomMembers) {

$MemberUsers = Get-ManagedDeviceUser -DeviceID $AppGroupDeviceMembers.Device.DeviceID
#May need to add code to handle multiple owners

foreach ($MemberUser in $MemberUsers) {

try {

Add-AzureADGroupMember -ObjectId $Groups -RefObjectId $MemberUser
}

catch {

"$MemberUser is already a member of $Groups"

}

}

try {

Add-AzureADGroupMember -ObjectId $Groups -RefObjectId $Member

}

catch {

"$Member is already a member of $Groups"

}

}
}
}

}

}

}

&nbsp;

<span style="display:inline !important;float:none;background-color:transparent;color:#3d596d;cursor:text;font-family:'Noto Serif', Georgia, 'Times New Roman', Times, serif;font-size:16px;font-style:normal;font-variant:normal;font-weight:400;letter-spacing:normal;line-height:19.2px;orphans:2;text-align:left;text-decoration:none;text-indent:0;text-transform:none;white-space:normal;word-spacing:0;">

 

 

As a Solution Architect, Simon inspires customers, partners and colleagues to create the best possible workplace for their users. His main focus is the Windows platform – but todays workplace consists of so much more than that. As an MCT he is passionate about teaching and sharing knowledge. He’s a frequent speaker, blogger and podcaster – as well as a penguin fanatic.

Tagged with: , , , , , , , , , , , , ,
Posted in Intune, Microsoft, WaaS Toolbox, Windows 10, Windows as a Service

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

I’m speaking!
I’m going!
Follow me on Twitter!
%d bloggers like this: