Visualizing SAP PI/PO with Squared Up

SAP Process Orchestration (or Integration) is a software within SAP which functions as data transformation gateway. It’s a central component of an SAP infrastructure and the standard way to communicate with external parties.

This post will show how to apply Squared Up’s “Single Pane of Glass” approach to SAP PI/PO.

Introduction

SAP PI/PO offers a SOAP interface that can be used to gather information about the system.
In our case we are interested about the ‘health state’ of the individual communication channels.

As SOAP expects a XML payload and responds then in XML a ‘transformation’ is required as Squared Up only works with RESTful APIs.

To have a solution that can be applied to different cases a free & opensource Management Pack for SCOM has been created. It serves well for this particular task and can be extend if needed.

Groundwork

SAP PI/PO

The configuration for SAP PO is rather simple. A normal user account needs to be created ( e.g.: in Active Directory ). In this example the user is called E11000.



In SAP PO – Identity Management then assign the roles of SAP_JAVA_NWADMIN_LOGVIEWER_ONLY and SAP_XI_APPL_SERV_USER



That’s all.

PowerShell – Test

Use the following code to test if all working as expected.

#global settings
$ErrorActionPreference = "stop"


#region Your_Configuration_Settings

$UserName      = 'E11000'
$PassWord      = 'ClearTextPassword'
$WebServiceUrl = 'https://servername:port/AdapterFramework/ChannelAdminServlet?party=*&service=*&channel=*&action=Status'

#endregion Your_Configuration_Settings



#region PREWORK Disabling the certificate validations

if ("TrustAllCertsPolicy" -as [type]) {
	$foo = 'already exist'
} else {
add-type -TypeDefinition @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[Net.ServicePointManager]::CertificatePolicy = New-Object -TypeName TrustAllCertsPolicy
}

#endregion PREWORK



#region querying_SAP

$XMLNodeName   =  '//Channel'
$rtnMsg = ''

$header   = @{"Authorization" = "Basic "+[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($UserName+":"+ $PassWord))}

try {	
	$reqAnsw = Invoke-WebRequest -Uri $WebServiceUrl -UseBasicParsing -Headers $header -ContentType $ContentType
} catch {
	$rtnMsg = 'Failure during InvokeWebRequest' + $Error.ToString()
}

[xml]$content = $reqAnsw.Content
$elementList  = $content.DocumentElement.SelectNodes($XMLNodeName)

#endregion querying_SAP



#region converting_XML-To-PowerShellObjects

$allElements = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'

$elementList | ForEach-Object {
  
	$tmpString = $_ | Out-String  
	$tmpEntry  = $tmpString -Split("`r`n")  
	$tmpObj    = New-Object -TypeName PSObject

	$tmpEntry | ForEach-Object {  

		if ($_ -match '[a-zA-Z0-9]') {
			$tmpEntryItm = $_ -Split("\s{1}\:\s{1}")   
			$tmpItmLeft  = $tmpEntryItm[0] -Replace("\s","")
			$tmpItmRight = $tmpEntryItm[1] -Replace("\s","")
			Add-Member -InputObject $tmpObj -MemberType NoteProperty -Name $tmpItmLeft -Value $tmpItmRight        
		}  
		
	} #end $tmpEntry | ForEach-Object {}

	$allElements.Add($tmpObj)

} #end $elementList | ForEach-Object {}

#endregion converting_XML-To-PowerShellObjects



#region verifying_result

$allElements | Out-GridView

#endregion verifying_result 

The result of Out-GridView shall look similar to this one:

SCOM

SCOM is here only used to run an Agent Task which queries the SOAP services and transforms the data to JSON.
The task consists of the Powershell script (mostly like above) which is then showing the results in Squared Up.

The agent task is part of a Management Pack named Windows.Computer.DataOnDemand.Addendum and can be downloaded via Community Catalog on  http://cookdown.com/scom-essentials/community-catalog/ or directly on GitHub https://github.com/Juanito99/Windows.Computer.DataOnDemand.Addendum .

After the Management Pack is important no further configuration in SCOM is required.

Squared Up

Dashboard Setup

Create an empty dashboard following the these steps:

1. Start with the SCOM Task tile
Select the SCOM Task Tile

2. Choose On-Demand Task (Grid)
Choose On-Demand Task (Grid)

3. Enter ANY Windows Server 2012 ( or higher ) which should query the PI/PO system.
Enter ANY Windows Server 2012 ( or higher )

4. Specify the task Get-RemoteSOAPServiceInfo (…)
Specify the task Get-RemoteSOAPServiceInfo (...)

5. Set at minimum the Overrides of PassWord UserName WebService URL and XMLNodeName
Set Overrides of PassWord UserName WebService URL and XMLNodeName

6. Choose which columns should be shown


7. Pick JSON as task return format
Pick json as task return format

8. Complete by enabling show column headers
Complete by enableing show column headers


The raw output should look similar to the one below:
Raw table format
The double hash tags ( ## ) are used later for improved visualization and can be disabled as Override Parameter.

Dashboard Styling

To make the dashboard now a bit more catchy, apply these steps.:

Edit the previously created SCOM task tile and select the Grid Columns section.

Start with editing column ‘Party‘ and paste in the following code:

{{#if (value.Party.substring(0,2) == "##") }} <span style="background-color:#E8E8E8;display: block;"> {{value.Party.replace("##","")}}  </span> {{else}}  {{value.Party}}  {{/if}}

Result after confirming with Done should be a grey background of every other row:

Result after confirming with Done

Add the same code with proper column names ( replace ‘Party’ with the corresponding one ) and the complete table will look good.

For completeness, here the code for ‘Direction‘ …

{{#if (value.Direction.substring(0,2) == "##") && (value.Direction.includes('OUTBOUND')) }} <span style="background-color:#E8E8E8;display: block;text-align: center;"> ▦ ->  </span>  {{elseif (value.Direction.substring(0,2) == "##") && (value.Direction.includes('INBOUND')) }} <span style="background-color:#E8E8E8;display: block;color:#0000ff;text-align: center;"> -> ▦  </span> {{elseif (value.Direction.substring(0,2) != "##") && (value.Direction.includes('INBOUND')) }} <span style="display: block;color:#0000ff;text-align: center;"> -> ▦ </span>   {{elseif (value.Direction.substring(0,2) != "##") && (value.Direction.includes('OUTBOUND')) }} <span style="display: block;text-align: center;"> ▦ -> </span>  {{/if}}

… and for ‘ChannelState‘:

{{#if (value.ChannelState.substring(0,2) == "##") && (value.ChannelState.includes('ERROR')) }} <span style="background-color:#E8E8E8;color:#ff0000;text-align: center;display: block;"> ✘  </span>  {{elseif (value.ChannelState.substring(0,2) == "##") && (value.ChannelState.includes('INACTIVE')) }} <span style="background-color:#E8E8E8;color:#000000;text-align: center;display: block;"> ⚪  </span> {{elseif (value.ChannelState.substring(0,2) == "##") && (value.ChannelState.includes('OK')) }} <span style="background-color:#E8E8E8;color:#00ff00;text-align: center;display: block;"> ✔  </span> {{elseif (value.ChannelState.substring(0,2) != "##") && (value.ChannelState.includes('ERROR')) }} <span style="color:#ff0000;text-align: center;display: block;"> ✘ </span>  {{elseif (value.ChannelState.substring(0,2) != "##") && (value.ChannelState.includes('INACTIVE')) }} <span style="color:#000000;text-align: center;display: block;"> ⚪  </span>  {{elseif (value.ChannelState.substring(0,2) != "##") && (value.ChannelState.includes('OK')) }} <span style="color:#00ff00;text-align: center;display: block;"> ✔  </span>  {{/if}} 

Last, but not least

If you have questions or comments, feel free to contact me.
You find me on twitter or on LinkedIn.

If the code isn’t running on your machine 😉
Or you like to add more features, please navigate the the corresponding GitHub site and raise an issue.
https://github.com/Juanito99/Windows.Computer.DataOnDemand.Addendum/issues

Squared Up dashboard for locked Active Directory Users – Only in PowerShell

Introduction

Squared Up’s Web-API tile allows it to integrate information from any web-service that returns JSON data.

With Polaris, a free and open source framework it is possible to build web-services just in PowerShell.

This example explains the steps to create web-service in Polaris which returns locked out user information and how to integrate them nicely in Squared Up.

su-Dashboard
Locked User Dashboard

Requirement

  • A windows server that will host your Polaris web-service
  • On that server PowerShell version 5.1
  • Active Directory Users & Computer and its module installed ( part of the RSAT )
  • Administrative permissions on to install Polaris (Install-Module -Name Polaris)
  • Create an empty directory adsvc insight of C:\Program Files\WindowsPowerShell\Modules\Polaris
  • Open the Windows firewall to allow incoming connection to the port you specify, here 8082.
  • Limit this port to only accept request from your Squared Up server.
  • NSSM – the Non-Sucking Service Manage to run your web-service script as a service.
    https://nssm.cc/

Realization

The solution consists of two PowerShell scripts. The first one exports locked user information into a JSON file. It needs to be scheduled via Task Scheduler to provide up-to-date information for the dashboard. It would be also possible to extract locked user information on each dashboard load, but that would be very slow.

Export Script

Create a directory C:\ScheduledTasks and copy the following lines into text file. Name it Export-ADLockedAndExpiredUsers.ps1. Place the following content into it:

Import-module ActiveDirectory

$jsonFilePath = 'C:\ScheduledTasks\SquaredUpExports\ADLockedAndExpiredUsers.json'

# storing raw active directory information in ArrayList
$rawLockedUersList = New-Object -TypeName System.Collections.ArrayList

Search-ADAccount -LockedOut | Select-Object -Property Name,SamAccountName,Enabled,PasswordNeverExpires,LockedOut,`
                                    LastLogonDate,PasswordExpired,DistinguishedName | ForEach-Object {
                                        if ($_.Enabled) {
                                            $null = $rawLockedUersList.Add($_)
                                        }
}


# helper function to get account lock out time
Function Get-ADUserLockedOutTime {

    param(
        [Parameter(Mandatory=$true)]
        [string]$userID
    )

    $time = Get-ADUser -Identity $_.SamAccountName -Properties AccountLockoutTime `
        | Select-Object @{Name = 'AccountLockoutTime'; Expression = {$_.AccountLockoutTime | Get-Date -Format "yyyy-MM-dd HH:mm"}}

    $rtnValue = $time | Select-Object -ExpandProperty AccountLockoutTime

    $rtnValue

} #End Function Get-ADUserLockedOutTime


# main function that sorts and formats the output to fit better in the dashboard
Function Get-ADUsersRecentLocked {

    param(
        [Parameter(Mandatory=$true)]
        [System.Collections.ArrayList]$userList
    )

    $tmpList = New-Object -TypeName System.Collections.ArrayList
    
    $tmpList = $userList | Sort-Object -Property LastLogonDate -Descending
    $tmpList = $tmpList  | Select-Object -Property Name,`
                    @{Name = 'UserId' ; Expression = { $_.SamAccountName }}, `
                    @{Name = 'OrgaUnit' ; Expression = { ($_.DistinguishedName -replace('(?i),DC=\w{1,}|CN=|\\','')) -replace(',OU=',' / ')} }, `
                    Enabled,PasswordExpired,PasswordNeverExpires, `
                    @{Name = 'LastLogonDate'; Expression = { $_.LastLogonDate | Get-Date -Format "yyyy-MM-dd HH:mm" }}, `
                    @{Name = 'AccountLockoutTime'; Expression = { (Get-ADUserLockedOutTime -userID $_.SamAccountName) }}

    $tmpList = $tmpList | Sort-Object -Property AccountLockoutTime -Descending                    
    
    # adding a flag character for improved visualization (alternating)
    $rtnList   = New-Object -TypeName System.Collections.ArrayList    
    $itmNumber = $tmpList.Count
    
    for ($counter = 0; $counter -lt $itmNumber; $counter ++) {

        $flack = ''
        if ($counter % 2) { 
            $flack = ''
        } else {
            $flack = '--'
        }

        $userProps = @{
            UserId               = $($flack + $tmpList[$counter].UserId)
            OrgaUnit             = $($flack + $tmpList[$counter].OrgaUnit)
            Enabled              = $($flack + $tmpList[$counter].Enabled)
            PasswordExpired      = $($flack + $tmpList[$counter].PasswordExpired)
            PasswordNeverExpires = $($flack + $tmpList[$counter].PasswordNeverExpires)
            LastLogonDate        = $($flack + $tmpList[$counter].LastLogonDate)
            AccountLockoutTime   = $($flack + $tmpList[$counter].AccountLockoutTime)
        }

        $userObject = New-Object -TypeName psobject -Property $userProps
        
        $null = $rtnList.Add($userObject)        
        Write-Host $userObject

    } #end for ()          

    $rtnList

} #End Function Get-ADUsersRecentLocked

if (Test-Path -Path $jsonFilePath) {
    Remove-Item -Path $jsonFilePath -Force
}


# exporting result to a JSON file and storing it on $jsonFilePath
Get-ADUsersRecentLocked -userList $rawLockedUersList  | ConvertTo-Json | Out-File $jsonFilePath -Encoding utf8 

Publish Script

Create a directory C:\WebSrv and create an empty text file in it. Rename the file Publish-ADData.ps1. Place the following content into it. This directory contains your web-service.

Import-Module -Name Polaris
$polarisPath = 'C:\Program Files\WindowsPowerShell\Modules\Polaris'

# runs every time the code runs and ensure valid JSON output
$middleWare = @"
    `$PolarisPath = '$polarisPath\adsvc'
    if (-not (Test-path `$PolarisPath)) {
        [void](New-Item `$PolarisPath -ItemType Directory)
    }
    if (`$Request.BodyString -ne `$null) {
        `$Request.Body = `$Request.BodyString | ConvertFrom-Json
    }
    `$Request | Add-Member -Name PolarisPath -Value `$PolarisPath -MemberType Noteproperty    
"@

New-PolarisRouteMiddleware -Name JsonBodyParser -ScriptBlock ([scriptblock]::Create($middleWare)) -Force


# the Get route is launched every time the web-service is called 
New-PolarisGetRoute -Path "/adsvc" -ScriptBlock {
        
    $rawLockedUersList = New-Object -TypeName System.Collections.ArrayList    
    

    $rawData  = Get-Content -Path 'C:\ScheduledTasks\SquaredUpExports\ADLockedAndExpiredUsers.json'
    $jsonData = $rawData | ConvertFrom-Json
    
    if ($jsonData.Count -ne 0) {
        $jsonData | ForEach-Object {
            $null = $rawLockedUersList.Add($_)
        }
    }
     
    $reportTime = Get-item -Path C:\ScheduledTasks\SquaredUpExports\ADLockedAndExpiredUsers.json `
                    | Select-Object -ExpandProperty LastWriteTime | Get-Date -Format "yyyy-MM-dd HH:mm"
    
    $maxNoOfUsers = $null    
    $maxNoOfUsers = $request.Query['maxNoOfUsers']   

    $getReportTime = 'no'
    $getReportTime = $request.Query['getReportTime'] 
    
    $getLockedUserCount = 'no'
    $getLockedUserCount = $request.Query['getLockedUserCount'] 
    
    #if getLockedUserCoutn is yes then return number of locked users
    if ($getLockedUserCount -eq 'yes') {
        $noProps = @{ 'number' = $rawLockedUersList.Count }
        $noObj = New-Object psobject -Property $noProps           
        $response.Send(($noObj | ConvertTo-Json))        
    } 

    #if maxNumber is a number than return locked user information
    if ($maxNoOfUsers -match '\d') {
        $rawLockedUersList = $rawLockedUersList | Select-Object -First $maxNoOfUsers
        $response.Send(($rawLockedUersList | ConvertTo-Json))                
    } 

    #if getReportTime is yes then the time of export will be returned    
    if  ($getReportTime -eq 'yes') {
        
        $tmProps = @{
            'Time' = $reportTime
            'DisplayName' = [System.TimezoneInfo]::Local | Select-Object -ExpandProperty DisplayName
        }
        $tmObj = New-Object psobject -Property $tmProps           
        $response.Send(($tmObj | ConvertTo-Json))        
    }

} -Force

Start-Polaris -Port 8082 

#Keep Polaris running
while($true) {
    Start-Sleep -Milliseconds 10
} 

Configure your web-service to run as a service

Download NSSM and store the nssm.exe in C:\WebSrv . Run the following PowerShell line to convert Publish-ADData.ps1 into a service. – Use ISE or VSCode.

function Install-Service {
    Param(
        [string]$nssmPath = '.',
        [string]$Name,
        [string]$Description,
        [string]$Executable,
        [string]$Arguments
    )

    $nssm = Join-Path -Path $nssmPath -ChildPath 'nssm.exe'
    & $nssm install $name $executable $arguments
    $null = & $nssm set $name Description $description
    Start-Service $name
}

Install-Service -Name WWW-Polaris-ADUserLockData -Description 'PowerShell HTTP API Service - Serves ADUserLockedInfo via REST' -Executable powershell.exe -Arguments '-ExecutionPolicy Bypass -Command C:\WebSrv\Publish-ADData.ps1'

The result can be found in the Windows Services:

Testing

From your Squared Up server, start a web browser and query the web-service.

Locked User Account
Report Time
Locked User Details

Dashboard Building

Add a provider

In Squared Up, switch to System and add a new WEB API provider.  The URL is the one of your Polaris web-service.

Adding WEB-API provider

Add the Dashboard

Create a new dashboard and name it Locked User Info for example. Add a Web-API tile to show the locked user count information.

1-      Locked User Counter

choose the recently created Provider
name the URL with ?getLockedUserCount=yes
skip headers & data with next
specify the key path with .number
optimally, specify the size, complete with done.

2 – Locked Users Details

select WEB API (Grid)
skip the scoping with next
choose the provider previously created
as a URL type in ?MaxNoOfUsers=20
as a key path name propery
edit the columns via edit
place the following code to ensure alternating colors and the removal of “–“
finalize with done

3 – Report time

Add a last WEB-API tile type grid and specify the following URL ?getReportTime=yes

Summery

This walk through shows how to integrate own data via Polaris as REST web-service.

Hope it is useful to one of you.

Feedback is appreciated

Build REST Service in PowerShell using Polaris

Introduction

The author say that Polaris is “a cross-platform, minimalist web framework for PowerShell.”

This framework allows it to easily build your own RESTful webservice with only PowerShell. Polaris is free, open source, and also written in PowerShell.

The project is hosted on GitHub. Tyler Leonhardt together with Micah Rairdon and other members in the community are maintaining the project actively.

https://github.com/PowerShell/Polaris

Background

The following terms are good to be known when working with REST.

REST

Representational State Transfer (REST) provides a standardized interface commonly used for machine-to-machine communication.

CRUD

CRUD abbreviates Create, Read, Update, and Delete. A (REST) API usually provide these common set of functions.

MAPPING

The process of allocating a HTTP method to a webservice function is called mapping.
the table below shows a common practice:

REST method Webservice function
POST C: Create
GET R: Read
PUT U: Update
DELETE D: Delete

JSON

JavaScript Object Notation (JSON) allows it to conserve structured data like objects, properties, and lists and their relation to each other. Serialization is the procedure to convert virtual objects to be conservable. Deserialization is the other way around. Polaris uses JSON to send and retrieve information, for example:

   “cars”: {


       “car1″:”Ford”,
       “car2″:”BMW”,

PowerShell offers easy handling with ConvertFrom-Json and ConvertTo-Json.

Consumption via Squared Up

The WEB API TILE from Squared Up allows to read data from a REST service and shows it on a Dashboard. With Polaris you can now query any data source that provides a PowerShell interface to pass it’s information to a dashboard.

https://support.squaredup.com/v3/Walkthroughs/Tiles/HowToUseTheWebAPITile/

 

Concrete examples will follow.

 

Further reading

A while ago I blogged on 4sysops an example of using Polaris. It helps to get a first understanding on how it works.

https://4sysops.com/archives/use-polaris-to-create-a-restful-webservice-in-powershell-for-managing-ad-users/