Bulk wipe Autopilot devices
Useful for schools and devices that change users often. If there is a need for bulk wipe devices that are registered in Autopilot we can use PowerShell and Graph to make this an easy task. The script takes all users in the group, finds Windows devices that they are Primary users on and wipe.
We need to create a Entra group first to collect our target users. For this example we call the group: Intune-Bulk Wipe Autopilot Devices. Get the ID of the group and change that in the script.
The script is based on the device management from the Intune example repository
Add the users for whom the devices should be wiped. run the script and boot up the devices. When I have been using this I have had like 10 network cables and a big table just to boot them up so they get the wipe command and reinstall. After they are back in the OOBE I just shut them down and the are ready for handing out to the next class.
It will also generate a report which devices it wiped and the upn’s that was connected to them.
#Import-Module AzureAD
Connect-AzureAD
#region Parameters
# The Entra group
$targets = Get-AzureADGroupMember -ObjectId 1234ABC-a336-446d-1234-6e36f35f789 | Select-Object userprincipalname
#endregion
####################################################
function Get-AuthToken {
<#
.SYNOPSIS
This function is used to authenticate with the Graph API REST interface
.DESCRIPTION
The function authenticate with the Graph API Interface with the tenant name
.EXAMPLE
Get-AuthToken
Authenticates you with the Graph API interface
.NOTES
NAME: 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-AADUser(){
<#
.SYNOPSIS
This function is used to get AAD Users from the Graph API REST interface
.DESCRIPTION
The function connects to the Graph API Interface and gets any users registered with AAD
.EXAMPLE
Get-AADUser
Returns all users registered with Azure AD
.EXAMPLE
Get-AADUser -userPrincipleName user@domain.com
Returns specific user by UserPrincipalName registered with Azure AD
.NOTES
NAME: Get-AADUser
#>
[cmdletbinding()]
param
(
$userPrincipalName,
$Property
)
# Defining Variables
$graphApiVersion = "v1.0"
$User_resource = "users"
try {
if($userPrincipalName -eq "" -or $userPrincipalName -eq $null){
$uri = "https://graph.microsoft.com/$graphApiVersion/$($User_resource)"
(Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).Value
}
else {
if($Property -eq "" -or $Property -eq $null){
$uri = "https://graph.microsoft.com/$graphApiVersion/$($User_resource)/$userPrincipalName"
Write-Verbose $uri
Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get
}
else {
$uri = "https://graph.microsoft.com/$graphApiVersion/$($User_resource)/$userPrincipalName/$Property"
Write-Verbose $uri
(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-AADUserDevices(){
<#
.SYNOPSIS
This function is used to get an AAD User Devices from the Graph API REST interface
.DESCRIPTION
The function connects to the Graph API Interface and gets a users devices registered with Intune MDM
.EXAMPLE
Get-AADUserDevices -UserID $UserID
Returns all user devices registered in Intune MDM
.NOTES
NAME: Get-AADUserDevices
#>
[cmdletbinding()]
param
(
[Parameter(Mandatory=$true,HelpMessage="UserID (guid) for the user you want to take action on must be specified:")]
$UserID
)
# Defining Variables
$graphApiVersion = "beta"
$Resource = "users/$UserID/managedDevices"
try {
$uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
Write-Verbose $uri
(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 Invoke-DeviceAction(){
<#
.SYNOPSIS
This function is used to set a generic intune resources from the Graph API REST interface
.DESCRIPTION
The function connects to the Graph API Interface and sets a generic Intune Resource
.EXAMPLE
Invoke-DeviceAction -DeviceID $DeviceID -remoteLock
Resets a managed device passcode
.NOTES
NAME: Invoke-DeviceAction
#>
[cmdletbinding()]
param
(
[switch]$RemoteLock,
[switch]$ResetPasscode,
[switch]$Wipe,
[switch]$Retire,
[switch]$Delete,
[switch]$Sync,
[switch]$Rename,
[Parameter(Mandatory=$true,HelpMessage="DeviceId (guid) for the Device you want to take action on must be specified:")]
$DeviceID
)
$graphApiVersion = "Beta"
try {
$Count_Params = 0
if($RemoteLock.IsPresent){ $Count_Params++ }
if($ResetPasscode.IsPresent){ $Count_Params++ }
if($Wipe.IsPresent){ $Count_Params++ }
if($Retire.IsPresent){ $Count_Params++ }
if($Delete.IsPresent){ $Count_Params++ }
if($Sync.IsPresent){ $Count_Params++ }
if($Rename.IsPresent){ $Count_Params++ }
if($Count_Params -eq 0){
write-host "No parameter set, specify -RemoteLock -ResetPasscode -Wipe -Delete -Sync or -rename against the function" -f Red
}
elseif($Count_Params -gt 1){
write-host "Multiple parameters set, specify a single parameter -RemoteLock -ResetPasscode -Wipe -Delete or -Sync against the function" -f Red
}
elseif($RemoteLock){
$Resource = "deviceManagement/managedDevices/$DeviceID/remoteLock"
$uri = "https://graph.microsoft.com/$graphApiVersion/$($resource)"
write-verbose $uri
Write-Verbose "Sending remoteLock command to $DeviceID"
Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post
}
elseif($ResetPasscode){
$Resource = "deviceManagement/managedDevices/$DeviceID/resetPasscode"
$uri = "https://graph.microsoft.com/$graphApiVersion/$($resource)"
write-verbose $uri
Write-Verbose "Sending remotePasscode command to $DeviceID"
Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post
}
elseif($Wipe){
$Resource = "deviceManagement/managedDevices/$DeviceID/wipe"
$uri = "https://graph.microsoft.com/$graphApiVersion/$($resource)"
write-verbose $uri
Write-Verbose "Sending wipe command to $DeviceID"
Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post
}
elseif($Retire){
$Resource = "deviceManagement/managedDevices/$DeviceID/retire"
$uri = "https://graph.microsoft.com/$graphApiVersion/$($resource)"
write-verbose $uri
Write-Verbose "Sending retire command to $DeviceID"
Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post
}
elseif($Delete){
$Resource = "deviceManagement/managedDevices('$DeviceID')"
$uri = "https://graph.microsoft.com/$graphApiVersion/$($resource)"
write-verbose $uri
Write-Verbose "Sending delete command to $DeviceID"
Invoke-RestMethod -Uri $uri -Headers $authToken -Method Delete
}
elseif($Sync){
$Resource = "deviceManagement/managedDevices('$DeviceID')/syncDevice"
$uri = "https://graph.microsoft.com/$graphApiVersion/$($resource)"
write-verbose $uri
Write-Verbose "Sending sync command to $DeviceID"
Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post
}
elseif($Rename){
write-host "Please type the new device name:" -ForegroundColor Yellow
$NewDeviceName = Read-Host
$JSON = @"
{
deviceName:"$($NewDeviceName)"
}
"@
write-host
write-host "Note: The RenameDevice remote action is only supported on supervised iOS devices"
write-host "Are you sure you want to rename this device to" $($NewDeviceName) "(Y or N?)"
$Confirm = read-host
if($Confirm -eq "y" -or $Confirm -eq "Y"){
$Resource = "deviceManagement/managedDevices('$DeviceID')/setDeviceName"
$uri = "https://graph.microsoft.com/$graphApiVersion/$($resource)"
write-verbose $uri
Write-Verbose "Sending rename command to $DeviceID"
Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post -Body $Json -ContentType "application/json"
}
else {
Write-Host "Rename of the device $DeviceID was cancelled..."
}
}
}
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
}
}
####################################################
#region Authentication
write-host
# Checking if authToken exists before running authentication
if($global:authToken){
# Setting DateTime to Universal time to work in all timezones
$DateTime = (Get-Date).ToUniversalTime()
# If the authToken exists checking when it expires
$TokenExpires = ($authToken.ExpiresOn.datetime - $DateTime).Minutes
if($TokenExpires -le 0){
write-host "Authentication Token expired" $TokenExpires "minutes ago" -ForegroundColor Yellow
write-host
# Defining User Principal Name if not present
if($User -eq $null -or $User -eq ""){
$User = Read-Host -Prompt "Please specify your user principal name for Azure Authentication"
Write-Host
}
$global:authToken = Get-AuthToken -User $User
}
}
# Authentication doesn't exist, calling Get-AuthToken function
else {
if($User -eq $null -or $User -eq ""){
$User = Read-Host -Prompt "Please specify your user principal name for Azure Authentication"
Write-Host
}
# Getting the authorization token
$global:authToken = Get-AuthToken -User $User
}
#endregion
####################################################
#region Device action
$output = @()
foreach($target in $targets){
$User = Get-AADUser -userPrincipalName $target.UserPrincipalName
$id = $User.Id
$Devices = Get-AADUserDevices -UserID $id -ErrorAction SilentlyContinue
if($Devices){
$DeviceCount = @($Devices).count
Write-Host
Write-Host $User.userPrincipalName "has" $DeviceCount "devices added to Intune..." -ForegroundColor Green
Write-Host
Foreach ($d in $Devices){
$SelectedDeviceId = $d | select -ExpandProperty id
# Device action
#Invoke-DeviceAction -DeviceID $SelectedDeviceId -RemoteLock -Verbose
#Invoke-DeviceAction -DeviceID $SelectedDeviceId -Retire -Verbose
Invoke-DeviceAction -DeviceID $SelectedDeviceId -Wipe -Verbose
#Invoke-DeviceAction -DeviceID $SelectedDeviceId -Delete -Verbose
#Invoke-DeviceAction -DeviceID $SelectedDeviceId -Sync -Verbose
$data = New-Object TypeName PSObject
$data | Add-Member MemberType NoteProperty Name 'UserPrincipalName' Value $User.userPrincipalName -ErrorAction SilentlyContinue
$data | Add-Member MemberType NoteProperty Name 'Display Name' Value $User.displayname -ErrorAction SilentlyContinue
$data | Add-Member MemberType NoteProperty Name 'Device Name' Value $D.deviceName -ErrorAction SilentlyContinue
$data | Add-Member MemberType NoteProperty Name 'Device Id' Value $SelectedDeviceId -ErrorAction SilentlyContinue
$output += $data
}
}
#$output | Format-Table -AutoSize
}
$output | Export-Csv -Path RemoteWipe-Report.csv -NoTypeInformation -Encoding utf8
#endregion