Tuesday, November 30, 2021

Use paging with Graph to change group tags on all AutoPilot devices

 Use paging with Graph to change group tags on all AutoPilot devices. Graph has a 1000 device limit and this script uses paging to get all of the devices. Once we have all of the devices we can manipulate group tags.

I would like to shout out January 2021 – TheSleepyAdmins for the assist on paging as well as Bulk Updating Autopilot enrolled devices with Graph API and assigning a Group Tag based on Purchase OrderID - Systems Management Squad (sysmansquad.com) for help with some of the other bits.


# Application (client) ID, tenant Name and secret

$clientid = Read-Host "Input your Client ID"

$clientSecret = Read-Host "Input Client Secret"

$TenantName = Read-Host "Input Tenant Name"

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

#$grouptag = "AP-Tag1"

$ReqTokenBody = @{

    Grant_Type    = "client_credentials"

    Scope         = "https://graph.microsoft.com/.default"

    client_Id     = $clientID

    Client_Secret = $clientSecret

 

$TokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token" -Method POST -Body $ReqTokenBody

$apiUrl = 'https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities/'

$Data = Invoke-RestMethod -Headers @{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Method Get

$Results = @()

$Results += $Data.value

[int]$pagecount = 0


$Pages = $Data.'@odata.nextLink'

while($null -ne $Pages) {

$pagecount += 1

Write-host "Checking Page $Pagecount. Count is $($Results.count)"

$Addtional = Invoke-RestMethod -Headers @{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $Pages -Method Get


if ($Pages){

$Pages = $Addtional."@odata.nextLink"

}

$Results += $Addtional.value

}

$Results | Export-Csv -Path "c:\temp\Devices.csv" -NoTypeInformation

#Tag the devices

if($grouptag -ne $null){

$body = '{"groupTag":"'+$groupTag+'"}'

foreach ($Result in $Results) {

    $apiUrl = "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeviceIdentities/$device/UpdateDeviceProperties"

    $rest = Invoke-RestMethod -Headers @{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl -Body $body -Method Post -ContentType 'application/json'

    Write-Host ($device + ' has been added to the ' + $grouptag)

}

 

#Sync the changes

$apiUrl2 = "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotSettings/sync"

Invoke-RestMethod -Headers @{Authorization = "Bearer $($TokenResponse.access_token)"} -Uri $apiUrl2 -Method Post


}


Tuesday, November 9, 2021

PowerShell Code to get all AD Users and attributes.


#Get All AD Users

$Table = @()

$Arrayofmembers = Get-ADUser -filter {objectClass -eq "user"}  -searchbase "DC=LOWES,DC=com" -Properties samaccountname | Sort-Object -Descending

[int]$count = $Arrayofmembers.Count


Foreach($A in $Arrayofmembers) {

$User = Get-ADUser $A.SamAccountName -Properties * | select name,cn,mail,physicaldeliveryofficename,title,extensionattribute12,extensionattribute5,extensionattribute10,extensionAttribute7, @{n=’MemberOf’; e= { ( $_.memberof | % { (Get-ADObject $_).Name }) -join “;” }}

[int]$count = $count - 1

Write-Host "Getting $($a.samaccountname) - $count to go"

$Obj=New-Object PSObject

    $Obj | Add-Member -Name "name" -MemberType NoteProperty  -Value $User.name

    $Obj | Add-Member -Name "CN" -MemberType NoteProperty  -Value $User.cn

    $Obj | Add-Member -Name "physicaldeliveryofficename" -MemberType NoteProperty  -Value $User.physicaldeliveryofficename

    $Obj | Add-Member -Name "Title" -MemberType NoteProperty  -Value $User.title

    $Obj | Add-Member -Name "lowesEffectiveJobCode" -MemberType NoteProperty  -Value $User.extensionattribute12

    $Obj | Add-Member -Name "lowesEffectiveDeptNumber" -MemberType NoteProperty  -Value $User.extensionattribute5

    $Obj | Add-Member -Name "extensionattribute10" -MemberType NoteProperty  -Value $User.extensionattribute10

    $Obj | Add-Member -Name "lowesEffectivePhyLocationCode" -MemberType NoteProperty  -Value $User.extensionAttribute7

$Table += $obj

}

$Table | export-csv "c:\Temp\ADUsers.csv" -NoTypeInformation




Monday, November 8, 2021

 Installing and Bootstraping an SCCM task sequence during AutoPilot


log-it -message "Starting SCCM Install Script" -component "AP" -path "C:\Temp\" -logname "Company_AP.log"

$ScriptDir = Split-Path $script:MyInvocation.MyCommand.Path

cd $ScriptDir

log-it -message "Working dir is now $ScriptDir" -component "AP" -path "C:\Temp\" -logname "Company_AP.log"


New-Item -Path C:\ -Name "Temp" -ItemType Directory -ErrorAction SilentlyContinue

New-Item -Path C:\Temp -Name "SCCM" -ItemType Directory -ErrorAction SilentlyContinue


log-it -message "PS Policy is now $Policy" -component "AP" -path "C:\Temp\" -logname "Company_AP.log"


Function log-it {

param( $message, $component, $path, $thread = 0, $file = 0 )


 


[string]$time = Get-Date -format "HH:mm:ss.fff+300"

[string]$date = Get-Date -Format "MM-dd-yyyy"


 


$a = "<![LOG["

$b = "]LOG]!>"

$carrot = "<"

$closecarrot = ">"

$c = "time=""$time"" date=""$date"" component=""$component"" context="""" type=""1"" thread=""$thread"" file=""$file"""

$logentry =  $a+$message+$b+$carrot+$c+$closecarrot

#Add-Content -Path c:\temp\SCCM-Install.log -Value $logentry


 


Add-Content -Path $path -Value $logmessage


 


}


log-it -message "Working directory is now $scriptdir" -component "AP-SCCM-Install" -path "C:\Temp\" -logname "Company_AP.log"

$dir = dir

log-it -message $dir -component "AP-SCCM-Install" -path "C:\Temp\" -logname "Company_AP.log"



Copy-Item .\ccmsetup.exe c:\temp\SCCM 

Copy-Item .\CMTrace.exe c:\temp




$script=@'

$ScriptDir = Split-Path $script:MyInvocation.MyCommand.Path

cd $ScriptDir


#If SCCM is installed AND the AutoPilot TS ran

if( (get-service ccmexec -ErrorAction SilentlyContinue) -and (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion").tsname -ne $null)

{

    Disable-ScheduledTask AutoPilotCMClientInstall

}


#TS hasn't ran or didn't finish

else

{

    #Checks if the user is logged in and in an active session

    function Query-User

    {

        $users = (((quser) -ireplace '\s{2,}',',' | ConvertFrom-Csv).username).replace(">","")

        if($users -eq "defaultuser0")

        {

            return $false

        }


        else

        {

            return $true

        }

    }

       

    

    #Checks to make sure machine is connected to VPN

    function Check-VPN

    {

        if ((Test-Connection $localServer -Count 1 -Quiet) -eq $false)

        {

            return $false

        }


        else

        {

            return $true

        }

    }



    $isOnline = Check-VPN

    $isUserLoggedIn = Query-User


    While(($isOnline -eq $false) -or ($isUserLoggedIn -eq $false))

    {

        $isOnline = Check-VPN

        $isUserLoggedIn = Query-User

    

        sleep -Seconds 5


    }


    

    #If TSManager.exe is not running then the TS is not actively running. Okay to install SCCM

    if((ps TSManager -ErrorAction SilentlyContinue) -eq $null)

    {

        Start-Process c:\temp\SCCM\ccmsetup.exe -argumentlist  "/noCRLCheck /mp:CompanyCMG.CLOUDAPP.NET CCMHOSTNAME=CompanyCMG.CLOUDAPP.NET/CCM_Proxy_MutualAuth/72057594037958338 SMSSiteCode=XXX PROVISIONTS=C0220AC4 /forceinstall" 

    }



    sleep -Seconds 600


    #If SCCM is installed AND the AutoPilot TS ran

    if( (get-service ccmexec -ErrorAction SilentlyContinue) -and (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion").tsname -ne $null)

    {

        Disable-ScheduledTask AutoPilotCMClientInstall

    }


}


'@


log-it -message "Creating C:\Temp\SCCM\AutoPilotCMClientInstall.ps1 script." -component "AP-SCCM-Install" -path "C:\Temp\" -logname "Company_AP.log"

Add-Content C:\Temp\SCCM\AutoPilotCMClientInstall.ps1 -Value $script



#Checks if SCCM is already installed. If it is, it will not create the scheduled task.

$isInstalled = gwmi win32_product | ? {$_.Name -eq "Configuration Manager Client"}


if($isInstalled -eq $null)

{

    log-it -message "Creating Scheduled Task." -component "AP-SCCM-Install" -path "C:\Temp\" -logname "Company_AP.log"

    

    $A = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-Ex Bypass -file C:\Temp\SCCM\AutoPilotCMClientInstall.ps1"

    #$T = New-ScheduledTaskTrigger -AtLogOn


    $T = @(

    $(New-ScheduledTaskTrigger -AtLogOn),

    $(New-ScheduledTaskTrigger -Once -At (get-date) -RepetitionInterval (New-TimeSpan -Minutes 1) )

    )


    $P =  New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount -RunLevel Highest

    $S = New-ScheduledTaskSettingsSet –AllowStartIfOnBatteries –DontStopIfGoingOnBatteries -DontStopOnIdleEnd

    $D = New-ScheduledTask -Action $A -Principal $P -Trigger $T -Settings $S

    Register-ScheduledTask AutoPilotCMClientInstall -InputObject $D

}



#Enables UAC visibility through MSRA / Quick Assist

Set-ItemProperty -path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System -name PromptOnSecureDesktop -Value 0 -ErrorAction SilentlyContinue







#Detects if script was successful

$task = (Get-ScheduledTask AutoPilotCMClientInstall -ErrorAction SilentlyContinue).state


if ( ($task -eq "Ready") -or ($task -eq "Running") -or (test-path C:\Windows\CCM\CcmExec.exe) )

{

    log-it -message "SCCM is either detected or the installation TS is enabled and will install it." -component "AP" -path "C:\Temp\" -logname "Company_AP.log"

}


else

{

    log-it -message "Didn't detect task!" -component "AP" -path "C:\Temp\" -logname "Company_AP.log"


}


 

Script to get the current user's AD details.

<#
Script will get the currently logged in user and then search for their AD attributes via ADSI.
The attributes are written to a csv file in c:\temp to be used later. They cannot be directly written to TS variables since this step has to be ran as a user account and it would have its own variable store.
#>

#Step needs to run as an AD account to pull info. Currently runs as _sccmsvc1


$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment

#Get logged in user
$user = (Get-WmiObject -Class Win32_Process -Filter 'Name="explorer.exe"').GetOwner().User

#Get AD User attributes
$searcher = [adsisearcher]"(samaccountname=$user)"
$userTitle = $searcher.FindOne().Properties.title
$userDeptNum = $searcher.FindOne().Properties.extensionattribute5
$userDeptName = $searcher.FindOne().Properties.department
$userLocation = $searcher.FindOne().Properties.extensionattribute7


$userObject = New-Object psobject -Property @{
"currentUserADID" = "$user"
"currentUserTitle" = "$userTitle"
"currentUserDeptNum" = "$userDeptNum"
"currentUserDeptName" = "$userDeptName"
"currentUserLocation" = "$userLocation"
}

$userObject | export-csv c:\temp\userAttributes.csv -NoTypeInformation

 Detect OOBE - Script to see if an AutoPilot device is in OOBE


$ScriptDir = Split-Path $script:MyInvocation.MyCommand.Path -ErrorAction Ignore
cd $ScriptDir -ErrorAction Ignore


$LoggedOnUser = Get-WmiObject win32_computersystem | select Username -ErrorAction Ignore
$user = $LoggedOnUser.Username

$count = 0
while($user -like "")
{
$LoggedOnUser = Get-WmiObject win32_computersystem | select Username -ErrorAction Ignore
$user = $LoggedOnUser.Username
$count++
sleep -Seconds 5

}


If($LoggedOnUser.Username -like "*defaultuser0")
{

Write-Output "OOBE"
exit 0

}


else
{

exit 12345

}

Thursday, October 28, 2021

PowerShell Code to add a PC to an Azure Group



<#
.Synopsis
Add Computers to Azure AD Group
.DESCRIPTION
Add Computers to Azure AD Group.
.EXAMPLE
Create a txt file with the netbios names of devices you want to add. The script invokes a file picker to allow you to choose the file.
.INPUTS
Inputs to this cmdlet (if any) None
.OUTPUTS
Output from this cmdlet (if any) Console
.NOTES
General notes
.COMPONENT
AzureAD#>
###################################################################################
# Adjust these variables accordingly... #
###################################################################################
$azgroup = "BitLocker Settings Assignment Group"

####################################################################################lets check to see if we have the Azure AD module installed...

if (Get-Module -ListAvailable -Name Azuread) {
Write-Host "AzureAD Module exists, loading"
Import-Module Azuread
}


else {
#no module, does user hae admin rights?
Write-Host "AzureAD Module does not exist please install`r`n with install-module azuread" -ForegroundColor Red
if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(`
[Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Host "Insufficient permissions to install module. Please run as an administrator and try again." -ForegroundColor DarkYellow
return(0)
}
else {
Write-Host "Attempting to install Azure AD module" -ForegroundColor Cyan
Install-Module AzureAD -Confirm:$False -Force
}
}# OK, lets pick the file..
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{
InitialDirectory = [Environment]::GetFolderPath('Desktop')
Filter = 'Documents (*.txt)|*.txt|TextFile (*.txt)|*.txt'
}
$null = $FileBrowser.ShowDialog()
$machines = get-content $FileBrowser.FileName
#ok, if we got here, we must have the Azure AD module installed, lets connect...
Connect-AzureAD
write-host "Getting Object ID of group.." -ForegroundColor Green
$objid = (get-azureadgroup -Filter "DisplayName eq '$azgroup'" ).objectid
write-host "Getting group members (We dont want duplicates!).." -ForegroundColor Cyan
$members = Get-AzureADGroupMember -ObjectId $objid -all $true | select displayname
foreach ($machine in $machines) {
$refid = Get-AzureADDevice -Filter "DisplayName eq '$machine'"
Write-host "Adding " $refid.displayname -ForegroundColor Cyan
Add-AzureADGroupMember -ObjectId $objid -RefObjectId $refid.objectid
}