<# .SYNOPSIS CLM-safe "join/deployment type" detector: Entra Joined, Hybrid Entra Joined, Domain Joined, Workplace Joined (AAD Registered), Workgroup. .USAGE .\jointype.ps1 .\jointype.ps1 -AsJson | Out-File .\jointype.json -Encoding utf8 .NOTES Works in ConstrainedLanguage mode (no [pscustomobject] casts). #> [CmdletBinding()] param( [switch]$AsJson ) function Get-ValueFromText { param( [Parameter(Mandatory)] [string]$Text, [Parameter(Mandatory)] [string]$Key ) $m = [regex]::Match($Text, "(?m)^\s*$([regex]::Escape($Key))\s*:\s*(.+?)\s*$") if ($m.Success) { return $m.Groups[1].Value.Trim() } return $null } function Get-DSRegStatus { $result = @{ DsregcmdAvailable = $false AzureAdJoined = $null DomainJoined = $null EnterpriseJoined = $null WorkplaceJoined = $null TenantName = $null TenantId = $null DeviceId = $null AzureAdPrt = $null WamDefaultSet = $null MdmUrl = $null MdmTouUrl = $null } $dsreg = Get-Command dsregcmd.exe -ErrorAction SilentlyContinue if (-not $dsreg) { return $result } try { $raw = & $dsreg.Source /status 2>$null | Out-String if (-not $raw) { return $result } $result.DsregcmdAvailable = $true $result.AzureAdJoined = Get-ValueFromText -Text $raw -Key "AzureAdJoined" $result.DomainJoined = Get-ValueFromText -Text $raw -Key "DomainJoined" $result.EnterpriseJoined = Get-ValueFromText -Text $raw -Key "EnterpriseJoined" $result.WorkplaceJoined = Get-ValueFromText -Text $raw -Key "WorkplaceJoined" $result.TenantName = Get-ValueFromText -Text $raw -Key "TenantName" $result.TenantId = Get-ValueFromText -Text $raw -Key "TenantId" $result.DeviceId = Get-ValueFromText -Text $raw -Key "DeviceId" $result.AzureAdPrt = Get-ValueFromText -Text $raw -Key "AzureAdPrt" $result.WamDefaultSet = Get-ValueFromText -Text $raw -Key "WamDefaultSet" $result.MdmUrl = Get-ValueFromText -Text $raw -Key "MdmUrl" $result.MdmTouUrl = Get-ValueFromText -Text $raw -Key "MdmTouUrl" } catch { # keep defaults } return $result } function Get-MdmEnrollmentHint { $hint = @{ MdmEnrolled = $false EnrollmentUpn = $null EnrollmentId = $null EnrollmentType = $null EnrollmentKeyPathSample = $null } $base = "HKLM:\SOFTWARE\Microsoft\Enrollments" if (-not (Test-Path $base)) { return $hint } try { foreach ($k in (Get-ChildItem $base -ErrorAction SilentlyContinue)) { try { $p = Get-ItemProperty -Path $k.PSPath -ErrorAction Stop # Try to find a key that looks meaningful $state = $p.EnrollmentState $upn = $p.UPN $etype = $p.EnrollmentType $eid = $p.EnrollmentID if ($state -ne $null -or $upn -or $etype -ne $null -or $eid) { $hint.MdmEnrolled = $true $hint.EnrollmentUpn = $upn $hint.EnrollmentId = $eid $hint.EnrollmentType = $etype $hint.EnrollmentKeyPathSample = $k.PSPath break } } catch { } } } catch { } return $hint } function Get-AutopilotHint { $paths = @( "HKLM:\SOFTWARE\Microsoft\Provisioning\Autopilot", "HKLM:\SOFTWARE\Microsoft\Windows\Autopilot", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\CloudExperienceHost\Autopilot", "HKLM:\SOFTWARE\Microsoft\Provisioning\Diagnostics\AutoPilot" ) $found = $null foreach ($p in $paths) { if (Test-Path $p) { $found = $p; break } } return @{ AutopilotHint = [bool]$found EvidencePath = $found } } function Get-SccmHint { $svc = Get-Service -Name CcmExec -ErrorAction SilentlyContinue $present = $false $status = $null if ($svc) { $present = $true try { $status = "$($svc.Status)" } catch { $status = "Unknown" } } return @{ SccmClientPresent = $present SccmServiceStatus = $status } } function To-BoolYes { param([string]$Value) return ($Value -eq "YES") } function Print-Section { param( [string]$Title, $Obj, [int]$Indent = 0 ) $pad = (" " * $Indent) Write-Host "" Write-Host ("{0}=== {1} ===" -f $pad, $Title) -ForegroundColor Cyan if ($Obj -is [hashtable]) { foreach ($k in ($Obj.Keys | Sort-Object)) { $v = $Obj[$k] if ($v -is [hashtable]) { Write-Host ("{0}{1}:" -f $pad, $k) Print-Section -Title "" -Obj $v -Indent ($Indent + 2) } elseif ($v -is [System.Collections.IEnumerable] -and -not ($v -is [string])) { Write-Host ("{0}{1}: {2}" -f $pad, $k, (($v | ForEach-Object { "$_" }) -join "; ")) } else { Write-Host ("{0}{1}: {2}" -f $pad, $k, $v) } } } else { Write-Host ("{0}{1}" -f $pad, $Obj) } } # --- Collect signals --- $ds = Get-DSRegStatus $mdm = Get-MdmEnrollmentHint $ap = Get-AutopilotHint $sccm = Get-SccmHint # Basic system info (safe even in CLM) $cs = $null; $os = $null; $bios = $null try { $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction SilentlyContinue } catch { } try { $os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction SilentlyContinue } catch { } try { $bios = Get-CimInstance -ClassName Win32_BIOS -ErrorAction SilentlyContinue } catch { } $azure = To-BoolYes $ds.AzureAdJoined $dom = To-BoolYes $ds.DomainJoined $wkp = To-BoolYes $ds.WorkplaceJoined $joinType = if ($azure -and $dom) { "Hybrid Entra Joined (Domain + Entra/Azure AD)" } elseif ($azure -and -not $dom) { "Entra Joined (Azure AD Joined)" } elseif (-not $azure -and $dom) { "AD Domain Joined (On-prem only)" } elseif ($wkp -and -not $azure -and -not $dom) { "Workplace Joined / AAD Registered" } else { "Workgroup / Not Joined" } $management = @() if ($mdm.MdmEnrolled -or $ds.MdmUrl -or $ds.MdmTouUrl) { $management += "MDM/Intune (enrollment detected)" } if ($sccm.SccmClientPresent) { $management += "ConfigMgr/SCCM client (CcmExec present)" } if ($dom) { $management += "Group Policy likely (domain-joined)" } if (-not $management -or $management.Count -eq 0) { $management = @("No management signals detected (best-effort)") } $report = @{ Summary = @{ JoinType = $joinType ManagementSignals = $management AutopilotHint = $ap.AutopilotHint } Device = @{ ComputerName = $env:COMPUTERNAME Manufacturer = if ($cs) { $cs.Manufacturer } else { $null } Model = if ($cs) { $cs.Model } else { $null } SerialNumber = if ($bios) { $bios.SerialNumber } else { $null } } OS = @{ Caption = if ($os) { $os.Caption } else { $null } Version = if ($os) { $os.Version } else { $null } BuildNumber = if ($os) { $os.BuildNumber } else { $null } } AD = @{ PartOfDomain = if ($cs) { [bool]$cs.PartOfDomain } else { $null } DomainOrWorkgroup = if ($cs) { if ($cs.PartOfDomain) { $cs.Domain } else { $cs.Workgroup } } else { $null } } EntraDsreg = $ds MDM = $mdm Autopilot = $ap SCCM = $sccm } if ($AsJson) { $report | ConvertTo-Json -Depth 6 return } # --- Output --- Write-Host "" Write-Host "=== Deployment / Join Type ===" -ForegroundColor Cyan Write-Host ("JoinType: {0}" -f $report.Summary.JoinType) Write-Host ("Management: {0}" -f ($report.Summary.ManagementSignals -join "; ")) Write-Host ("Autopilot hint: {0}" -f $report.Summary.AutopilotHint) Print-Section -Title "AD / Workgroup" -Obj $report.AD Print-Section -Title "Entra (dsregcmd /status)" -Obj $report.EntraDsreg Print-Section -Title "MDM / Intune hint" -Obj $report.MDM Print-Section -Title "Autopilot hint" -Obj $report.Autopilot Print-Section -Title "SCCM hint" -Obj $report.SCCM Write-Host "" Write-Host "LanguageMode: $($ExecutionContext.SessionState.LanguageMode)" -ForegroundColor DarkGray