URL Watcher Discovery Data Source Module

Microsoft.LS.2015.Discovery.UrlWatcher.DS (DataSourceModuleType)

Element properties:

TypeDataSourceModuleType
IsolationAny
AccessibilityInternal
RunAsDefault
OutputTypeSystem.Discovery.Data

Member Modules:

ID Module Type TypeId RunAs 
DS DataSource Microsoft.LS.2015.Discovery.Common.DS.ShellOut Default

Overrideable Parameters:

IDParameterTypeSelectorDisplay NameDescription
IntervalSecondsint$Config/IntervalSeconds$Interval SecondsInterval Seconds
TimeoutSecondsint$Config/TimeoutSeconds$Timeout in SecondsTimeout in Seconds

Source Code:

<DataSourceModuleType ID="Microsoft.LS.2015.Discovery.UrlWatcher.DS" Accessibility="Internal" Batching="false">
<Configuration>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="IntervalSeconds" type="xsd:integer"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="TimeoutSeconds" type="xsd:integer"/>
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="IntervalSeconds" Selector="$Config/IntervalSeconds$" ParameterType="int"/>
<OverrideableParameter ID="TimeoutSeconds" Selector="$Config/TimeoutSeconds$" ParameterType="int"/>
</OverrideableParameters>
<ModuleImplementation Isolation="Any">
<Composite>
<MemberModules>
<DataSource ID="DS" TypeID="Microsoft.LS.2015.Discovery.Common.DS.ShellOut">
<IntervalSeconds>$Config/IntervalSeconds$</IntervalSeconds>
<SyncTime/>
<ScriptName>Communication Server MP Url Watcher Discovery</ScriptName>
<ScriptFileName>UrlWatcherDiscovery.ps1</ScriptFileName>
<ScriptBody><Script>
#############################################################################
# Get list of Simple Url Configuration.
#############################################################################
#CONSTANTS###################################################################
$GLOBAL_SCOPE = "Global"
$URL_DISPLAYNAME_FORMAT = "Url: [{0}]"
$MEET_URL_FORMAT = "{0}/TestAlias/TestCode1234"
$MEET_SVC_URL_FORMAT = "{0}/TestDomainAlias/TestAlias/TestCode1234"
$DIALIN_URL_FORMAT = "{0}"
$WEBSCHEDULER_URL_FORMAT = "{0}/Scheduler"
$CSCP_URL_FORMAT = "{0}/cscp"
# TODO - FYFIX: CSCP type is not supported. It requires authentication.
# TODO - FYFIX: web scheduler. Requires Auth. cannot find any part of that doesn't require auth.
# Testing fix: Set evaluation of base page rendering to GreaterThan 403. This means by HTTP RFC that even if it fails to login, but the server responds
# with something, it will pass.
$TEST_URL_FORMATS = @{
"meet" = $MEET_URL_FORMAT;
"dialin" = $DIALIN_URL_FORMAT;
"meet_svc" = $MEET_SVC_URL_FORMAT;
"webscheduler" = $WEBSCHEDULER_URL_FORMAT;
"cscp" = $CSCP_URL_FORMAT;
};
# E.g: https://lslm84.meet.microsoft.com/meet/anandl/5KITIMIM or
# in service: https://meet.skype.com/contoso/joe/123456
#############################################################################

$CSONLINE_MODULE_NAME = "SkypeForBusinessOnline"
$CSONLINE_MODULE_ADDED = "SkypeForBusinessOnline Module is added";
$CSONLINE_MODULE_ALREADY_LOADED = "SkypeForBusinessOnline Module is already loaded. No need to reload."
$CSONLINE_MODULE_FAILED_LOAD = "Failed to load SkypeForBusinessOnline Module. Error: {0}."

$CS_MODULE_NAME = "SkypeForBusiness"
$CS_MODULE_ADDED = "SkypeForBusiness Module is added";
$CS_MODULE_ALREADY_LOADED = "SkypeForBusiness Module is already loaded. No need to reload."
$CS_MODULE_FAILED_LOAD = "Failed to load SkypeForBusiness Module. Error: {0}."




$NO_PROV_SERVICE_CONFIG = "No provisioning configuration found, please check your configuration !"
$NO_SIMPLEURLDNS_CONFIG = "No SimpleURLDNS configuration found for provisioning service, please check your configuration !"

$EVENT_ERROR = 1
$EVENT_WARNING = 2
$EVENT_INFORMATION = 4
$EVENT_ID = 261
$EVENT_SCRIPT_NAME = "UrlWatcherDiscovery.ps1"


#############################################################################
#HELPER FUNCTIONS#########################################################################################
function IsCSModuleLoaded()
{
(get-module $CS_MODULE_NAME ) -ne $null
}

function LoadCSModule()
{
# Script execution are managed by SCOM powershell module.
# MonitoringHost.exe is hosting powershell runspaces.
# SCOM might run the scripts on the same process and reuse the app domain if neccesary.
# Double check on wheter module is loaded or not.
if (IsCSModuleLoaded)
{
TRACE ($CS_MODULE_ALREADY_LOADED)
}
else
{
# Refresh PSModulePath in case new build is deployed into different folder but SCOM Agent is not restarted
$psPaths = $env:PSModulePath -split ";"
$env:PSModulePath = ($psPaths + ([Environment]::GetEnvironmentVariable("PSModulePath", "Machine") -split ";" | ?{$psPaths -notcontains $_})) -join ";"

# Due to possible race condition between steps of checking module is loaded and loading module steps,
# we need to double check after module loading attempt.
$moduleImported = $false
$retryCount = 0

while (!$moduleImported -and $retryCount -lt 10)
{
try
{
Import-module $CS_MODULE_NAME -ErrorAction SilentlyContinue
$moduleImported = $true
}
catch
{
$retryCount= $retryCount + 1
TRACE ("Retrying Import Module" + $retryCount)
}
}

if (IsCSModuleLoaded)
{
TRACE ($CS_MODULE_ADDED)
}
else
{
$lastError = $error[0]
TRACE ($CS_MODULE_FAILED_LOAD -f $lastError)
}
}
}



# TODO - FY: Merge LoadCSModule and LoadCSOnlineModule functions.

function IsCSOnlineModuleLoaded()
{
(get-module $CSONLINE_MODULE_NAME ) -ne $null
}

function LoadCSOnlineModule()
{
# Script execution are managed by SCOM powershell module.
# MonitoringHost.exe is hosting powershell runspaces.
# SCOM might run the scripts on the same process and reuse the app domain if neccesary.
# Double check on wheter module is loaded or not.
if (IsCSOnlineModuleLoaded)
{
TRACE ($CSONLINE_MODULE_ALREADY_LOADED)
}
else
{
# Refresh PSModulePath in case new build is deployed into different folder but SCOM Agent is not restarted
$psPaths = $env:PSModulePath -split ";"
$env:PSModulePath = ($psPaths + ([Environment]::GetEnvironmentVariable("PSModulePath", "Machine") -split ";" | ?{$psPaths -notcontains $_})) -join ";"

# Due to possible race condition between steps of checking module is loaded and loading module steps,
# we need to double check after module loading attempt.
$moduleImported = $false
$retryCount = 0

while (!$moduleImported -and $retryCount -lt 10)
{
try
{
Import-module $CSONLINE_MODULE_NAME -ErrorAction SilentlyContinue
$moduleImported = $true
}
catch
{
$retryCount= $retryCount + 1
TRACE ("Retrying Import Module" + $retryCount)
}
}
if (IsCSOnlineModuleLoaded)
{
TRACE ($CSONLINE_MODULE_ADDED)
}
else
{
$lastError = $error[0]
TRACE ($CSONLINE_MODULE_FAILED_LOAD -f $lastError)
}
}
}

function AddUrlWatcher($url, $component, $domain, $urlToTest, $IsActive, $displayName)
{
$urlWatcher = $DiscoveryData.CreateClassInstance("$MPElement[Name='Microsoft.LS.2015.UrlWatcher']$");
$urlWatcher.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", $TargetComputer);

TRACE ("url is {0}, component is {1}, domain is {2}, urlTTest is {3} " -f $url, $component, $domain, $urlToTest)

$urlWatcher.AddProperty("$MPElement[Name='Microsoft.LS.2015.UrlWatcher']/SimpleUrl$", $url);
$urlWatcher.AddProperty("$MPElement[Name='Microsoft.LS.2015.UrlWatcher']/Type$", $component);
$urlWatcher.AddProperty("$MPElement[Name='Microsoft.LS.2015.UrlWatcher']/Domain$", $domain);
$urlWatcher.AddProperty("$MPElement[Name='Microsoft.LS.2015.UrlWatcher']/UrlToTest$", $urlToTest);
$urlWatcher.AddProperty("$MPElement[Name='Microsoft.LS.2015.UrlWatcher']/IsActive$", $IsActive);
$urlWatcher.AddProperty("$MPElement[Name='Microsoft.LS.2015.UrlWatcher']/Scope$", $GLOBAL_SCOPE);
$urlWatcher.AddProperty("$MPElement[Name='System!System.Entity']/DisplayName$", $displayName);
$DiscoveryData.AddInstance($urlWatcher);

$WatcherNodeHostsUrlWatcher =
$DiscoveryData.CreateRelationshipInstance("$MPElement[Name='Microsoft.LS.2015.Relationship.UrlWatcherNodeHostsUrlWatcher']$");
$WatcherNodeHostsUrlWatcher.Source = $watcherNode
$WatcherNodeHostsUrlWatcher.Target = $urlWatcher
$DiscoveryData.AddInstance($WatcherNodeHostsUrlWatcher);

$deploymentContainsUrlWatcher= $DiscoveryData.CreateRelationshipInstance("$MPElement[Name='Microsoft.LS.2015.Relationship.DeploymentContainsUrlWatcher']$");
$deploymentContainsUrlWatcher.Source = $deployment
$deploymentContainsUrlWatcher.Target = $urlWatcher
$DiscoveryData.AddInstance($deploymentContainsUrlWatcher);

TRACE ("{0} is discovered." -f $displayName)

}

function IsProvisionServiceInstalled()
{
TRACE ("Searching for provision service in the topology.")
$provisionServiceName = [Microsoft.Rtc.Management.Core.RoleName]::ProvisionService
$prov = ((get-cstopology).Services | where {$_.RoleId.Name -eq $provisionServiceName})
$result = $prov -ne $null
if ($result)
{
TRACE ("Successfully found provision service in the topology.")
}
else
{
TRACE ("Can not find provision service in the topology.")
}
$result
}

LoadCSModule

$DiscoveryData = GetDiscoveryData

$deployment = $DiscoveryData.CreateClassInstance("$MPElement[Name='Microsoft.LS.2015.Deployment']$");
$watcherNode = $DiscoveryData.CreateClassInstance("$MPElement[Name='Microsoft.LS.2015.WatcherNode.Url']$");
$watcherNode.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", $TargetComputer);


# Logic: Look for provisioning Server in the topology. This should be present only in the service deployments.
# Based on the results, you can choose which URLs to ping (server or service)

If (IsProvisionServiceInstalled)
{
# We expect the provisioning service to be present only in the service deployments.

LoadCSOnlineModule

If (IsCSOnlineModuleLoaded)
{
$provConfig = Get-CsProvisionServiceConfiguration

if ($provConfig -ne $null)
{
$simpleurl = $provConfig.SimpleUrlDNSName

if ($simpleurl -ne $null)
{
$domain = "*"
#this is hardcoded because it _ONLY_ applies in the online "provisioning" condition
$IsActive = $true
$component = "meet_svc"

$testUrlFormat = $TEST_URL_FORMATS[$component.ToLower()];
$urlToTest = $testUrlFormat -f $simpleurl
$displayName = $URL_DISPLAYNAME_FORMAT -f $simpleurl

AddUrlWatcher $simpleurl $component $domain $urlToTest $IsActive $displayName
}
}

}
}
# If there is not provisioning service, assume it's onprem only and get URL's via onprem method, or just tag the simpleURLs onto the existing online URLs.

## TODO - FYFIX: Currently only iterating over Global Simple Urls.
# There is always default value for Global identity.

$simpleUrls = (Get-CsSimpleUrlConfiguration -Identity Global).SimpleUrl

foreach ($simpleUrl in $simpleUrls)
{
$activeUrl =$simpleUrl.ActiveUrl
$component = $simpleUrl.Component
$domain = $simpleUrl.Domain

if ($TEST_URL_FORMATS[$component] -ne $null)
{
foreach ($SimpeUrlEntry in $simpleUrl.SimpleUrlEntry)
{
$url = $SimpeUrlEntry.Url
$IsActive = $false
if ($url -eq $activeUrl)
{
$IsActive = $true
}

$testUrlFormat = $TEST_URL_FORMATS[$component.ToLower()];
$urlToTest = $testUrlFormat -f $url
$displayName = $URL_DISPLAYNAME_FORMAT -f $url

AddUrlWatcher $url $component $domain $urlToTest $IsActive $displayName
}
}
else
{
Continue;
}
}
#Return Trace info to event log
#$MOMapi.LogScriptEvent($EVENT_SCRIPT_NAME, $EVENT_ID, $EVENT_SEVERITY , $logMessage.ToString())
</Script></ScriptBody>
<TimeoutSeconds>$Config/TimeoutSeconds$</TimeoutSeconds>
<EventId>261</EventId>
<TargetComputer>$Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</TargetComputer>
</DataSource>
</MemberModules>
<Composition>
<Node ID="DS"/>
</Composition>
</Composite>
</ModuleImplementation>
<OutputType>System!System.Discovery.Data</OutputType>
</DataSourceModuleType>