SARA.ps1
# This script contains functions used in Microsoft Support and Recovery Assistant (SARA) # Sep 23rd 2021 function Get-SARAUserInfo { <# .SYNOPSIS Gets user information using SARA API .DESCRIPTION Gets user information using Microsoft Support and Recovery Assistant (SARA) API .Parameter AccessToken Access Token .Example $at=Get-AADIntAccessTokenForSARA PS C:\>Get-AADIntSARAUserInfo -AccessToken $at AnalyzerName : AnalysisRule, Microsoft.Online.CSE.HRC.Analysis.Analyzers.ExchangeCmdlets.GetUserAnalyzer, Microsoft.Online.CSE.HRC.Analysis.Analyzers.ExchangeCmdlets, Version=16.0.3144.0, Culture= neutral, PublicKeyToken=31bf3856ad364e35 AnalyzerDesc : Attempting to get information about user "user@company.com". StartTime : 2019-07-08T12:29:40.4911399Z Duration : 00:00:51.1166849 CoreDuration : 00:00:51.1166849 WaitingDuration : 00:00:00 TotalChildrenDuration : 00:00:00 TotalWaitingDuration : 00:00:00 ParentId : 00000000-0000-0000-0000-000000000000 Value : true ResultTitle : Extracting information about Office 365 user is completed. ResultTitleId : Microsoft.Online.CSE.HRC.Analysis.Analyzers.ExchangeCmdlets.StringsGetUserComplete UserMessage : Successfully got the user information for "user@company.com". UserMessageId : Microsoft.Online.CSE.HRC.Analysis.Analyzers.ExchangeCmdlets.StringsGetUserSuccessDesc AdminMessage : SupportMessage : IsMessageShown : False GenericInfo : Severity : 2 OverridesChildren : False ProblemId : 00000000-0000-0000-0000-000000000000 TimeCached : 0001-01-01T00:00:00 SaraSymptomId : 00000000-0000-0000-0000-000000000000 SaraWorkflowRunId : 00000000-0000-0000-0000-000000000000 SaraSymptomRunId : 00000000-0000-0000-0000-000000000000 SaraSessionId : 00000000-0000-0000-0000-000000000000 Id : d5b4c239-7619-4367-9ccb-e9fe2fe01e23 DisplayName : Demo USer FirstName : Demo Guid : 67a93665-decb-4058-b42a-271d41c47c61 Id : Identity : EURP185A001.PROD.OUTLOOK.COM/Microsoft Exchange Hosted Organizations/demoo365life4.onmicrosoft.com/AdminO365life IsDirSynced : False IsValid : True LastName : User MicrosoftOnlineServicesID : user@company.com Name : DemoUser NetID : 401320004BA7A415 RecipientType : UserMailbox RecipientTypeDetails : UserMailbox UserPrincipalName : user@company.com WindowsEmailAddress : user@company.com WindowsLiveID : user@company.com IsHybridTenant : False Forest : EURP185.PROD.OUTLOOK.COM #> [cmdletbinding()] Param( [Parameter(Mandatory=$False)] [String]$AccessToken, [Parameter(Mandatory=$False)] [String]$UserName, [Parameter(Mandatory=$False)] [ValidateSet('NotSet','HrcCloud','HrcCmd','Sara','MsftSupportModeSara','SaraCloud','QTest')] [String]$ExecutionEnvironment='SaraCloud' ) Begin { } Process { # Get from cache if not provided $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c" -Resource "https://api.diagnostics.office.com" if(!$UserName) { $userName = (Read-Accesstoken $AccessToken).upn } $userInformation = [ordered]@{"UserName" = $UserName} # # TenantUserInfo # Write-Verbose "TenantUserInfo" $body=@{ "Symptom" = "TenantUserInfo" "RequestTimeoutInMs" = 180000 "Parameters" = @( @{ "Name" = "AffectedUser" "Value" = $UserName #.Split("@")[1] "ComplianceClassification" = "Identifiable" } @{ "Name" = "Symptom" "Value" = "TenantUserInfo" "ComplianceClassification" = "Identifiable" } @{ "Name" = "ScenarioSymptom" "Value" = "TenantUserInfo" "ComplianceClassification" = "Identifiable" } @{ "Name" = "UserPuid" "Value" = "" "ComplianceClassification" = "Identifiable" } @{ "Name" = "CorrelationId" "Value" = "" "ComplianceClassification" = "Identifiable" } @{ "Name" = "TargetService" "Value" = "Exchange" "ComplianceClassification" = "Identifiable" } @{ "Name" = "IsMsaUser" "Value" = $False "ComplianceClassification" = "Identifiable" } @{ "Name" = "MailboxClient" "Value" = "Outlook" "ComplianceClassification" = "Identifiable" } @{ "Name" = "TestHook" "Value" = "" "ComplianceClassification" = "Identifiable" } ) } $response = Call-AnalysisAPI -Body ($body | ConvertTo-Json) -AccessToken $AccessToken -Url "https://api.diagnostics.office.com/v1/cloudcheck" if($response.ProcessingStatus -eq "Succeeded") { $additionalInfo = $response.AdditionalInfo | ConvertFrom-Json if($additionalInfo.IsSuccess -eq "true") { $item = ([xml]$additionalInfo.TenantUserInfo).TenantUserInfo.FirstChild while($item) { if($item.name -eq "LicenseInformations") { $userInformation[$item.Name] = $item.InnerXml } else { $userInformation[$item.Name] = $item.InnerText } $item = $item.NextSibling } } } # # CasMailbox # Write-Verbose "CasMailBox" $body=@{ "Symptom" = "CasMailbox" "RequestTimeoutInMs" = 180000 "Parameters" = @( @{ "Name" = "AffectedUser" "Value" = $UserName "ComplianceClassification" = "Identifiable" } @{ "Name" = "MailboxClient" "Value" = "Outlook" "ComplianceClassification" = "Identifiable" } @{ "Name" = "Symptom" "Value" = "CasMailbox" "ComplianceClassification" = "Identifiable" } @{ "Name" = "ScenarioSymptom" "Value" = "CasMailbox" "ComplianceClassification" = "Identifiable" } ) } $response = Call-AnalysisAPI -Body ($body | ConvertTo-Json) -AccessToken $AccessToken -Url "https://api.diagnostics.office.com/v1/cloudcheck" if($response.ProcessingStatus -eq "Succeeded") { $userInformation["CASInfo"] = $response.MessageToUser } # # GetUserDiagnostic # Write-Verbose "GetUserDiagnostic" $body=@{ "UserUpn" = $parsedToken.upn "UserSMTPEmail" = $parsedToken.upn "Symptom" = "GetUserDiagnostic" "RequestTimeoutInMs" = 180000 "Parameters" = @( @{ "Name" = "AffectedUser" "Value" = $UserName "ComplianceClassification" = "Identifiable" } @{ "Name" = "Symptom" "Value" = "GetUserDiagnostic" "ComplianceClassification" = "Identifiable" } @{ "Name" = "ScenarioSymptom" "Value" = "GetUser" "ComplianceClassification" = "Identifiable" } ) } $response = Call-AnalysisAPI -Body ($body | ConvertTo-Json) -AccessToken $AccessToken -Url "https://api.diagnostics.office.com/v1/cloudcheck" if($response.ProcessingStatus -eq "Succeeded") { $additionalInfo = $response.AdditionalInfo | ConvertFrom-Json if($additionalInfo.IsSuccess -eq "true") { $userInfo = [xml]$additionalInfo.UserInfo $item = $userInfo.UserInfo.FirstChild while($item) { $userInformation[$item.Name] = $item.InnerText $item = $item.NextSibling } } else { Write-Warning $additionalInfo.ErrorInfo.Split("`n")[0] } } New-Object psobject -Property $userInformation } } # Sep 23rd 2021 function Get-SARATenantInfo { [cmdletbinding()] Param( [Parameter(ParameterSetName='AccessToken', Mandatory=$False)] [String]$AccessToken, [Parameter(Mandatory=$False)] [String]$UserName, [Parameter(Mandatory=$False)] [ValidateSet('ExchangeHybridTenant','DirSyncCheck')] [String[]]$Tests=@('ExchangeHybridTenant','DirSyncCheck') ) Begin { } Process { # Get from cache if not provided $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c" -Resource "https://api.diagnostics.office.com" $parsedToken = Read-Accesstoken $AccessToken if(!$UserName) { $userName = $parsedToken.upn } $tenantInfo = [ordered]@{ "Domain" = $UserName.Split("@")[1] } # # ExchangeHybridTenant Check # if($Tests -contains "ExchangeHybridTenant") { Write-Verbose "ExchangeHybridTenant" $body=@{ "Symptom" = "ExchangeHybridTenant" "RequestTimeoutInMs" = 180000 "Parameters" = @( @{ "Name" = "AffectedUser" "Value" = $UserName "ComplianceClassification" = "Identifiable" } @{ "Name" = "ExchangeHybridTenantClient" "Value" = "OutlookFreeBusy" "ComplianceClassification" = "Identifiable" } @{ "Name" = "Symptom" "Value" = "ExchangeHybridTenant" "ComplianceClassification" = "Identifiable" } @{ "Name" = "ScenarioSymptom" "Value" = "ExchangeHybridTenant" "ComplianceClassification" = "Identifiable" } ) } $response = Call-AnalysisAPI -Body ($body | ConvertTo-Json) -AccessToken $AccessToken -Url "https://api.diagnostics.office.com/v1/cloudcheck" if($response.AdditionalInfo) { $additionalInfo = ($response.AdditionalInfo | ConvertFrom-Json) if($additionalInfo.Category -eq "S") { $hybridInfoXML = ([xml]($response.AdditionalInfo | ConvertFrom-Json).OrganizationRelationShipInfo).HybridInfo $orgRels = @() $onPrems = @() foreach($rel in $hybridInfoXML.OrganizationRelationShips.OrganizationRelationShip) { $orgRels += New-Object psobject -Property @{ "FreeBusyAccessLevel" = $rel.FreeBusyAccessLevel "FreeBusyEnabled" = $rel.FreeBusyEnabled "Identity" = $rel.Identity "IsValid" = $rel.IsValid } } foreach($rel in $hybridInfoXML.OnPremOrganizationRelationShips.OnPremOrganizationRelationShip) { $onPrems += New-Object psobject -Property @{ "FreeBusyAccessLevel" = $rel.FreeBusyAccessLevel "OrganizationGuid" = $rel.OrganizationGuid "OrganizationName" = $rel.OrganizationName "OrganizationRelationship" = $rel.OrganizationRelationship } } $tenantInfo["IsHybrid"] = $additionalInfo.isHybrid $tenantInfo["OrganizationRelationShips"] = $orgRels $tenantInfo["OnPremOrganizationRelationShips"] = $onPrems } else { Write-Warning "ExchangeHybridTenant error $($additionalInfo.ScenarioResultName)" } } } # # DirSyncCheck # if($Tests -contains "DirSyncCheck") { Write-Verbose "DirSyncCheck" $body=@{ "Symptom" = "DirSyncCheck" "RequestTimeoutInMs" = 180000 "Parameters" = @( @{ "Name" = "TenantDomain" "Value" = $UserName.Split("@")[1] "ComplianceClassification" = "Identifiable" } @{ "Name" = "Symptom" "Value" = "DirSyncCheck" "ComplianceClassification" = "Identifiable" } @{ "Name" = "ScenarioSymptom" "Value" = "DirSyncCheck" "ComplianceClassification" = "Identifiable" } ) } $response = Call-AnalysisAPI -Body ($body | ConvertTo-Json) -AccessToken $AccessToken -Url "https://api.diagnostics.office.com/v1/cloudcheck" $tenantInfo["DirSync"] = $response.MessageToAdmin } # Return New-Object psobject -Property $tenantInfo } } # Sep 23rd 2021 function Get-SARAFreeBusyInformation { [cmdletbinding()] Param( [Parameter(Mandatory=$False)] [String]$AccessToken, [Parameter(Mandatory=$False)] [String]$UserName, [Parameter(Mandatory=$True)] [String]$TargetUser ) Begin { } Process { # Get from cache if not provided $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c" -Resource "https://api.diagnostics.office.com" if(!$UserName) { $userName = (Read-Accesstoken $AccessToken).upn } # # FreeBusyTenantUserInfo # Write-Verbose "FreeBusyTenantUserInfo" $body=@{ "Symptom" = "FreeBusyTenantUserInfo" "RequestTimeoutInMs" = 180000 "Parameters" = @( @{ "Name" = "AffectedUser" "Value" = $UserName "ComplianceClassification" = "Identifiable" } @{ "Name" = "Symptom" "Value" = "FreeBusyTenantUserInfo" "ComplianceClassification" = "Identifiable" } @{ "Name" = "ScenarioSymptom" "Value" = "FreeBusyTenantUserInfo" "ComplianceClassification" = "Identifiable" } @{ "Name" = "TargetSmtpAddress" "Value" = $TargetUser "ComplianceClassification" = "Identifiable" } ) } $response = Call-AnalysisAPI -Body ($body | ConvertTo-Json) -AccessToken $AccessToken -Url "https://api.diagnostics.office.com/v1/cloudcheck" if($response.ProcessingStatus -eq "Succeeded") { $additionalInfo = $response.AdditionalInfo | ConvertFrom-Json if($additionalInfo.IsSuccess -eq "true") { return $response.MessageToUser } else { Write-Error $response.MessageToUser } } } } # Aug 30th 2023 # Uses SARA to test if provided port is available function Test-SARAPort { <# .SYNOPSIS Tests whether the given TCP port is open on the given host using SARA API. .DESCRIPTION Tests whether the given TCP port is open on the given host using SARA API. .Parameter AccessToken Access Token .PARAMETER Host Hostname or IP address of the target .PARAMETER Port TCP port number .Example Get-AADIntAccessTokenForSARA -SaveToCache PS C:\>Test-AADIntSARAPort -Host www.company.com -Port 443 Host Port Open ---- ---- ---- www.company.com 443 True #> [cmdletbinding()] Param( [Parameter(Mandatory=$False)] [String]$AccessToken, [Parameter(Mandatory=$True)] [String]$Host, [Parameter(Mandatory=$True)] [String]$Port ) Begin { } Process { # Get from cache if not provided $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c" -Resource "https://api.diagnostics.office.com" $body=@{ "Symptom" = "PortCheck" "RequestTimeoutInMs" = 180000 "Parameters" = @( @{ "Name" = "HostName" "Value" = $Host "ComplianceClassification" = "Identifiable" } @{ "Name" = "Symptom" "Value" = "PortCheck" "ComplianceClassification" = "Identifiable" } @{ "Name" = "Port" "Value" = $Port.ToString() "ComplianceClassification" = "Identifiable" } @{ "Name" = "IsReadBanner" "Value" = $true "ComplianceClassification" = "Identifiable" } @{ "Name" = "ScenarioSymptom" "Value" = "Port" "ComplianceClassification" = "Identifiable" } ) "UseDiagnosticService" = $true } $response = Call-AnalysisAPI -Body ($body | ConvertTo-Json) -AccessToken $AccessToken -Url "https://api.diagnostics.office.com/v1/cloudcheck" $isOpen = $false if($response.ProcessingStatus -eq "Succeeded") { $additionalInfo = $response.AdditionalInfo | ConvertFrom-Json $isOpen = $additionalInfo.IsSuccess -eq "true" } [pscustomobject][ordered]@{ "Host" = $Host "Port" = $Port "Open" = $isOpen } } } # Aug 30th 2023 # Uses SARA to resolve DNS name function Resolve-SARAHost { <# .SYNOPSIS Tests whether the given hostname can be resolved from DNS using SARA API. .DESCRIPTION Tests whether the given hostname can be resolved from DNS using SARA API. .Parameter AccessToken Access Token .PARAMETER Host Hostname of the target .Example Get-AADIntAccessTokenForSARA -SaveToCache PS C:\>Resolve-AADIntSARAHost -Host www.company.com Host Resolved ---- -------- www.company.com True #> [cmdletbinding()] Param( [Parameter(Mandatory=$False)] [String]$AccessToken, [Parameter(Mandatory=$False)] [String]$Host ) Begin { } Process { # Get from cache if not provided $AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientId "d3590ed6-52b3-4102-aeff-aad2292ab01c" -Resource "https://api.diagnostics.office.com" $body=@{ "Symptom" = "ResolveHostCheck" "RequestTimeoutInMs" = 180000 "Parameters" = @( @{ "Name" = "HostName" "Value" = $Host "ComplianceClassification" = "Identifiable" } @{ "Name" = "Symptom" "Value" = "ResolveHostCheck" "ComplianceClassification" = "Identifiable" } ) "UseDiagnosticService" = $true } $response = Call-AnalysisAPI -Body ($body | ConvertTo-Json) -AccessToken $AccessToken -Url "https://api.diagnostics.office.com/v1/cloudcheck" $isResolved = $false if($response.ProcessingStatus -eq "Succeeded") { $isResolved = $response.MessageTitle.StartsWith("We succeeded") } [pscustomobject][ordered]@{ "Host" = $Host "Resolved" = $isResolved } } } |