Friday, March 9, 2018

Infrastructure testing using Pester - Part 2

In my previous post I explained briefly about the basics of using Pester to test and validate your infrastructure. This article is about how to create/ output custom messages on Pester test failures. Let's have a look at the below example.

#example_test.ps1
Describe "Verify drive D" {
    Context "Check file system type and AUS" {
        It "Test 1: Should be NTFS" {
            $drive_stat = fsutil fsinfo statistics D:
            ($drive_stat[0]) -match 'File System Type :\s+NTFS' | Should Be $true
        }
        It "Test 2: Should have 4K AUS" {
            $AUS_stat = fsutil fsinfo refsinfo D:
            ($AUS_stat[8]) -match 'Bytes Per Cluster :\s+4096' | Should Be $true
        }
    }
}

Describe "Verify Hyper-V vSwitch" {
    Context "Check for Corp vSwitch, its type and connected NIC" {
       
        $check = Get-VMSwitch | where name -eq Corp

        It "Test 3: Corp vSwitch should be present" {
            ($check.Name) | Should -BeExactly "Corp"
        }
        It "Test 4: Corp vSwitch type should be Internal" {
            ($check.SwitchType) | Should -BeExactly "Internal"  
        }
    }
}


I get the following output, when I run it against one of my test machine.


The above screenshot shows, Test 1 and Test 4 is failed. Eventhough you get the error message, some users/ admins prefer to have a custom message when a test fails. So that it will be very easy to troubleshoot for the one who is validating the infrastructure. To save custom messages for each test, I am using a hash table, and if a test is found to be failed, it will pick the respective custom message from the hash table and displays it and the end of the test. Here I created a new ps1 file as below.

#invoke_test.ps1
$test = Invoke-Pester .\example_test.ps1 -PassThru

$failmsg_table = @{
    "Test 1: Should be NTFS"                        = "Test 1: Failed because the filesystem of drive D is not NTFS"
    "Test 2: Should have 4K AUS"                    = "Test 2: Failed because the AUS of drive D is not 4K"
    "Test 3: Corp vSwitch should be present"        = "Test 3: Failed becuase Corp vSwitch is not present"
    "Test 4: Corp vSwitch type should be Internal"  = "Test 4: Failed because Corp vSwitch type is not Internal"
}

for ($i=0; $i -le $test.TotalCount; $i++){
    if ($test.TestResult[$i].Result -eq "Failed") {
        write-output $failmsg_table[$($test.TestResult[$i].Name)]
    }
}



When I execute "invoke_test.ps1", it invokes "example_test.ps1" and the output is saved to variable "test". The hash table has the list of tests and the corresponding custom message. If any of the "TestResult" is Failed, the corresponding custom message will be displayed. Sample output screenshot is given below.


 Hope this was useful. Cheers!

Wednesday, March 7, 2018

Working with ScaleIO REST API using PowerShell - Part 2

In my previous post, I explained how to connect and authenticate with ScaleIO REST API, and use Invoke-RestMethod. In this article, I will explain how to query selected statistics/ properties with POST method. 

First, you have to define the set of properties that you would like to query, and then pass it to "invoke-restmethod". You can get the list of available properties of the respective object from the ScaleIO REST API Reference Guide. 

Example 1: Query and collect MDM cluster information
Here I am collecting the below-mentioned properties of "MDMCluster" object. 

#selected properties to query

$param1 = @'
{
    "properties":["name", "clusterMode", "master", "clusterState", "virtualIps"]
}
'@

#invoke-restmethod with respective uri

$MDM_cluster_stats = (Invoke-RestMethod -Uri "https://192.168.11.15:443/api/instances/System/queryMdmCluster " -Body $param1 -ContentType "application/json" -Headers $ScaleIOAuthHeaders -Method Post)

#organize output in a hash table

$mdm_props = @{

    'Cluster name'      = ($MDM_cluster_stats).name
    'Mode'              = ($MDM_cluster_stats).clusterMode
    'Cluster state'     = ($MDM_cluster_stats).clusterState
    'Master MDM IP'     = ($MDM_cluster_stats.master).managementIPs[0]
    'Cluster VIP01'     = ($MDM_cluster_stats).virtualIps[0]
    'Cluster VIP02'     = ($MDM_cluster_stats).virtualIps[1]
}

Write-Output $mdm_props

#sample output screenshot given below


Example 2: Query and collect overall ScaleIO system capacity details
Here I am collecting some properties of "System Statistics" object. 

#selected properties to query

$param2 = @'
{
    "properties":["maxCapacityInKb", "capacityInUseInKb", "spareCapacityInKb", "failedCapacityInKb", "degradedFailedCapacityInKb"]
}
'@

#invoke-restmethod with respective uri

$system_overall_stats = (Invoke-RestMethod -uri "https://192.168.11.15:443/api/types/System/instances/action/querySelectedStatistics " -Body $param2 -ContentType "application/json" -Headers $ScaleIOAuthHeaders -Method Post)

#organize output in a hash table


$system_capacity_props = @{

    "System max capacity (TB)"                = (($system_overall_stats.maxCapacityInKb)/1024/1024/1024)
    "System capacity in use (TB)"             = (($system_overall_stats.capacityInUseInKb)/1024/1024/1024)
    "System spare capacity (TB)"              = (($system_overall_stats.spareCapacityInKb)/1024/1024/1024)
    "System failed capacity (TB)"             = (($system_overall_stats.failedCapacityInKb)/1024/1024/1024)
    "System degraded failed capacity (TB)"    = (($system_overall_stats.degradedFailedCapacityInKb)/1024/1024/1024)
}

Write-Output $system_capacity_props

#sample output screenshot given below


Hope this was useful. Please refer Dell EMC ScaleIO Ready Node Version 2.5 API Reference Guide for more details.

Wednesday, February 28, 2018

Working with Dell EMC PowerFlex REST API using PowerShell - Part 1

In this article, I will explain briefly how to work with REST API exposed by PowerFlex (formerly known as ScaleIO and VxFlex OS Software) using PowerShell to manage/ monitor your PowerFlex cluster. The PowerFlex REST API is served from the PowerFlex Gateway which allows querying information and performing actions related to PowerFlex software components. PowerFlex Gateway connects and queries master MDM, reformats the results in a RESTful manner, and passes it to a REST client.

Access to the API requires a login using Gateway username and password. A successful login returns a token, and you can use this token for later authentications for other requests. The responses or results returned are in JSON format. In the following sections, I will explain how to use PowerShell to query PowerFlex REST API and obtain relevant results.  

PowerShell uses Invoke-RestMethod cmdlet which sends HTTP/ HTTPS requests to REST web services that return rich structured data. PowerShell will format the response based on the data type. For JSON PowerShell converts the response content to objects. 

Step 1: Authentication and token generation

#Note: 192.168.11.15 is the IP of PowerFlex Gateway
$User = Read-Host -Prompt "Please enter username"
$SecurePassword = Read-Host -Prompt "Enter Password for user $user" AsSecureString
$Credentials = New-Object System.Management.Automation.PSCredential ($user,$Securepassword)

#HTTPS Get request is invoked with credentials for authentication
$Token = Invoke-RestMethod -Uri "https://192.168.11.15:443/api/login" -Method Get -Credential $Credentials

Step 2: Create header with the token
$auth = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(':'+$Token))
$global:PowerFlexAuthHeaders = @{'Authorization' = "Basic $auth"
'Content-Type' = "application/json" }

Step 3: Invoke-RestMethod

#Query all alerts in the system
$alerts = (Invoke-RestMethod -Uri "https://192.168.11.15:443/api/types/Alert/instances/" -Method Get -Headers $PowerFlexAuthHeaders)

#Query all SDSs in a PD
$all_SDS = (Invoke-RestMethod -Uri "https://192.168.11.15:443/api/instances/ProtectionDomain::3ce4af5f00000000/relationships/Sds " -Method Get -Headers $PowerFlexAuthHeaders)

#Query a specified PD
$pd01 = (Invoke-RestMethod -Uri "https://192.168.11.15:443/api/instances/ProtectionDomain::3ce4af5f00000000 " -Method Get -Headers $PowerFlexAuthHeaders)
#Query statistics of a specified PD
$pd01_stats = (Invoke-RestMethod -Uri "https://192.168.11.15:443/api/instances/ProtectionDomain::3ce4af5f00000000/relationships/Statistics " -Method Get -Headers $PowerFlexAuthHeaders)

Hope this was useful. Refer to the PowerFlex REST API Reference Guide for more details.
In the next article, I will explain how to query selected statistics/ parameters with POST method which will help you to
create custom monitoring scripts for your PowerFlex clusters.

Note:
If you are getting the below error while trying to create the token during step 1, please add the below lines of code to the
start of your script.

"Invoke-RestMethod : The underlying connection was closed: An unexpected error occurred on a send."

#Solution to above error!
add-type @"
    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;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 -bor [System.Net.SecurityProtocolType]::Tls11

Monday, February 5, 2018

PowerShell Remoting

Remoting is a feature that helps you manage Windows infrastructure in scale. It uses WS-MAN protocol implemented using WinRM. PS Remoting is enabled by default in Windows Server 2012 and later. It is recommended to turn on remoting because a lot of new graphical administrative tools are making use of PowerShell and PowerShell Remoting in the background.

Here I will be explaining remoting on systems part of same domain.

1:1 Remoting

This case is useful in managing a single system. Enter-PSSession cmdlet can be used for 1:1 remote connection.

Enter-PSSession -ComputerName AD-DNS


In the above screenshot you can see that I connected to AD-DNS from VM01 using Enter-PSSession. Once the session is established you can see the PS prompt changes to "[AD-DNS]:". This means currently you are in the command line prompt of AD-DNS. The next two cmdlets gets the IPV4 address and eventlogs of the remote machine.

1:Many Remoting

This case is useful if you want to run a specific command or task on a set of computers and get the results back to you.

invoke-command -ComputerName VM01, AD-DNS { gsv msiscsi }



What actually happens here is first a PS session is established to the remote machine. Load PowerShell and .NET and the give code is sent across the connection, execute the code on the remote machine, the resultant objects are then serialized into XML, send them across the connection, deserialize the XML to objects and place them in the pipeline of the PowerShell session.

Lets have a look at the below case where we execute gsv msiscsi on the local machine. You can see that the type name is System.ServiceProcess.ServiceController .


When you execute gsv msiscsi on remote machines by adding -ComputerName with the Invoke-Command, you can see the type name changed to Deserialized.System.ServiceProcess.ServiceController .


Another remoting use case given below where you want to check the remaining size of some specific drive on multiple machines.


PS Sessions

When you use the Enter-PSSession or Invoke-Command with -ComputerName parameter a remote session is established and it will run the task which was asked to and it will end the session when the task is complete. In case of Enter-PSSession cmdlet, the PS session will end once the user termiates the session using Exit-PSSession. So always there is an overhead of starting and ending a PS session. There is way to create persistent PS session using New-PSSession cmdlet.

$s1 = New-PSSession -ComputerName AD-DNS


Here $s1 will hold a persistent PS session to computer AD-DNS. Now you can invoke tasks remotely using the session that is already created and opened.


It is the responsibility of the user to remove the PS-Sessions after use. Otherwise it will remain opened and consume resources.

PowerShell Direct

PowerShell Direct is a new feature introduced in PowerShell version 5.1 which supports management of Windows 10 and Windows Server 2016 guest VMs running on Windows 10 or Windows Server 2016 host machines. This simply means you can establish a PowerShell session from the host machine to any of the VMs running on it by just using the VM name and it works even without network connectivity to the VM through a vSwitch. Because the connection is established not via network but over the Hyper-V VM bus. You can even use PS Direct sessions to copy files to a VM which does not have IP connectivity.

Lets have a look into the example below where I have few VMs hosted on Windows Server 2016. I will connect to one of the VM named "AD" using PS direct.


In the above screenshot you can see that a new PS session is established using the VM name. Now lets see how you can copy files to a VM over PS Direct sessions.


Hope this was useful. Happy PS remoting !

Reference ebooks:

Secrets of PowerShell Remoting
Layman's Guide to PowerShell 2.0 remoting

Reference videos: