Invoke-RemoteDiagsREDFISH.psm1

<#
_author_ = Texas Roemer <Texas_Roemer@Dell.com>
_version_ = 1.0
 
Copyright (c) 2024, Dell, Inc.
 
This software is licensed to you under the GNU General Public License,
version 2 (GPLv2). There is NO WARRANTY for this software, express or
implied, including the implied warranties of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
along with this software; if not, see
http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
#>


<#
.Synopsis
   iDRAC cmdlet using Redfish API with OEM extension to run remote diagnostics on the server. Once remote diags has been performed you can export diag results locally or a network share to review.
.DESCRIPTION
   iDRAC cmdlet using Redfish API with OEM extension to run remote diagnostics on the server. Once remote diags has been performed you can export diag results locally or a network share to review.
   Supported parameters to pass in for cmdlet:
    
   - idrac_ip: Pass in iDRAC IP
   - idrac_username: Pass in iDRAC username
   - idrac_password: Pass in iDRAC password
   - x_auth_token: Pass in iDRAC X-Auth token session to execute cmdlet instead of username / password (recommended)
   - run_diags: Perform remote diags on the server.
   - reboot_type: Pass in server reboot job type. Server reboot is required to start running remote diags. Supported values: GracefulRebootWithForcedShutdown, GracefulRebootWithoutForcedShutdown or PowerCycle.
   - run_mode: Pass in the run mode type you want to execute for diags. Supported values: Express, ExpressAndExtended and Extended
   - export: Export diag results locally or network share.
   - network_share_IPAddress: Pass in network share IP address
   - ShareName: Pass in network share name
   - ShareType: Pass in network share type. Supported values: Local, NFS, CIFS, HTTP and HTTPS
   - FileName: Pass in unique name for exporting diags results to a network share. Note this argument not required for exporting locally.
   - share_username: If your network share has auth enabled, pass in username
   - share_password: If your network share has auth enabled, pass in username password
 
.EXAMPLE
   Invoke-RemoteDiagsREDFISH -idrac_ip 192.168.0.120 -idrac_username root -idrac_password calvin -run_diags -reboot_type PowerCycle -run_mode Express
   This example shows performing server power cycle to run express remote diags.
.EXAMPLE
   Invoke-RemoteDiagsREDFISH -idrac_ip 192.168.0.120 -idrac_username root -idrac_password calvin -export -ShareType Local
   This example shows exporting remote diags results locally using default browser.
.EXAMPLE
   Invoke-RemoteDiagsREDFISH -idrac_ip 192.168.0.120 -idrac_username root -idrac_password calvin -export -ShareType NFS -network_share_IPAddress 192.168.0.130 -ShareName /nfs -FileName diags_results.txt
   This example shows exporting remote diags results to a NFS share.
#>


function Invoke-RemoteDiagsREDFISH {

param(
    [Parameter(Mandatory=$True)]
    [string]$idrac_ip,
    [Parameter(Mandatory=$False)]
    [string]$idrac_username,
    [Parameter(Mandatory=$False)]
    [string]$idrac_password,
    [Parameter(Mandatory=$False)]
    [string]$x_auth_token,
    [Parameter(Mandatory=$False)]
    [switch]$run_diags,
    [ValidateSet("GracefulRebootWithForcedShutdown", "GracefulRebootWithoutForcedShutdown", "PowerCycle")]
    [Parameter(Mandatory=$False)]
    [string]$reboot_type,
    [ValidateSet("Express", "ExpressAndExtended", "Extended")]
    [Parameter(Mandatory=$False)]
    [string]$run_mode,
    [Parameter(Mandatory=$False)]
    [switch]$export,
    [ValidateSet("Local", "NFS", "CIFS", "HTTP", "HTTPS")]
    [Parameter(Mandatory=$False)]
    [string]$ShareType,
    [Parameter(Mandatory=$False)]
    [string]$ShareName,
    [Parameter(Mandatory=$False)]
    [string]$network_share_IPAddress,
    [Parameter(Mandatory=$False)]
    [string]$FileName,
    [Parameter(Mandatory=$False)]
    [string]$share_username,
    [Parameter(Mandatory=$False)]
    [string]$share_password
    )


# Function to ignore SSL certs

function Ignore-SSLCertificates
{
    $Provider = New-Object Microsoft.CSharp.CSharpCodeProvider
    $Compiler = $Provider.CreateCompiler()
    $Params = New-Object System.CodeDom.Compiler.CompilerParameters
    $Params.GenerateExecutable = $false
    $Params.GenerateInMemory = $true
    $Params.IncludeDebugInformation = $false
    $Params.ReferencedAssemblies.Add("System.DLL") > $null
    $TASource=@'
        namespace Local.ToolkitExtensions.Net.CertificatePolicy
        {
            public class TrustAll : System.Net.ICertificatePolicy
            {
                public bool CheckValidationResult(System.Net.ServicePoint sp,System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest req, int problem)
                {
                    return true;
                }
            }
        }
'@
 
    $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
    $TAAssembly=$TAResults.CompiledAssembly
    $TrustAll = $TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
    [System.Net.ServicePointManager]::CertificatePolicy = $TrustAll
}

# Function to set up iDRAC credentials

function setup_idrac_creds
{

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::TLS12

if ($x_auth_token)
{
$global:x_auth_token = $x_auth_token
}
elseif ($idrac_username -and $idrac_password)
{
$user = $idrac_username
$pass= $idrac_password
$secpasswd = ConvertTo-SecureString $pass -AsPlainText -Force
$global:credential = New-Object System.Management.Automation.PSCredential($user, $secpasswd)
}
else
{
$get_creds = Get-Credential
$global:credential = New-Object System.Management.Automation.PSCredential($get_creds.UserName, $get_creds.Password)
}
}

# Function to get Powershell version

function get_powershell_version 
{
$get_host_info = Get-Host
$major_number = $get_host_info.Version.Major
$global:get_powershell_version = $major_number
}


# Function to run remote diags

function execute_remote_diags
{

$Global:create_job_success = "yes"
$create_payload = @{"RebootJobType"=$reboot_type;"RunMode"=$run_mode}

$JsonBody = $create_payload | ConvertTo-Json -Compress

$uri = "https://$idrac_ip/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellLCService/Actions/DellLCService.RunePSADiagnostics"

if ($x_auth_token)
{
try
    {
    if ($global:get_powershell_version -gt 5)
    {
    
    $post_result = Invoke-WebRequest -UseBasicParsing -SkipHeaderValidation -SkipCertificateCheck -Uri $uri -Method Post -Body $JsonBody -ContentType 'application/json' -Headers @{"Accept" = "application/json"; "X-Auth-Token" = $x_auth_token} -ErrorVariable RespErr
    }
    else
    {
    Ignore-SSLCertificates
    $post_result = Invoke-WebRequest -UseBasicParsing -Uri $uri -Method Post -Body $JsonBody -ContentType 'application/json' -Headers @{"Accept" = "application/json"; "X-Auth-Token" = $x_auth_token} -ErrorVariable RespErr
    }
    }
    catch
    {
    Write-Host
    $RespErr
    return
    } 
}


else
{
try
    {
    if ($global:get_powershell_version -gt 5)
    {
    
    $post_result = Invoke-WebRequest -UseBasicParsing -SkipHeaderValidation -SkipCertificateCheck -Uri $uri -Credential $credential -Method Post -Body $JsonBody -ContentType 'application/json' -Headers @{"Accept"="application/json"} -ErrorVariable RespErr
    }
    else
    {
    Ignore-SSLCertificates
    $post_result = Invoke-WebRequest -UseBasicParsing -Uri $uri -Credential $credential -Method Post -Body $JsonBody -ContentType 'application/json' -Headers @{"Accept"="application/json"} -ErrorVariable RespErr
    }
    }
    catch
    {
    Write-Host
    $RespErr
    return
    } 
}


if ($post_result.StatusCode -eq 202 -or $post_result.StatusCode -eq 200)
{
    $job_id_search=$post_result.Headers['Location']
    $Global:job_id=$job_id_search.Split("/")[-1]
    [String]::Format("`n- PASS, statuscode {0} returned successfully for POST command to create update job ID '{1}'",$post_result.StatusCode, $Global:job_id)
    Write-Host
    return
}
else
{
    [String]::Format("- FAIL, statuscode {0} returned. Detail error message: {1}",$post_result.StatusCode,$post_result)
    break
}

}

function export_remote_diags_results
{

$create_payload = @{}

if ($ShareType)
{
$create_payload["ShareType"]=$ShareType
}
if ($network_share_IPAddress)
{
$create_payload["IPAddress"]=$network_share_IPAddress
}
if ($ShareName)
{
$create_payload["ShareName"]=$ShareName
}
if ($FileName)
{
$create_payload["FileName"]=$FileName
}
if ($share_password)
{
$create_payload["Password"]=$share_password
}
if ($share_username)
{
$create_payload["Username"]=$share_username
}

$JsonBody = $create_payload | ConvertTo-Json -Compress

$uri = "https://$idrac_ip/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellLCService/Actions/DellLCService.ExportePSADiagnosticsResult"
$Global:create_job_success = "no"

if ($x_auth_token)
{
try
    {
    if ($global:get_powershell_version -gt 5)
    {
    
    $post_result = Invoke-WebRequest -UseBasicParsing -SkipHeaderValidation -SkipCertificateCheck -Uri $uri -Method Post -Body $JsonBody -ContentType 'application/json' -Headers @{"Accept" = "application/json"; "X-Auth-Token" = $x_auth_token} -ErrorVariable RespErr
    }
    else
    {
    Ignore-SSLCertificates
    $post_result = Invoke-WebRequest -UseBasicParsing -Uri $uri -Method Post -Body $JsonBody -ContentType 'application/json' -Headers @{"Accept" = "application/json"; "X-Auth-Token" = $x_auth_token} -ErrorVariable RespErr
    }
    }
    catch
    {
    Write-Host
    $RespErr
    return
    } 
}


else
{
try
    {
    if ($global:get_powershell_version -gt 5)
    {
    
    $post_result = Invoke-WebRequest -UseBasicParsing -SkipHeaderValidation -SkipCertificateCheck -Uri $uri -Credential $credential -Method Post -Body $JsonBody -ContentType 'application/json' -Headers @{"Accept"="application/json"} -ErrorVariable RespErr
    }
    else
    {
    Ignore-SSLCertificates
    $post_result = Invoke-WebRequest -UseBasicParsing -Uri $uri -Credential $credential -Method Post -Body $JsonBody -ContentType 'application/json' -Headers @{"Accept"="application/json"} -ErrorVariable RespErr
    }
    }
    catch
    {
    Write-Host
    $RespErr
    return
    } 
}

if ($ShareType -eq "Local")
{
    if ($post_result.StatusCode -eq 202 -or $post_result.StatusCode -eq 200)
    {
    $diags_export_local_uri = $post_result.Headers.Location
    $uri = "https://$idrac_ip$diags_export_local_uri"
    Start-Sleep 3
    start $uri
    }
    else
    {
    [String]::Format("- FAIL, statuscode {0} returned. Detail error message: {1}",$post_result.StatusCode,$post_result)
    break
    }

}
else
{
    if ($post_result.StatusCode -eq 202 -or $post_result.StatusCode -eq 200)
    {
    $job_id_search = $post_result.Headers.Location
    $Global:job_id = $job_id_search.Split("/")[-1]
    [String]::Format("`n- PASS, statuscode {0} returned successfully for POST command to create update job ID '{1}'",$post_result.StatusCode, $Global:job_id)
    $Global:create_job_success = "yes"
    }
    else
    {
    [String]::Format("- FAIL, statuscode {0} returned. Detail error message: {1}",$post_result.StatusCode,$post_result)
    return
}
}

}


function loop_job_status
{
if ($Global:create_job_success -eq "no")
{
return
}
Write-Host "- INFO, script will now loop polling the job status until marked completed`n"
while ($true)
{
$loop_time = Get-Date
$uri ="https://$idrac_ip/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/Jobs/$Global:job_id"

    if ($x_auth_token)
{
 try
    {
    if ($global:get_powershell_version -gt 5)
    {
    $result = Invoke-WebRequest -SkipCertificateCheck -SkipHeaderValidation -Uri $uri -Method Get -UseBasicParsing -ErrorVariable RespErr -Headers @{"Accept" = "application/json"; "X-Auth-Token" = $x_auth_token}
    }
    else
    {
    Ignore-SSLCertificates
    $result = Invoke-WebRequest -Uri $uri -Method Get -UseBasicParsing -ErrorVariable RespErr -Headers @{"Accept"="application/json"; "X-Auth-Token" = $x_auth_token}
    }
    }
    catch
    {
    $RespErr
    return
    }
}

else
{
    try
    {
    if ($global:get_powershell_version -gt 5)
    {
    $result = Invoke-WebRequest -SkipCertificateCheck -SkipHeaderValidation -Uri $uri -Credential $credential -Method Get -UseBasicParsing -ErrorVariable RespErr -Headers @{"Accept"="application/json"}
    }
    else
    {
    Ignore-SSLCertificates
    $result = Invoke-WebRequest -Uri $uri -Credential $credential -Method Get -UseBasicParsing -ErrorVariable RespErr -Headers @{"Accept"="application/json"}
    }
    }
    catch
    {
    $RespErr
    return
    }
}
$get_output = $result.Content | ConvertFrom-Json
$job_message = $get_output.Message
$job_message = $job_message.ToLower()

if ($get_output.JobState -eq "Completed")
{
Write-Host "- PASS, $job_message"
break
}
elseif ($get_output.JobState -eq "Failed" -or $job_message.Contains.("fail") -or $job_message.Contains("error"))
{
Write-Host ("- FAIL, job ID final results detected an error")
$get_output
return
}
else
{
Write-Host "- INFO, job ID not marked completed, polling job status again"
Start-Sleep 10
}

}

}


# Run cmdlet

get_powershell_version
setup_idrac_creds

# Check to validate iDRAC version detected supports this feature

$uri = "https://$idrac_ip/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellLCService"
 if ($x_auth_token)
{
 try
    {
    if ($global:get_powershell_version -gt 5)
    {
    $result = Invoke-WebRequest -SkipCertificateCheck -SkipHeaderValidation -Uri $uri -Method Get -UseBasicParsing -ErrorVariable RespErr -Headers @{"Accept" = "application/json"; "X-Auth-Token" = $x_auth_token}
    }
    else
    {
    Ignore-SSLCertificates
    $result = Invoke-WebRequest -Uri $uri -Method Get -UseBasicParsing -ErrorVariable RespErr -Headers @{"Accept"="application/json"; "X-Auth-Token" = $x_auth_token}
    }
    }
    catch
    {
    $RespErr
    Write-Host "`n- WARNING, iDRAC version detected does not support this feature using Redfish API or incorrect iDRAC user credentials passed in.`n"
    return
    }
}

else
{
    try
    {
    if ($global:get_powershell_version -gt 5)
    {
    $result = Invoke-WebRequest -SkipCertificateCheck -SkipHeaderValidation -Uri $uri -Credential $credential -Method Get -UseBasicParsing -ErrorVariable RespErr -Headers @{"Accept"="application/json"}
    }
    else
    {
    Ignore-SSLCertificates
    $result = Invoke-WebRequest -Uri $uri -Credential $credential -Method Get -UseBasicParsing -ErrorVariable RespErr -Headers @{"Accept"="application/json"}
    }
    }
    catch
    {
    $RespErr
    return
    }
}

if ($result.StatusCode -eq 200 -or $result.StatusCode -eq 202)
{
$get_actions = $result.Content | ConvertFrom-Json
$run_diags_action_name = "#DellLCService.RunePSADiagnostics"
$validate_supported_idrac = $get_actions.Actions.$run_diags_action_name
    try
    {
    $test = $validate_supported_idrac.GetType()
    }
    catch
    {
    Write-Host "`n- WARNING, iDRAC version detected does not support this feature using Redfish API or incorrect iDRAC user credentials passed in.`n"
    return
    }
}
else
{
Write-Host "`n- WARNING, iDRAC version detected does not support this feature using Redfish API or incorrect iDRAC user credentials passed in.`n"
return
}

if ($run_diags -and $reboot_type -and $run_mode)
{
execute_remote_diags
loop_job_status
}
elseif ($export -and $ShareType)
{
export_remote_diags_results

if ($ShareType -ne "Local")
    {
    loop_job_status
    }
}

else
{
Write-Host "- FAIL, either incorrect parameter(s) used or missing required parameters(s)"
}


}