BuildIntuneEnvironment.ps1
<#PSScriptInfo
.VERSION 2.0 .GUID 729ebf90-26fe-4795-92dc-ca8f570cdd22 .AUTHOR AndrewTaylor .DESCRIPTION Builds an Intune environment using intunebackupandrestore .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 microsoft.graph.intune intunebackupandrestore .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES #> <# .SYNOPSIS Builds an Intune Environment .DESCRIPTION Builds an Intune environment using intunebackupandrestore .INPUTS None required .OUTPUTS Within Azure .NOTES Version: 2.0 Author: Andrew Taylor Twitter: @AndrewTaylor_2 WWW: andrewstaylor.com Creation Date: 21/08/2021 Purpose/Change: Initial script development .EXAMPLE N/A #> $version = "2.0" ############################################################################################################### ###### Install Modules ###### ############################################################################################################### Write-Host "Installing Intune modules if required (current user scope)" #Install MS Graph if not available if (Get-Module -ListAvailable -Name Microsoft.Graph.Intune) { Write-Host "Microsoft Graph Already Installed" } else { try { Install-Module -Name Microsoft.Graph.Intune -Scope CurrentUser -Repository PSGallery -Force } catch [Exception] { $_.message exit } } #Install MS Intune Backup and Restore if not available if (Get-Module -ListAvailable -Name IntuneBackupAndRestore) { Write-Host "Intune Backup and Restore Already Installed" } else { try { Install-Module -Name IntuneBackupAndRestore -Scope CurrentUser -Repository PSGallery -Force } catch [Exception] { $_.message exit } } Write-Host "Installing AzureAD Preview modules if required (current user scope)" #Install AZ Module if not available if (Get-Module -ListAvailable -Name AzureADPreview) { Write-Host "AZ Ad Preview Module Already Installed" } else { try { Install-Module -Name AzureADPreview -Scope CurrentUser -Repository PSGallery -Force -AllowClobber } catch [Exception] { $_.message exit } } #Importing Modules Import-Module IntuneBackupAndRestore Import-Module Microsoft.Graph.Intune #Group creation needs preview module so we need to remove non-preview first # Unload the AzureAD module (or continue if it's already unloaded) Remove-Module AzureAD -ErrorAction SilentlyContinue # Load the AzureADPreview module Import-Module AzureADPreview ############################################################################################################### ###### Add Functions ###### ############################################################################################################### 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-DeviceConfigurationPolicy(){ <# .SYNOPSIS This function is used to get device configuration policies from the Graph API REST interface .DESCRIPTION The function connects to the Graph API Interface and gets any device configuration policies .EXAMPLE Get-DeviceConfigurationPolicy Returns any device configuration policies configured in Intune .NOTES NAME: Get-DeviceConfigurationPolicy #> [cmdletbinding()] param ( $name ) $graphApiVersion = "beta" $DCP_resource = "deviceManagement/deviceConfigurations" try { if($Name){ $uri = "https://graph.microsoft.com/$graphApiVersion/$($DCP_resource)?`$filter=displayName eq '$name'" (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).value } else { $uri = "https://graph.microsoft.com/$graphApiVersion/$($DCP_resource)" (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-DeviceConfigurationPolicySC(){ <# .SYNOPSIS This function is used to get device configuration policies from the Graph API REST interface - SETTINGS CATALOG .DESCRIPTION The function connects to the Graph API Interface and gets any device configuration policies .EXAMPLE Get-DeviceConfigurationPolicySC Returns any device configuration policies configured in Intune .NOTES NAME: Get-DeviceConfigurationPolicySC #> [cmdletbinding()] param ( $name ) $graphApiVersion = "beta" $DCP_resource = "deviceManagement/configurationPolicies" try { if($Name){ $uri = "https://graph.microsoft.com/$graphApiVersion/$($DCP_resource)?`$filter=name eq '$name'" (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).value } else { $uri = "https://graph.microsoft.com/$graphApiVersion/$($DCP_resource)" (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-AutoPilotProfile(){ <# .SYNOPSIS This function is used to get device configuration policies from the Graph API REST interface - AUTPILOT .DESCRIPTION The function connects to the Graph API Interface and gets any device configuration policies .EXAMPLE Get-AutoPilotProfile Returns any device configuration policies configured in Intune .NOTES NAME: Get-AutoPilotProfile #> [cmdletbinding()] param ( $name ) $graphApiVersion = "beta" $DCP_resource = "deviceManagement/windowsAutopilotDeploymentProfiles" try { if($Name){ $uri = "https://graph.microsoft.com/$graphApiVersion/$($DCP_resource)?`$filter=displayName eq '$name'" (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).value } else { $uri = "https://graph.microsoft.com/$graphApiVersion/$($DCP_resource)" (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-DeviceConfigurationPolicyAssignment(){ <# .SYNOPSIS This function is used to get device configuration policy assignment from the Graph API REST interface .DESCRIPTION The function connects to the Graph API Interface and gets a device configuration policy assignment .EXAMPLE Get-DeviceConfigurationPolicyAssignment $id guid Returns any device configuration policy assignment configured in Intune .NOTES NAME: Get-DeviceConfigurationPolicyAssignment #> [cmdletbinding()] param ( [Parameter(Mandatory=$true,HelpMessage="Enter id (guid) for the Device Configuration Policy you want to check assignment")] $id ) $graphApiVersion = "Beta" $DCP_resource = "deviceManagement/deviceConfigurations" try { $uri = "https://graph.microsoft.com/$graphApiVersion/$($DCP_resource)/$id/groupAssignments" (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-DeviceConfigurationPolicyAssignmentSC(){ <# .SYNOPSIS This function is used to get device configuration policy assignment from the Graph API REST interface - SETTINGS CATALOG Version .DESCRIPTION The function connects to the Graph API Interface and gets a device configuration policy assignment .EXAMPLE Get-DeviceConfigurationPolicyAssignmentSC $id guid Returns any device configuration policy assignment configured in Intune .NOTES NAME: Get-DeviceConfigurationPolicyAssignmentSC #> [cmdletbinding()] param ( [Parameter(Mandatory=$true,HelpMessage="Enter id (guid) for the Device Configuration Policy you want to check assignment")] $id ) $graphApiVersion = "Beta" $DCP_resource = "deviceManagement/configurationPolicies" try { $uri = "https://graph.microsoft.com/$graphApiVersion/$($DCP_resource)/$id/Assignments" (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-AutoPilotProfileAssignments(){ <# .SYNOPSIS This function is used to get AutoPilot Profile assignment from the Graph API REST interface .DESCRIPTION The function connects to the Graph API Interface and gets an Autopilot profile assignment .EXAMPLE Get-AutoPilotProfileAssignments $id guid Returns any autopilot profile assignment configured in Intune .NOTES NAME: Get-AutoPilotProfileAssignments #> [cmdletbinding()] param ( [Parameter(Mandatory=$true,HelpMessage="Enter id (guid) for the Autopilot Profile you want to check assignment")] $id ) $graphApiVersion = "Beta" $DCP_resource = "deviceManagement/windowsAutopilotDeploymentProfiles" try { $uri = "https://graph.microsoft.com/$graphApiVersion/$($DCP_resource)/$id/Assignments" (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 Add-DeviceConfigurationPolicyAssignment(){ <# .SYNOPSIS This function is used to add a device configuration policy assignment using the Graph API REST interface .DESCRIPTION The function connects to the Graph API Interface and adds a device configuration policy assignment .EXAMPLE Add-DeviceConfigurationPolicyAssignment -ConfigurationPolicyId $ConfigurationPolicyId -TargetGroupId $TargetGroupId Adds a device configuration policy assignment in Intune .NOTES NAME: Add-DeviceConfigurationPolicyAssignment #> [cmdletbinding()] param ( [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $ConfigurationPolicyId, [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $TargetGroupId, [parameter(Mandatory=$true)] [ValidateSet("Included","Excluded")] [ValidateNotNullOrEmpty()] [string]$AssignmentType ) $graphApiVersion = "Beta" $Resource = "deviceManagement/deviceConfigurations/$ConfigurationPolicyId/assign" try { if(!$ConfigurationPolicyId){ write-host "No Configuration Policy Id specified, specify a valid Configuration Policy Id" -f Red break } if(!$TargetGroupId){ write-host "No Target Group Id specified, specify a valid Target Group Id" -f Red break } # Checking if there are Assignments already configured in the Policy $DCPA = Get-DeviceConfigurationPolicyAssignment -id $ConfigurationPolicyId $TargetGroups = @() if(@($DCPA).count -ge 1){ if($DCPA.targetGroupId -contains $TargetGroupId){ Write-Host "Group with Id '$TargetGroupId' already assigned to Policy..." -ForegroundColor Red Write-Host break } # Looping through previously configured assignements $DCPA | foreach { $TargetGroup = New-Object -TypeName psobject if($_.excludeGroup -eq $true){ $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.exclusionGroupAssignmentTarget' } else { $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.groupAssignmentTarget' } $TargetGroup | Add-Member -MemberType NoteProperty -Name 'groupId' -Value $_.targetGroupId $Target = New-Object -TypeName psobject $Target | Add-Member -MemberType NoteProperty -Name 'target' -Value $TargetGroup $TargetGroups += $Target } # Adding new group to psobject $TargetGroup = New-Object -TypeName psobject if($AssignmentType -eq "Excluded"){ $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.exclusionGroupAssignmentTarget' } elseif($AssignmentType -eq "Included") { $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.groupAssignmentTarget' } $TargetGroup | Add-Member -MemberType NoteProperty -Name 'groupId' -Value "$TargetGroupId" $Target = New-Object -TypeName psobject $Target | Add-Member -MemberType NoteProperty -Name 'target' -Value $TargetGroup $TargetGroups += $Target } else { # No assignments configured creating new JSON object of group assigned $TargetGroup = New-Object -TypeName psobject if($AssignmentType -eq "Excluded"){ $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.exclusionGroupAssignmentTarget' } elseif($AssignmentType -eq "Included") { $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.groupAssignmentTarget' } $TargetGroup | Add-Member -MemberType NoteProperty -Name 'groupId' -Value "$TargetGroupId" $Target = New-Object -TypeName psobject $Target | Add-Member -MemberType NoteProperty -Name 'target' -Value $TargetGroup $TargetGroups = $Target } # Creating JSON object to pass to Graph $Output = New-Object -TypeName psobject $Output | Add-Member -MemberType NoteProperty -Name 'assignments' -Value @($TargetGroups) $JSON = $Output | ConvertTo-Json -Depth 3 # POST to Graph Service $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)" Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post -Body $JSON -ContentType "application/json" } 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 Add-DeviceConfigurationPolicyAssignmentSC(){ <# .SYNOPSIS This function is used to add a device configuration policy assignment using the Graph API REST interface Settings Catalog .DESCRIPTION The function connects to the Graph API Interface and adds a device configuration policy assignment .EXAMPLE Add-DeviceConfigurationPolicyAssignment -ConfigurationPolicyId $ConfigurationPolicyId -TargetGroupId $TargetGroupId Adds a device configuration policy assignment in Intune .NOTES NAME: Add-DeviceConfigurationPolicyAssignment #> [cmdletbinding()] param ( [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $ConfigurationPolicyId, [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $TargetGroupId, [parameter(Mandatory=$true)] [ValidateSet("Included","Excluded")] [ValidateNotNullOrEmpty()] [string]$AssignmentType ) $graphApiVersion = "Beta" $Resource = "deviceManagement/configurationPolicies/$ConfigurationPolicyId/assign" try { if(!$ConfigurationPolicyId){ write-host "No Configuration Policy Id specified, specify a valid Configuration Policy Id" -f Red break } if(!$TargetGroupId){ write-host "No Target Group Id specified, specify a valid Target Group Id" -f Red break } # Checking if there are Assignments already configured in the Policy $DCPA = Get-DeviceConfigurationPolicyAssignmentSC -id $ConfigurationPolicyId $TargetGroups = @() if(@($DCPA).count -ge 1){ if($DCPA.targetGroupId -contains $TargetGroupId){ Write-Host "Group with Id '$TargetGroupId' already assigned to Policy..." -ForegroundColor Red Write-Host break } # Looping through previously configured assignements $DCPA | foreach { $TargetGroup = New-Object -TypeName psobject if($_.excludeGroup -eq $true){ $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.exclusionGroupAssignmentTarget' } else { $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.groupAssignmentTarget' } $TargetGroup | Add-Member -MemberType NoteProperty -Name 'groupId' -Value $_.targetGroupId $Target = New-Object -TypeName psobject $Target | Add-Member -MemberType NoteProperty -Name 'target' -Value $TargetGroup $TargetGroups += $Target } # Adding new group to psobject $TargetGroup = New-Object -TypeName psobject if($AssignmentType -eq "Excluded"){ $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.exclusionGroupAssignmentTarget' } elseif($AssignmentType -eq "Included") { $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.groupAssignmentTarget' } $TargetGroup | Add-Member -MemberType NoteProperty -Name 'groupId' -Value "$TargetGroupId" $Target = New-Object -TypeName psobject $Target | Add-Member -MemberType NoteProperty -Name 'target' -Value $TargetGroup $TargetGroups += $Target } else { # No assignments configured creating new JSON object of group assigned $TargetGroup = New-Object -TypeName psobject if($AssignmentType -eq "Excluded"){ $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.exclusionGroupAssignmentTarget' } elseif($AssignmentType -eq "Included") { $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.groupAssignmentTarget' } $TargetGroup | Add-Member -MemberType NoteProperty -Name 'groupId' -Value "$TargetGroupId" $Target = New-Object -TypeName psobject $Target | Add-Member -MemberType NoteProperty -Name 'target' -Value $TargetGroup $TargetGroups = $Target } # Creating JSON object to pass to Graph $Output = New-Object -TypeName psobject $Output | Add-Member -MemberType NoteProperty -Name 'assignments' -Value @($TargetGroups) $JSON = $Output | ConvertTo-Json -Depth 3 # POST to Graph Service $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)" Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post -Body $JSON -ContentType "application/json" } 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 Add-AutoPilotProfileAssignment(){ <# .SYNOPSIS This function is used to add an autopilot profile assignment using the Graph API REST interface .DESCRIPTION The function connects to the Graph API Interface and adds an autopilot profile assignment .EXAMPLE Add-AutoPilotProfileAssignment -ConfigurationPolicyId $ConfigurationPolicyId -TargetGroupId $TargetGroupId Adds a device configuration policy assignment in Intune .NOTES NAME: Add-AutoPilotProfileAssignment #> [cmdletbinding()] param ( [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $ConfigurationPolicyId, [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $TargetGroupId, [parameter(Mandatory=$true)] [ValidateSet("Included","Excluded")] [ValidateNotNullOrEmpty()] [string]$AssignmentType ) $graphApiVersion = "Beta" $Resource = "deviceManagement/windowsAutopilotDeploymentProfiles/$ConfigurationPolicyId/assignments" try { if(!$ConfigurationPolicyId){ write-host "No Configuration Policy Id specified, specify a valid Configuration Policy Id" -f Red break } if(!$TargetGroupId){ write-host "No Target Group Id specified, specify a valid Target Group Id" -f Red break } # Checking if there are Assignments already configured in the Policy $DCPA = Get-AutoPilotProfileAssignments -id $ConfigurationPolicyId $TargetGroups = @() if(@($DCPA).count -ge 1){ if($DCPA.targetGroupId -contains $TargetGroupId){ Write-Host "Group with Id '$TargetGroupId' already assigned to Policy..." -ForegroundColor Red Write-Host break } # Looping through previously configured assignements $DCPA | foreach { $TargetGroup = New-Object -TypeName psobject if($_.excludeGroup -eq $true){ $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.exclusionGroupAssignmentTarget' } else { $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.groupAssignmentTarget' } $TargetGroup | Add-Member -MemberType NoteProperty -Name 'groupId' -Value $_.targetGroupId $Target = New-Object -TypeName psobject $Target | Add-Member -MemberType NoteProperty -Name 'target' -Value $TargetGroup $TargetGroups += $Target } # Adding new group to psobject $TargetGroup = New-Object -TypeName psobject if($AssignmentType -eq "Excluded"){ $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.exclusionGroupAssignmentTarget' } elseif($AssignmentType -eq "Included") { $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.groupAssignmentTarget' } $TargetGroup | Add-Member -MemberType NoteProperty -Name 'groupId' -Value "$TargetGroupId" $Target = New-Object -TypeName psobject $Target | Add-Member -MemberType NoteProperty -Name 'target' -Value $TargetGroup $TargetGroups += $Target } else { # No assignments configured creating new JSON object of group assigned $TargetGroup = New-Object -TypeName psobject if($AssignmentType -eq "Excluded"){ $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.exclusionGroupAssignmentTarget' } elseif($AssignmentType -eq "Included") { $TargetGroup | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.deviceAndAppManagementAssignmentTarget' $TargetGroup | Add-Member -MemberType NoteProperty -Name 'deviceAndAppManagementAssignmentFilterType' -Value 'include' } $TargetGroup | Add-Member -MemberType NoteProperty -Name 'deviceAndAppManagementAssignmentFilterId' -Value "$TargetGroupId" $Target = New-Object -TypeName psobject $Target | Add-Member -MemberType NoteProperty -Name '@odata.type' -Value '#microsoft.graph.windowsAutopilotDeploymentProfileAssignment' $Target | Add-Member -MemberType NoteProperty -Name 'target' -Value $TargetGroup $Target | Add-Member -MemberType NoteProperty -Name 'sourceId' -Value $TargetGroupId $Target | Add-Member -MemberType NoteProperty -Name 'source' -Value "direct" $TargetGroups = $Target } # Creating JSON object to pass to Graph $Output = New-Object -TypeName psobject $Output | Add-Member -MemberType NoteProperty -Name 'assignments' -Value @($TargetGroups) $JSON = $Output | ConvertTo-Json -Depth 4 # POST to Graph Service $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)" Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post -Body $JSON -ContentType "application/json" } 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 } } ############################################################################################################### ###### Launch Form ###### ############################################################################################################### <# This form was created using POSHGUI.com a free online gui designer for PowerShell .NAME Intune #> Add-Type -AssemblyName System.Windows.Forms [System.Windows.Forms.Application]::EnableVisualStyles() $CreateIntuneEnv = New-Object system.Windows.Forms.Form $CreateIntuneEnv.ClientSize = New-Object System.Drawing.Point(397,379) $CreateIntuneEnv.text = "Create Intune Environment Version $version" $CreateIntuneEnv.TopMost = $false $CreateIntuneEnv.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#ffffff") $Label1 = New-Object system.Windows.Forms.Label $Label1.text = "Created by Andrew Taylor (andrewstaylor.com)" $Label1.AutoSize = $true $Label1.width = 25 $Label1.height = 10 $Label1.location = New-Object System.Drawing.Point(4,348) $Label1.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $Label2 = New-Object system.Windows.Forms.Label $Label2.text = "Client Name" $Label2.AutoSize = $true $Label2.width = 25 $Label2.height = 10 $Label2.location = New-Object System.Drawing.Point(10,22) $Label2.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $Label3 = New-Object system.Windows.Forms.Label $Label3.text = "Tenant ID" $Label3.AutoSize = $true $Label3.width = 25 $Label3.height = 10 $Label3.location = New-Object System.Drawing.Point(10,66) $Label3.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $Label4 = New-Object system.Windows.Forms.Label $Label4.text = "HomePage" $Label4.AutoSize = $true $Label4.width = 25 $Label4.height = 10 $Label4.location = New-Object System.Drawing.Point(10,109) $Label4.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $Label5 = New-Object system.Windows.Forms.Label $Label5.text = "Background URL" $Label5.AutoSize = $true $Label5.width = 25 $Label5.height = 10 $Label5.location = New-Object System.Drawing.Point(10,156) $Label5.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $Label6 = New-Object system.Windows.Forms.Label $Label6.text = "Background Filename" $Label6.AutoSize = $true $Label6.width = 25 $Label6.height = 40 $Label6.location = New-Object System.Drawing.Point(10,215) $Label6.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $clientname = New-Object system.Windows.Forms.TextBox $clientname.multiline = $false $clientname.width = 200 $clientname.height = 20 $clientname.location = New-Object System.Drawing.Point(151,22) $clientname.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $tenantid = New-Object system.Windows.Forms.TextBox $tenantid.multiline = $false $tenantid.width = 200 $tenantid.height = 20 $tenantid.location = New-Object System.Drawing.Point(151,66) $tenantid.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $homepage = New-Object system.Windows.Forms.TextBox $homepage.multiline = $false $homepage.width = 200 $homepage.height = 20 $homepage.location = New-Object System.Drawing.Point(151,109) $homepage.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $bgurl = New-Object system.Windows.Forms.TextBox $bgurl.multiline = $false $bgurl.width = 200 $bgurl.height = 20 $bgurl.location = New-Object System.Drawing.Point(151,156) $bgurl.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $bgname = New-Object system.Windows.Forms.TextBox $bgname.multiline = $false $bgname.width = 200 $bgname.height = 20 $bgname.location = New-Object System.Drawing.Point(151,215) $bgname.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $build = New-Object system.Windows.Forms.Button $build.text = "Build" $build.width = 153 $build.height = 72 $build.location = New-Object System.Drawing.Point(113,257) $build.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',19) $Label7 = New-Object system.Windows.Forms.Label $Label7.text = " (inc extension)" $Label7.AutoSize = $true $Label7.width = 25 $Label7.height = 10 $Label7.location = New-Object System.Drawing.Point(29,231) $Label7.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10) $CreateIntuneEnv.controls.AddRange(@($Label1,$Label2,$Label3,$Label4,$Label5,$Label6,$clientname,$tenantid,$homepage,$bgurl,$bgname,$build,$Label7)) $build.Add_Click({ $clientnameout = $clientname.Text $tenantidout = $tenantid.Text $homepageout = $homepage.Text $bloburl = $bgurl.Text $backgroundfilename = $bgname.Text ############################################################################################################### ###### Deploy ###### ############################################################################################################### ############################################################################################################### ###### Group Creation ###### ############################################################################################################### #Connect to Azure AD Connect-AzureAD #Create Azure AD Groups #AutoPilot Group $autopilotgrp = New-AzureADMSGroup -DisplayName "Autopilot-Devices" -Description "Dynamic group for Autopilot Devices" -MailEnabled $False -MailNickName "group" -SecurityEnabled $True -GroupTypes "DynamicMembership" -MembershipRule "(device.devicePhysicalIDs -any (_ -contains ""[ZTDid]""))" -MembershipRuleProcessingState "On" #Pilot Group $pilotgrp = New-AzureADMSGroup -DisplayName "Intune-Pilot-Users" -Description "Assigned group for Pilot Users" -MailEnabled $False -MailNickName "group" -SecurityEnabled $True #Preview Group $previewgrp = New-AzureADMSGroup -DisplayName "Intune-Preview-Users" -Description "Assigned group for Preview Users" -MailEnabled $False -MailNickName "group" -SecurityEnabled $True #VIP Group $vipgrp = New-AzureADMSGroup -DisplayName "Intune-VIP-Users" -Description "Assigned group for VIP Users" -MailEnabled $False -MailNickName "group" -SecurityEnabled $True #Notify complete Add-Type -AssemblyName PresentationCore,PresentationFramework $msgBody = "Autopilot AAD Group Created, moving on to Intune Deployment" [System.Windows.MessageBox]::Show($msgBody) ##Connect to Intune Connect-MSGraph ############################################################################################################### ###### Create Dir ###### ############################################################################################################### #Create path for files $DirectoryToCreate = "c:\temp" if (-not (Test-Path -LiteralPath $DirectoryToCreate)) { try { New-Item -Path $DirectoryToCreate -ItemType Directory -ErrorAction Stop | Out-Null #-Force } catch { Write-Error -Message "Unable to create directory '$DirectoryToCreate'. Error was: $_" -ErrorAction Stop } "Successfully created directory '$DirectoryToCreate'." } else { "Directory already existed" } $random = Get-Random -Maximum 1000 $random = $random.ToString() $date =get-date -format yyMMddmmss $date = $date.ToString() $path2 = $random + "-" + $date $path = "c:\temp\" + $path2 + "\" New-Item -ItemType Directory -Path $path $pathvar = [PSCustomObject]@{value=$path} Write-Host "Directory Created" #Set Paths $url = "https://github.com/andrew-s-taylor/Intune-Config/archive/main.zip" $pathaz = "c:\temp\" + $path2 + "\Intune-Config" $output = "c:\temp\" + $path2 + "\main.zip" ############################################################################################################### ###### Extract ###### ############################################################################################################### #Download Files Invoke-WebRequest -Uri $url -OutFile $output -Method Get #Unzip them Expand-Archive $output -DestinationPath $path -Force #Remove Zip file downloaded remove-item $output -Force #Notify complete Add-Type -AssemblyName PresentationCore,PresentationFramework $msgBody = "Files saves to $path" [System.Windows.MessageBox]::Show($msgBody) ############################################################################################################### ###### Edit Scripts ###### ############################################################################################################### ##Device Script $devicescript = $path + "\Intune-Config-main\Device Management Scripts\Script Content\Device Config.ps1" #Update Client Name (Get-Content -path $devicescript -Raw ) ` -replace '<CLIENTREPLACENAME>',$clientnameout | Set-Content -Path $devicescript #Update O365 Tenant (Get-Content -path $devicescript -Raw ) ` -replace '<CLIENTTENANT>',$tenantidout | Set-Content -Path $devicescript #Update Client Homepage (Get-Content -path $devicescript -Raw ) ` -replace '<CLIENTHOMEPAGE>',$homepageout | Set-Content -Path $devicescript #Update Background location (Get-Content -path $devicescript -Raw ) ` -replace '<BACKGROUNDBLOBURL>',$bloburl | Set-Content -Path $devicescript #Update Background Name (Get-Content -path $devicescript -Raw ) ` -replace '<BACKGROUNDFILENAME>',$backgroundfilename | Set-Content -Path $devicescript ##User Script $userscript = $path + "\Intune-Config-main\Device Management Scripts\Script Content\User-Config.ps1" (Get-Content -path $userscript -Raw ) ` -replace '<BACKGROUNDFILENAME>',$backgroundfilename | Set-Content -Path $userscript ##Restore Start-IntuneRestoreConfig -Path $path ############################################################################################################### ###### MS Graph Implementations ###### ############################################################################################################### #Assign Policies to Groups #Authenticate for MS Graph #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 ############################################################################################################### ###### Create Autpilot Profile ###### ############################################################################################################### $graphApiVersion = "beta" $Resource = "deviceManagement/windowsAutopilotDeploymentProfiles" $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource" $profilename = "Autopilot Profile" $json = @" { "@odata.type": "#microsoft.graph.azureADWindowsAutopilotDeploymentProfile", "displayName": "$profilename", "description": "OOBE Autopilot Profile", "language": "en-GB", "extractHardwareHash": true, "deviceNameTemplate": "%SERIAL%", "deviceType": "windowsPc", "enableWhiteGlove": true, "outOfBoxExperienceSettings": { "hidePrivacySettings": true, "hideEULA": true, "userType": "standard", "deviceUsageType": "singleUser", "skipKeyboardSelectionPage": false, "hideEscapeLink": true }, "enrollmentStatusScreenSettings": { "@odata.type": "microsoft.graph.windowsEnrollmentStatusScreenSettings", "hideInstallationProgress": false, "allowDeviceUseBeforeProfileAndAppInstallComplete": true, "blockDeviceSetupRetryByUser": true, "allowLogCollectionOnInstallFailure": true, "installProgressTimeoutInMinutes": 120, "allowDeviceUseOnInstallFailure": true } } "@ Write-Verbose "POST $uri`n$json" try { Invoke-MSGraphRequest -Url $uri -HttpMethod POST -Content $json } catch { Write-Error $_.Exception break } ##########################################endregion############################################################## ############################################################################################################### ###### Create Enrollment Status Page ###### ############################################################################################################### $graphApiVersion = "beta" $Resource = "deviceManagement/deviceEnrollmentConfigurations" $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource" $json = @" { "@odata.type": "#microsoft.graph.windows10EnrollmentCompletionPageConfiguration", "displayName": "AutoPilot Enrollment", "description": "Custom Enrollment Status", "showInstallationProgress": true, "blockDeviceSetupRetryByUser": false, "allowDeviceResetOnInstallFailure": false, "allowLogCollectionOnInstallFailure": true, "customErrorMessage": "Enter your custom error here", "installProgressTimeoutInMinutes": 120, "allowDeviceUseOnInstallFailure": true } "@ Write-Verbose "POST $uri`n$json" try { $enrollment = Invoke-MSGraphRequest -Url $uri -HttpMethod Post -Content $json } catch { Write-Error $_.Exception break } ##Assign it # Defining Variables $id = $enrollment.id #Remove extra text from the ID #$id2 = $id3.split('_') #$id = $id2[0] $groupid = $autopilotgrp.id $graphApiVersion = "beta" $Resource = "deviceManagement/deviceEnrollmentConfigurations" $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id/assign" $json = @" { "enrollmentConfigurationAssignments": [ { "target": { "@odata.type": "#microsoft.graph.groupAssignmentTarget", "groupId": "$groupid" } } ] } "@ Write-Verbose "POST $uri`n$json" try { Invoke-MSGraphRequest -Url $uri -HttpMethod Post -Content $json } catch { Write-Error $_.Exception break } ####################################################### ############################################################################################################### ###### Assign Autopilot Profile ###### ############################################################################################################### # Defining Variables $graphApiVersion = "beta" $Resource = "deviceManagement/windowsAutopilotDeploymentProfiles" $uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id/assignments" $ap1 = Get-AutoPilotProfile -name $profilename $id = $ap1.id $groupid = $autopilotgrp.id $full_assignment_id = $id + "_" + $groupid + "_0" $json = @" { "id": "$full_assignment_id", "target": { "@odata.type": "#microsoft.graph.groupAssignmentTarget", "groupId": "$groupid" } } "@ Write-Verbose "POST $uri`n$json" try { Invoke-MSGraphRequest -Url $uri -HttpMethod Post -Content $json } catch { Write-Error $_.Exception break } ######################################################################### ############################################################################################################### ###### Assign Windows Update Rings ###### ############################################################################################################### #Assign Windows Update Rings #Pilot Ring $PolicyName = "Pilot Ring" $DCP = Get-DeviceConfigurationPolicy -name "$PolicyName" if($DCP){ $Assignment = Add-DeviceConfigurationPolicyAssignment -ConfigurationPolicyId $DCP.id -TargetGroupId $pilotgrp.id -AssignmentType Included Write-Host "Assigned '$pilotgrp.Name' to $($DCP.displayName)/$($DCP.id)" -ForegroundColor Green Write-Host } else { Write-Host "Can't find Device Configuration Policy with name '$PolicyName'..." -ForegroundColor Red Write-Host } #Preview Ring $PolicyName = "Preview Ring" $DCP = Get-DeviceConfigurationPolicy -name "$PolicyName" if($DCP){ $Assignment = Add-DeviceConfigurationPolicyAssignment -ConfigurationPolicyId $DCP.id -TargetGroupId $previewgrp.id -AssignmentType Included Write-Host "Assigned '$previewgrp' to $($DCP.displayName)/$($DCP.id)" -ForegroundColor Green Write-Host } else { Write-Host "Can't find Device Configuration Policy with name '$PolicyName'..." -ForegroundColor Red Write-Host } #VIP Ring $PolicyName = "VIP Channel" $DCP = Get-DeviceConfigurationPolicy -name "$PolicyName" if($DCP){ $Assignment = Add-DeviceConfigurationPolicyAssignment -ConfigurationPolicyId $DCP.id -TargetGroupId $vipgrp.id -AssignmentType Included Write-Host "Assigned '$vipgrp' to $($DCP.displayName)/$($DCP.id)" -ForegroundColor Green Write-Host } else { Write-Host "Can't find Device Configuration Policy with name '$PolicyName'..." -ForegroundColor Red Write-Host } #Broad Ring $PolicyName = "Broad Ring" $DCP = Get-DeviceConfigurationPolicy -name "$PolicyName" if($DCP){ $Assignment = Add-DeviceConfigurationPolicyAssignment -ConfigurationPolicyId $DCP.id -TargetGroupId $vipgrp.id -AssignmentType Excluded $Assignment = Add-DeviceConfigurationPolicyAssignment -ConfigurationPolicyId $DCP.id -TargetGroupId $pilotgrp.id -AssignmentType Excluded $Assignment = Add-DeviceConfigurationPolicyAssignment -ConfigurationPolicyId $DCP.id -TargetGroupId $previewgrp.id -AssignmentType Excluded Write-Host "Assigned exclusion groups to $($DCP.displayName)/$($DCP.id)" -ForegroundColor Green Write-Host } else { Write-Host "Can't find Device Configuration Policy with name '$PolicyName'..." -ForegroundColor Red Write-Host } ############################################################################################################### ###### Assign Office Update Rings ###### ############################################################################################################### #Assign Office Update Rings #Pilot Ring $PolicyName = "Office-PilotRing" $DCP = Get-DeviceConfigurationPolicySC -name "$PolicyName" if($DCP){ $Assignment = Add-DeviceConfigurationPolicyAssignmentSC -ConfigurationPolicyId $DCP.id -TargetGroupId $pilotgrp.id -AssignmentType Included Write-Host "Assigned '$pilotgrp' to $($DCP.displayName)/$($DCP.id)" -ForegroundColor Green Write-Host } else { Write-Host "Can't find Device Configuration Policy with name '$PolicyName'..." -ForegroundColor Red Write-Host } #Preview Ring $PolicyName = "Office-PreviewRing" $DCP = Get-DeviceConfigurationPolicySC -name "$PolicyName" if($DCP){ $Assignment = Add-DeviceConfigurationPolicyAssignmentSC -ConfigurationPolicyId $DCP.id -TargetGroupId $previewgrp.id -AssignmentType Included Write-Host "Assigned '$previewgrp' to $($DCP.displayName)/$($DCP.id)" -ForegroundColor Green Write-Host } else { Write-Host "Can't find Device Configuration Policy with name '$PolicyName'..." -ForegroundColor Red Write-Host } #VIP Ring $PolicyName = "Office-VIPRing" $DCP = Get-DeviceConfigurationPolicySC -name "$PolicyName" if($DCP){ $Assignment = Add-DeviceConfigurationPolicyAssignmentSC -ConfigurationPolicyId $DCP.id -TargetGroupId $vipgrp.id -AssignmentType Included Write-Host "Assigned '$vipgrp' to $($DCP.displayName)/$($DCP.id)" -ForegroundColor Green Write-Host } else { Write-Host "Can't find Device Configuration Policy with name '$PolicyName'..." -ForegroundColor Red Write-Host } #Broad Ring $PolicyName = "Office-BroadRing" $DCP = Get-DeviceConfigurationPolicySC -name "$PolicyName" if($DCP){ $Assignment = Add-DeviceConfigurationPolicyAssignmentSC -ConfigurationPolicyId $DCP.id -TargetGroupId $vipgrp.id -AssignmentType Excluded $Assignment = Add-DeviceConfigurationPolicyAssignmentSc -ConfigurationPolicyId $DCP.id -TargetGroupId $pilotgrp.id -AssignmentType Excluded $Assignment = Add-DeviceConfigurationPolicyAssignmentSC -ConfigurationPolicyId $DCP.id -TargetGroupId $previewgrp.id -AssignmentType Excluded Write-Host "Assigned all exclusion groups to $($DCP.displayName)/$($DCP.id)" -ForegroundColor Green Write-Host } else { Write-Host "Can't find Device Configuration Policy with name '$PolicyName'..." -ForegroundColor Red Write-Host } ############################################################################################################### ###### DONE ###### ############################################################################################################### Add-Type -AssemblyName PresentationCore,PresentationFramework $msgBody = "Environment Built" [System.Windows.MessageBox]::Show($msgBody) }) [void]$CreateIntuneEnv.ShowDialog() |