create-w365-groups.ps1
<#PSScriptInfo
.VERSION 1.0.1 .GUID 90ccb2c9-a75c-4d6b-a92a-a12866166b84 .AUTHOR AndrewTaylor .DESCRIPTION Creates Windows 365 for all purchased SKUs and then nests them within master groups .COMPANYNAME .COPYRIGHT GPL .TAGS intune endpoint MEM environment .LICENSEURI https://github.com/andrew-s-taylor/public/blob/main/LICENSE .PROJECTURI https://github.com/andrew-s-taylor/public .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES #> <# .SYNOPSIS Creates Windows 365 groups for each SKU .DESCRIPTION Creates Windows 365 for all purchased SKUs and then nests them within master groups .INPUTS None .OUTPUTS Creates a log file in %Temp% .NOTES Version: 1.0.1 Author: Andrew Taylor WWW: andrewstaylor.com Creation Date: 25/07/2022 Purpose/Change: Initial script development .EXAMPLE N/A #> ################################################################################################################################## ################# INITIALIZATION ################# ################################################################################################################################## $ErrorActionPreference = "Continue" ##Start Logging to %TEMP%\intune.log $date = get-date -format yyyyMMddTHHmmssffff Start-Transcript -Path $env:TEMP\intune-$date.log #Install MS Graph if not available Write-Host "Installing Microsoft Graph modules if required (current user scope)" #Install MS Graph if not available #Install MS Graph if not available if (Get-Module -ListAvailable -Name Microsoft.Graph.Authentication) { Write-Host "Microsoft Graph Authentication Already Installed" } else { Install-Module -Name Microsoft.Graph.Authentication -Scope CurrentUser -Repository PSGallery -Force -RequiredVersion 1.19.0 Write-Host "Microsoft Graph Authentication Installed" } if (Get-Module -ListAvailable -Name Microsoft.Graph.Groups) { Write-Host "Microsoft Graph Groups Already Installed " } else { Install-Module -Name Microsoft.Graph.Groups -Scope CurrentUser -Repository PSGallery -Force -RequiredVersion 1.19.0 Write-Host "Microsoft Graph Groups Installed" } # Load the Graph module Import-Module microsoft.graph.authentication import-module microsoft.Graph.Groups Function Connect-ToGraph { <# .SYNOPSIS Authenticates to the Graph API via the Microsoft.Graph.Authentication module. .DESCRIPTION The Connect-ToGraph cmdlet is a wrapper cmdlet that helps authenticate to the Intune Graph API using the Microsoft.Graph.Authentication module. It leverages an Azure AD app ID and app secret for authentication or user-based auth. .PARAMETER Tenant Specifies the tenant (e.g. contoso.onmicrosoft.com) to which to authenticate. .PARAMETER AppId Specifies the Azure AD app ID (GUID) for the application that will be used to authenticate. .PARAMETER AppSecret Specifies the Azure AD app secret corresponding to the app ID that will be used to authenticate. .PARAMETER Scopes Specifies the user scopes for interactive authentication. .EXAMPLE Connect-ToGraph -TenantId $tenantID -AppId $app -AppSecret $secret -#> [cmdletbinding()] param ( [Parameter(Mandatory = $false)] [string]$Tenant, [Parameter(Mandatory = $false)] [string]$AppId, [Parameter(Mandatory = $false)] [string]$AppSecret, [Parameter(Mandatory = $false)] [string]$scopes ) Process { Import-Module Microsoft.Graph.Authentication $version = (get-module microsoft.graph.authentication | Select-Object -expandproperty Version).major if ($AppId -ne "") { $body = @{ grant_type = "client_credentials"; client_id = $AppId; client_secret = $AppSecret; scope = "https://graph.microsoft.com/.default"; } $response = Invoke-RestMethod -Method Post -Uri https://login.microsoftonline.com/$Tenant/oauth2/v2.0/token -Body $body $accessToken = $response.access_token $accessToken if ($version -eq 2) { write-host "Version 2 module detected" $accesstokenfinal = ConvertTo-SecureString -String $accessToken -AsPlainText -Force } else { write-host "Version 1 Module Detected" Select-MgProfile -Name Beta $accesstokenfinal = $accessToken } $graph = Connect-MgGraph -AccessToken $accesstokenfinal Write-Host "Connected to Intune tenant $TenantId using app-based authentication (Azure AD authentication not supported)" } else { if ($version -eq 2) { write-host "Version 2 module detected" } else { write-host "Version 1 Module Detected" Select-MgProfile -Name Beta } $graph = Connect-MgGraph -scopes $scopes Write-Host "Connected to Intune tenant $($graph.TenantId)" } } } ############################################################ ############################################################ ############# CHANGE THIS TO USE IN AUTOMATION ############# ############################################################ ############################################################ $automated = "no" ############################################################ ############################################################ ############# AUTOMATION NOTES ############# ############################################################ ## You need to add these modules to your Automation Account if using Azure Automation ## Don't use the V2 preview versions ## https://www.powershellgallery.com/packages/Microsoft.Graph.Authentication/1.19.0 ## https://www.powershellgallery.com/packages/Microsoft.Graph.Groups/1.19.0 if ($automated -eq "yes") { ################################################################################################################################## ################# VARIABLES ################# ################################################################################################################################## $clientid = "YOUR_AAD_REG_ID" $clientsecret = "YOUR_CLIENT_SECRET" $sourcetenant = "TENANT_ID" ################################################################################################################################## ################# END VARIABLES ################# ################################################################################################################################## } ############################################################################################################### ###### MS Graph Implementations ###### ############################################################################################################### if ($automated -eq "yes") { Connect-ToGraph -Tenant $sourcetenant -AppId $clientid -AppSecret $clientsecret write-host "Graph Connection Established" } else { ##Connect to Graph Connect-ToGraph -Scopes Domain.Read.All, Domain.ReadWrite.All, Directory.Read.All openid, profile, email, offline_access, Group.ReadWrite.All } ############################################################################################################### ###### ENGAGE ###### ############################################################################################################### ##Create AAD Group write-host "Creating Azure AD Groups" ##Create W365 Groups for W365 users, manually assigned ##Check if group exists first $w365users = Get-MgGroup -Filter "DisplayName eq 'W365-Users'" if ($null -eq $w365users) { write-host "Creating W365 Users Group" $w365users = New-MGGroup -DisplayName "W365-Users" -Description "Windows 365 Users" -MailEnabled:$False -MailNickName "W365Users" -SecurityEnabled -IsAssignableToRole:$false write-host "W365 Users Group Created" } else { write-host "W365 Users Group Already Exists" } ##Check if device group exists $w365devices = Get-MgGroup -Filter "DisplayName eq 'W365 Devices'" if ($null -eq $w365devices) { ##Create Devices Group with dynamic membership based on Cloud PC model type write-host "Creating W365 Devices Group - Dynamically Assigned" $w365devices = New-MGGroup -DisplayName "W365 Devices" -Description "Dynamic group for all Windows 365 Single User devices" -MailEnabled:$False -MailNickName "w365devices" -SecurityEnabled -GroupTypes "DynamicMembership" -MembershipRule "(device.deviceModel -startsWith ""Cloud"")" -MembershipRuleProcessingState "On" -IsAssignableToRole:$false write-host "W365 Devices Group Created" } else { write-host "W365 Devices Group Already Exists" } ## Get the Group ID for our Win365 devices $groupid = $w365users.Id ##Grab all SKUs and create groups accordingly ##Assign Licenses to Group write-host "Creating groups and assigning licenses" ##Get Assigned SKUs ##Get All skus in the tenant write-host "Getting SKUs" $sku2 = ((Invoke-MgGraphRequest -uri "https://graph.microsoft.com/v1.0/subscribedSkus" -method get -OutputType PSObject).value) ##Loop through looking for W365 enterprise non-frontline SKUs (currently start with either CPC_E) $skuids = @() foreach ($sku in $sku2) { $part = $sku.skuPartNumber if ($part -like "*CPC_E*") { $skuidsobject = [pscustomobject]@{ sid = $sku.skuid part = $part } $skuids += $skuidsobject write-host "SKU Found - $part" } } $i = 1 foreach ($skuiditem in $skuids) { $skuid = $skuiditem.sid $skupart = $skuiditem.part ##Check if this group already exists $w365userssku = Get-MgGroup -Filter "DisplayName eq 'W365-Users-$skupart'" if ($null -eq $w365userssku) { ##Create W365 Groups for W365 users of each sku, manually assigned write-host "Creating W365 Users Group for SKU $skupart" $w365userssku = New-MGGroup -DisplayName "W365-Users-$skupart" -Description "Windows 365 Users $skupart" -MailEnabled:$False -MailNickName "W365Users_$i" -SecurityEnabled -IsAssignableToRole:$false write-host "W365 Users Group Created for SKU $skupart" $skugroupid = $w365userssku.Id ##Assign the license to the group write-host "Assigning License to Group - W365 Users $skupart" $uri = "https://graph.microsoft.com/v1.0/groups/$skugroupid/assignLicense" $body = @" { "addLicenses": [{ "disabledPlans": [], "skuId": "$skuid" }], "removeLicenses": [] } "@ Invoke-MgGraphRequest -Uri $uri -Method POST -Body $body -ContentType "application/json" write-host "License Assigned to Group W365 Users $skupart" ##Add to main group write-host "Nesting Group $skugroupid in $groupid" New-MgGroupMember -GroupId "$groupid" -DirectoryObjectId "$skugroupid" write-host "Group $skugroupid nested in $groupid" $i++ } else { write-host "Group W365-Users-$skupart already exists, skipping" } } ###############Front Line Users ##Create AAD Group ##Check if group exists first $w365frontlineusers = Get-MgGroup -Filter "DisplayName eq 'W365-Frontline-Users'" if ($null -eq $w365users) { ##Create W365 Groups for W365 Frontline users, manually assigned write-host "Creating W365 Frontline Users Group" $w365frontlineusers = New-MGGroup -DisplayName "W365-Frontline-Users" -Description "Windows 365 Frontline Users" -MailEnabled:$False -MailNickName "W365FrontlineUsers" -SecurityEnabled -IsAssignableToRole:$false write-host "W365 Frontline Users Group Created" } else { write-host "W365 Frontline Users Group already exists" } ##Assign Licenses to Group write-host "Assigning Licenses to Group" $frontlinegroupid = $w365frontlineusers.Id ##Get Assigned SKUs ##Get All skus in the tenant write-host "Getting SKUs" $skuf2 = ((Invoke-MgGraphRequest -uri "https://graph.microsoft.com/v1.0/subscribedSkus" -method get -OutputType PSObject).value) ##Loop through looking for W365 SKUs (currently start with Windows_365_S) $skuidfs = @() foreach ($skuf in $skuf2) { $partf = $skuf.skuPartNumber if (($partf -like "*Windows_365_S*")) { $skuidsobjectf = [pscustomobject]@{ sid = $skuf.skuid part = $partf } $skuidfs += $skuidsobjectf write-host "SKU Found - $partf" } } $i = 1 foreach ($skuidfitem in $skuidfs) { $skuidf = $skuidfitem.sid $skupartf = $skuidfitem.part ##Check if group exists $w365usersskuf = Get-MgGroup -Filter "DisplayName eq 'W365-Frontline-Users-$skupartf'" if ($null -eq $w365usersskuf) { ##Create W365 Groups for W365 users of each sku, manually assigned write-host "Creating W365 Users Group for SKU $skupartf" $w365usersskuf = New-MGGroup -DisplayName "W365-Frontline-Users-$skupartf" -Description "Windows 365 Frontline Users $skupartf" -MailEnabled:$False -MailNickName "W365Usersfront_$i" -SecurityEnabled -IsAssignableToRole:$false write-host "W365 Frontline Users Group Created for SKU $skuidf" $skugroupidf = $w365usersskuf.Id ##Assign the license to the group write-host "Assigning License to Group - W365 Users $skupartf" $uri = "https://graph.microsoft.com/v1.0/groups/$skugroupidf/assignLicense" $body = @" { "addLicenses": [{ "disabledPlans": [], "skuId": "$skuidf" }], "removeLicenses": [] } "@ Invoke-MgGraphRequest -Uri $uri -Method POST -Body $body -ContentType "application/json" write-host "License Assigned to Group W365 Users $skupartf" ##Add to main group write-host "Nesting Group $skugroupidf in $frontlinegroupid" New-MgGroupMember -GroupId "$frontlinegroupid" -DirectoryObjectId "$skugroupidf" write-host "Group $skugroupidf nested in $frontlinegroupid" $i++ } else { write-host "Group W365-Users-$skupartf already exists, skipping" } } |