M365ST.TeamsCalendarMon.Serial.PA

M365ST.TeamsCalendarMon.Serial.PA (ProbeActionModuleType)

Designed to leverage a Posh script PA for retrieving properties/metrics.

Element properties:

TypeProbeActionModuleType
IsolationAny
AccessibilityInternal
RunAsM365SL.RunAs.Profile
InputTypeSystem.BaseData
OutputTypeMicrosoft.Windows.SerializedObjectData

Member Modules:

ID Module Type TypeId RunAs 
POSH ProbeAction Microsoft.Windows.PowerShellProbe Default

Overrideable Parameters:

IDParameterTypeSelector
EventIDFilterstring$Config/EventIDFilter$
PoshLibraryPathstring$Config/PoshLibraryPath$
ProbeActionTimeoutSecondsint$Config/ProbeActionTimeoutSeconds$
WriteToEventLogbool$Config/WriteToEventLog$

Source Code:

<ProbeActionModuleType ID="M365ST.TeamsCalendarMon.Serial.PA" Accessibility="Internal" Batching="false" PassThrough="false" RunAs="M365SL!M365SL.RunAs.Profile">
<Configuration>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="ApiTokenScopeURL" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="ApiTokenURL" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="ApiURL" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="M365_AccountName" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="M365_AccountPassword" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="M365_ClientID" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="M365_ClientSecret" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="EventIDFilter" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="PoshLibraryPath" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="ProbeActionTimeoutSeconds" type="xsd:integer"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="TeamName" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="TenantName" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="TLSVersion" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="WorkflowName" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="WriteToEventLog" type="xsd:boolean"/>
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="EventIDFilter" Selector="$Config/EventIDFilter$" ParameterType="string"/>
<OverrideableParameter ID="PoshLibraryPath" Selector="$Config/PoshLibraryPath$" ParameterType="string"/>
<OverrideableParameter ID="ProbeActionTimeoutSeconds" Selector="$Config/ProbeActionTimeoutSeconds$" ParameterType="int"/>
<OverrideableParameter ID="WriteToEventLog" Selector="$Config/WriteToEventLog$" ParameterType="bool"/>
</OverrideableParameters>
<ModuleImplementation Isolation="Any">
<Composite>
<MemberModules>
<ProbeAction ID="POSH" TypeID="Windows!Microsoft.Windows.PowerShellProbe">
<ScriptName>M365ST.TeamsCalendarMon.ps1</ScriptName>
<ScriptBody><Script>&lt;#
#===============================================================================================================================================================
Created by: Tyson Paul, Taylour Blackwell
Filename: M365ST.TeamsCalendarMon.ps1
Description: This will post a message and a reply to that message to verify Teams functionality/access.

Version History:
2021.10.20.1611 - v1

Requirements: Must have an Azure application registered and have the following:
Permissions- Group.ReadWrite.All / Calendars.Read

===============================================================================================================================================================
#&gt;

Param(
[string]$ApiTokenScopeURL,
[string]$ApiTokenURL,
[string]$ApiURL,
[string]$M365_AccountName,
[string]$M365_AccountPassword,

# Azure Application auth
[string]$M365_ClientID,
[string]$M365_ClientSecret,

# Comma-separated list of event IDs, script will only write to log for these EventIDs. This is a way to only write specific events. Only valid if $WriteToEventLog parameter is 'true'.
[string]$EventIDFilter,

# Comma-separated list of .ps1 files to load
[string]$PoshLibraryPath,

[string]$TeamName,

# This is a clever way to utilize the exact same script for both rule/mon and agent tasks.
[Parameter(Mandatory=$false,
ValueFromPipeline=$false,
ValueFromPipelineByPropertyName=$false,
ValueFromRemainingArguments=$false)]
[ValidateSet('PropertyBag', 'Serialized')]
[string]$ScriptOutputType = 'PropertyBag',

# Azure tenant
[string]$TenantName,

[string]$TLSVersion,

# Typically this is the name of the workflow calling this script. Should be set to the name of the probe/WA if being used by both rules/mons so as not to break cookdown.
# Keep in mind that datasource params need to be identical for cookdown to work.
[string]$WorkflowName,

# type:string. SCOM bool params are different than Posh bool. Will get converted to Posh bool below.
[string]$WriteToEventLog = 'false'
)

# Set defaults for event filters. Apparently setting this in the Params declaration did not work.
If (-NOT $EventIDFilter) {
$EventIDFilter = "9990,9991,9992,9995,9996,9997,9998,9999"
}

$ScriptName = 'M365ST.TeamsCalendarMon.ps1'
$NameSpace = 'Teams'
[bool]$WriteToEventLog = [System.Convert]::ToBoolean($WriteToEventLog)
$Testing = $false

Function Load-Library {
Param (
[string]$PoshLibraryPath
)
$ErrorActionPreference = 'STOP'
If (-NOT $NameSpace.Length) {
$NameSpace = $ScriptName
}
$EventSource = "M365 Supplemental"
$EventLogName = "Application"
$objEvent = New-Object System.Diagnostics.EventLog
$objEvent.Source = $EventSource
$objEvent.Log = $EventLogName
$EventID_Normal = 9992
$EventID_Anomaly = 9996
[Int]$info=4 #System.Diagnostics.EventInstance
[Int]$critical=1
[Int]$warn=2

If ($PoshLibraryPath ){
Start-Sleep -Seconds 5 #Allow time for Library to be deployed
ForEach ($Path in $PoshLibraryPath.Split(',') ){
Try {
If (($Path.Length) -AND ($Path -notmatch '^-1$')) {
. $Path
If ([bool]$WriteToEventLog -eq $true) {
$msg = "Line [$($MyInvocation.ScriptLineNumber)], $($MyInvocation.PSCommandPath): Success loading library file: [$($Path)]"
$logData = "$($Msg)^$($Msg)^$($ScriptName)^$($TenantName)^$($NameSpace)"
[array]$arrMessage = @($logData.Split('^'))
$objEventID = New-Object System.Diagnostics.EventInstance($EventID_Normal,1,$info)
$objEvent.WriteEvent($objEventID, @($arrMessage))
}
}
} Catch {
[bool]$LibExists =$false
Try {
# It's possible the libfile exists (as it should) but cannot be loaded for some reason.
[bool]$LibExists = [bool](Test-Path -Path $Path -PathType Leaf)
} Catch {
#File does not exist. LibExists = $false already by default
}
$msg = "Line [$($MyInvocation.ScriptLineNumber )]: Error loading PoshLibrary at path:[$($Path)]. Library file exists? [$($LibExists)]. This is likely to cause many other dependent functions to fail. `n`nError data: $($_)`n`n"
$logData = "$($Msg)^$($Msg)^$($ScriptName)^$($TenantName)^$($NameSpace)"
[array]$arrMessage = @($logData.Split('^'))
$objEventID = New-Object System.Diagnostics.EventInstance($EventID_Anomaly,1,$warn)
$objEvent.WriteEvent($objEventID, @($arrMessage))
}
}
}
$ErrorActionPreference = 'CONTINUE'
}
################################################################

############## TESTING ##############
&lt;# #Run this as needed when testing

Function Testing {
$Testing = $true
Get-Item Alias:\_LINE_ -ErrorAction Ignore | Remove-Item -ErrorAction Ignore
$testParamsFile = (Join-path $TestFolder ("PARAMS_$($ScriptName)"))
#Write-Host "$(Test-Path $testParamsFile):$($testParamsFile)" -F Yellow -B Green
. $testParamsFile
. Load-Library -PoshLibraryPath $PoshLibraryPath

# Encode user data/passwords in current test user context
$M365_ClientSecret = Encode-UserData $M365_ClientSecret_PLAINTEXT
$M365_AccountPassword = Encode-UserData $M365_AccountPassword_PLAINTEXT
$error.Clear()
}

$ThisScriptFullName = $MyInvocation.MyCommand.Path
$TestFolder = Join-Path (Split-Path $ThisScriptFullName -Parent) "TestSetup"
If (Test-Path -Path $TestFolder) {
. Testing
}

#&gt;
############## TESTING ##############

. Load-Library -PoshLibraryPath $PoshLibraryPath
LogIt -EventID 9990 -Type $info -Msg "Begin script..." -Proceed $WriteToEventLog -LINE $(_LINE_); $Error.Clear()

# Set up BasicBag with defaults
$bag = New-Object -TypeName BasicBag
$bag.AddValue('Category',"CalendarTest")
$bag.AddValue('Message',"")
$bag.AddValue('Result',"UNKNOWN") #default, will be altered upon success/failure
$bag.AddValue('TeamName',"$TeamName")
$bag.AddValue('EventId','')
$bag.AddValue('CreateEventDurationMS', [double]0.00)
$bag.AddValue('DeleteEventDurationMS',[double]0.00)
$bag.AddValue('TotalDurationMS',[double]0.00)
$bag.AddValue('ThisScriptInstanceGUID',"$ThisScriptInstanceGUID")
$bag.AddValue('Whoami',"$whoami")
$bag.AddValue('M365_ClientID',"$M365_ClientID")
$bag.AddValue('M365_AccountName',"$M365_AccountName")
$bag.AddValue('TenantName',"$TenantName")

LogIt -EventID 9992 -Type $info -msg "Setting TLS for session, Version: [$($TLSVersion)]." -Proceed $WriteToEventLog -Line $(_LINE_); $Error.Clear();
#Set Default TLS
. Set-TLS -TLSVersion $TLSVersion

# Decode Account Password
$Message = "Decode password for [$($NameSpace)]."
LogIt -EventID 9992 -Type $info -Msg $Message -Proceed $WriteToEventLog -LINE $(_LINE_); $Error.Clear()
Try {
$M365_AccountPassword_DECRYPTED = Decode-UserData -Data $M365_AccountPassword
} Catch {
$Message += "Unable to $($Message) See error data."
LogIt -EventID 9995 -Type $warn -Msg $Message -Proceed $true -LINE $(_LINE_); $Error.Clear()
$bag.Message += " $Message"
}

# Decode Client Secret
$Message = "Decode client secret for [$($NameSpace)]."
LogIt -EventID 9992 -Type $info -Msg $Message -Proceed $WriteToEventLog -LINE $(_LINE_); $Error.Clear()
Try {
$M365_ClientSecret_DECRYPTED = Decode-UserData -Data $M365_ClientSecret
} Catch {
$Message += "Unable to $($Message) See error data."
LogIt -EventID 9995 -Type $warn -Msg $Message -Proceed $true -LINE $(_LINE_); $Error.Clear()
$bag.Message += " $Message"
}

# Get Access Token
$Message = "Get Access Token for [$($NameSpace)]."
LogIt -EventID 9992 -Type $info -Msg $Message -Proceed $WriteToEventLog -LINE $(_LINE_); $Error.Clear()
Try {
$TokenResponse = Get-AccessToken -Delegated -User $M365_AccountName -Pass $M365_AccountPassword_DECRYPTED -ClientID $M365_ClientID -ClientSecret $M365_ClientSecret_DECRYPTED -TenantName $TenantName -ApiTokenUrl $ApiTokenURL -ApiTokenScopeURL $ApiTokenScopeURL
} Catch {
$Message = "Unable to $($Message) See error data. Exiting."
LogIt -EventID 9997 -Type $warn -Msg $Message -Proceed $true -LINE $(_LINE_); $Error.Clear()
$bag.Message += " $Message"
# Monitor type supports Critical CD for token failure.
Output $bag -Tag 'TokenFailure'
Exit
}



# Retrieve Team
$Activity = "attempt to retrieve $($NameSpace) object with TeamName: $($TeamName). "
LogIt -EventID 9992 -Type $info -Msg $Activity -Proceed $WriteToEventLog -LINE $(_LINE_); $Error.Clear()

try{
$GetTeamApiURL = "$($ApiURL)/v1.0/me/joinedTeams"
$GetTeamResult = Invoke-RestMethod -Headers @{Authorization = "Bearer $($Tokenresponse.access_token)"} -Uri $GetTeamApiURL -Method Get -ContentType 'application/json' -ErrorAction Stop
$objTeam = $GetTeamResult.value | Where-Object {$_.displayName -eq $TeamName}
If (-NOT $objTeam) {throw}
$Message = "Successful $Activity."
$bag.Message = $Message
}
Catch
{
$Message = "Failed $Activity See error data. Verify that the correct permissions have been granted to the App Registration and that Admin Consent has been granted to all permissions, regardless of if consent is 'required'."
LogIt -EventID 9992 -Type $info -Msg $Message -Proceed $WriteToEventLog -LINE $(_LINE_); $Error.Clear()
$bag.Message = $Message
$bag.Result = 'FAILURE'
Output $bag -Tag 'GetTeamFailure'
Exit
}



# Create Test Event
$Activity = "attempt to create $($NameSpace) calendar test event for TeamName [$($TeamName)]. "
LogIt -EventID 9992 -Type $info -Msg $Activity -Proceed $WriteToEventLog -LINE $(_LINE_); $Error.Clear()
try {
$now = (Get-Date)
$EventStartDate = ("{0:yyyy-MM-ddTHH:mm:ss}" -f $now)
$EventEndDate = ("{0:yyyy-MM-ddTHH:mm:ss}" -f ($now.AddMinutes(30)) )
$Subject = "TestEvent_$($EventStartDate)-$($ThisScriptInstanceGUID)"

$CalendarEventapiUrl = "$($ApiURL)/v1.0/groups/$($objTeam.id)/calendar/events"

$TestCalendarEvent = @"
{
"subject": "$subject",
"body": {
"contentType": "HTML",
"content": "This is an automated event to test the teams calendar functionality."
},
"start": {
"dateTime": "$EventStartDate",
"timeZone": "Pacific Standard Time"
},
"end": {
"dateTime": "$EventEndDate",
"timeZone": "Pacific Standard Time"
},
"location":{
"displayName":"Contoso"
},
"attendees": [
{
"emailAddress": {
"address":"$M365_AccountName",
"name": "Synthetic User"
},
"type": "required"
}
]
}
"@

$StopWatch = [System.Diagnostics.Stopwatch]::StartNew()
$objEvent = Invoke-RestMethod -Headers @{Authorization = "Bearer $($Tokenresponse.access_token)"} -Uri $CalendarEventapiUrl -Body $TestCalendarEvent -Method Post -ContentType 'application/json'
$StopWatch.Stop()

$bag.CreateEventDurationMS = [double](Format-Number $StopWatch.Elapsed.TotalMilliseconds -DecimalPlaces 0)
$bag.Result = 'SUCCESS'
$Message = "Successful $Activity"
$bag.Message = $Message
$bag.EventId = $objEvent.Id
LogIt -EventID 9992 -Type $info -Msg $Message -Proceed $WriteToEventLog -LINE $(_LINE_); $Error.Clear()
}
catch{
$Message = "Failed $Activity See error data. Verify that the correct permissions have been granted to the App Registration and that Admin Consent has been granted to all permissions, regardless of if consent is 'required'."
LogIt -EventID 9992 -Type $info -Msg $Message -Proceed $WriteToEventLog -LINE $(_LINE_); $Error.Clear()
$bag.Message = $Message
$bag.Result = 'FAILURE'
}


If ($objEvent.id.Length) {
$Activity = "attempt to verify $($NameSpace) calendar test event exists for TeamName [$($TeamName)]. "
LogIt -EventID 9992 -Type $info -Msg $Activity -Proceed $WriteToEventLog -LINE $(_LINE_); $Error.Clear()

#Verify event exists
try {
$Verifyeventapiurl = "$($ApiURL)/v1.0/groups/$($objTeam.id)/calendar/events/$($objEvent.ID)"
$Verifyeventmessage = Invoke-RestMethod -Headers @{Authorization = "Bearer $($Tokenresponse.access_token)"} -Uri $Verifyeventapiurl -Method Get -ContentType 'application/json' -ErrorAction Stop
$Message = "Successful $Activity Event now exists with EventId: [$($Verifyeventmessage.Id)]."
LogIt -EventID 9992 -Type $info -Msg $Message -Proceed $WriteToEventLog -LINE $(_LINE_); $Error.Clear()

# Delete Test Event
$Activity = "attempt to delete $($NameSpace) calendar test event for TeamName [$($TeamName)]."
LogIt -EventID 9992 -Type $info -Msg $Activity -Proceed $WriteToEventLog -LINE $(_LINE_); $Error.Clear()
try {
$StopWatch = [System.Diagnostics.Stopwatch]::StartNew()
$DeleteEventapiurl = "$($ApiURL)/v1.0/groups/$($objTeam.id)/calendar/events/$($objEvent.ID)"
$DeleteEvent = Invoke-RestMethod -Headers @{Authorization = "Bearer $($Tokenresponse.access_token)"} -Uri $DeleteEventapiurl -Method Delete -ContentType 'application/json' -ErrorAction Stop
$StopWatch.Stop()
$bag.DeleteEventDurationMS = [double](Format-Number $StopWatch.Elapsed.TotalMilliseconds -DecimalPlaces 0)
$bag.TotalDurationMS = ($bag.CreateEventDurationMS + $bag.DeleteEventDurationMS)
$bag.Result = 'SUCCESS'
$Message = "Successful $Activity"
$bag.Message += $Message
LogIt -EventID 9992 -Type $info -Msg $Message -Proceed $WriteToEventLog -LINE $(_LINE_); $Error.Clear()
}
Catch{
$Message = "Failed $Activity See error data. Verify that the correct permissions have been granted to the App Registration and that Admin Consent has been granted to all permissions, regardless of if consent is 'required'."
LogIt -EventID 9992 -Type $info -Msg $Message -Proceed $WriteToEventLog -LINE $(_LINE_); $Error.Clear()
$bag.Message = $Message
$bag.Result = 'FAILURE'
}

}
Catch {
$Message = "Failed $Activity. See error data. Verify that the correct permissions have been granted to the App Registration and that Admin Consent has been granted to all permissions, regardless of if consent is 'required'."
LogIt -EventID 9992 -Type $info -Msg $Message -Proceed $WriteToEventLog -LINE $(_LINE_); $Error.Clear()
$bag.Message = $Message
$bag.Result = 'FAILURE'
}
}

Output $bag
LogIt -EventID 9991 -Type $info -Msg "Script End. Finished in [$($ScriptTimer.Elapsed.TotalSeconds)] seconds. `n" -LINE $(_LINE_)</Script></ScriptBody>
<Parameters>
<Parameter>
<Name>ApiTokenScopeURL</Name>
<Value>$Config/ApiTokenScopeURL$</Value>
</Parameter>
<Parameter>
<Name>ApiTokenURL</Name>
<Value>$Config/ApiTokenURL$</Value>
</Parameter>
<Parameter>
<Name>ApiURL</Name>
<Value>$Config/ApiURL$</Value>
</Parameter>
<Parameter>
<Name>M365_AccountName</Name>
<Value>$Config/M365_AccountName$</Value>
</Parameter>
<Parameter>
<Name>M365_AccountPassword</Name>
<Value>$Config/M365_AccountPassword$</Value>
</Parameter>
<Parameter>
<Name>M365_ClientID</Name>
<Value>$Config/M365_ClientID$</Value>
</Parameter>
<Parameter>
<Name>M365_ClientSecret</Name>
<Value>$Config/M365_ClientSecret$</Value>
</Parameter>
<Parameter>
<Name>EventIDFilter</Name>
<Value>$Config/EventIDFilter$</Value>
</Parameter>
<Parameter>
<Name>PoshLibraryPath</Name>
<Value>$FileResource[Name='Res.M365ST.M365Library.ps1.Resource']/Path$,$Config/PoshLibraryPath$</Value>
</Parameter>
<Parameter>
<Name>ScriptOutputType</Name>
<Value>Serialized</Value>
</Parameter>
<Parameter>
<Name>TeamName</Name>
<Value>$Config/TeamName$</Value>
</Parameter>
<Parameter>
<Name>TenantName</Name>
<Value>$Config/TenantName$</Value>
</Parameter>
<Parameter>
<Name>TLSVersion</Name>
<Value>$Config/TLSVersion$</Value>
</Parameter>
<Parameter>
<Name>WorkflowName</Name>
<Value>M365ST.TeamsCalendarMon.Serial.PA_($Config/WorkflowName$)</Value>
</Parameter>
<Parameter>
<Name>WriteToEventLog</Name>
<Value>$Config/WriteToEventLog$</Value>
</Parameter>
</Parameters>
<TimeoutSeconds>$Config/ProbeActionTimeoutSeconds$</TimeoutSeconds>
</ProbeAction>
</MemberModules>
<Composition>
<Node ID="POSH"/>
</Composition>
</Composite>
</ModuleImplementation>
<!--<OutputType>System!System.PropertyBagData</OutputType>-->
<OutputType>Windows!Microsoft.Windows.SerializedObjectData</OutputType>
<InputType>System!System.BaseData</InputType>
</ProbeActionModuleType>