Invoke-ServerVirtualAcCycleREDFISH.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
   Cmdlet using iDRAC with Redfish API to perform server virtual a/c power cycle.
.DESCRIPTION
   Cmdlet using iDRAC with Redfish API to perform server virtual a/c power cycle. DMTF action will only a/c cycle the server and not drain flea power. OEM action will a/c cycle the server and also drain flea power (same behavior as pulling a/c power cables). Note to run OEM action the server must be powered off first.
   Supported parameters:
   - idrac_ip: Pass in the iDRAC IP
   - idrac_username: Pass in the iDRAC user name
   - idrac_password: Pass in the iDRAC user name password
   - x_auth_token: Pass in iDRAC X-Auth token session to execute cmdlet instead of username / password (recommended)
   - get_current_power_state: Get current server power state
   - dmtf_virtual_ac_cycle: Perform virtual server a/c power cycle the server using DMTF method. Note this method will not completely drain flea power
   - oem_virtual_ac_cycle: Perform virtual server a/c cycle the server using OEM method. Note: This method will completely drain flea power which is equivalent to the action of disconnecting power cables. Note server must be powered off first before running this OEM action.
   - power_off: Power off the server first before running OEM action to perform server virtual a/c power cycle
   - final_power_state: Final server power state after performing OEM server virtual a/c cycle. Note this optional argument is only supported for OEM virtual a/c power cycle. Note if this argument is not passed in server will stay in Off state after a/c cycle. Supported values: On and Off
.EXAMPLE
   Invoke-ServerVirtualAcCycleREDFISH -idrac_ip 192.168.0.120 -idrac_username root -idrac_ password calvin -get_current_power_state
   This example will get current server power state
.EXAMPLE
   Invoke-ServerVirtualAcCycleREDFISH -idrac_ip 192.168.0.120 -get_current_power_state
   This example will first prompt to enter iDRAC username and password using Get-Credentials, then get current server power state.
.EXAMPLE
   Invoke-ServerVirtualAcCycleREDFISH -idrac_ip 192.168.0.120 get_current_power_state -x_auth_token 7bd9bb9a8727ec366a9cef5bc83b2708
   This example will get current server power state using iDRAC X-auth token session.
.EXAMPLE
   Invoke-ServerVirtualAcCycleREDFISH -idrac_ip 192.168.0.120 -idrac_username root -idrac_password calvin -dmtf_virtual_ac_cycle
   This example will perform DMTF action to virtual a/c power cycle the server.
.EXAMPLE
   Invoke-ServerVirtualAcCycleREDFISH -idrac_ip 192.168.0.120 -idrac_username root -idrac_password calvin -oem_virtual_ac_cycle -power_off
   This example will first power off the server, then perform OEM action to virtual a/c power cycle the server. After a/c cycle is complete server will still be in Off state.
.EXAMPLE
   Invoke-ServerVirtualAcCycleREDFISH -idrac_ip 192.168.0.120 -idrac_username root -idrac_password calvin -oem_virtual_ac_cycle -power_off -final_power_state On
   This example will first power off the server, then perform OEM action to virtual a/c power cycle the server. After a/c cycle is complete server will automatically power on.
#>


function Invoke-ServerVirtualAcCycleREDFISH {

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]$get_current_power_state,
    [Parameter(Mandatory=$False)]
    [switch]$dmtf_virtual_ac_cycle,
    [Parameter(Mandatory=$False)]
    [switch]$oem_virtual_ac_cycle,
    [Parameter(Mandatory=$False)]
    [switch]$power_off,
    [ValidateSet("Off", "On")]
    [Parameter(Mandatory=$False)]
    [string]$final_power_state
    )

# 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 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 setup iDRAC creds

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)
}
}

# Get current server power state

function get_current_server_power_state
{

$uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/"
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
    }
}

if ($result.StatusCode -eq 200)
{
    #[String]::Format("- PASS, statuscode {0} returned successfully to get current power state",$result.StatusCode)
}
else
{
    [String]::Format("- FAIL, statuscode {0} returned",$result.StatusCode)
    return
}

$get_content = $result.Content | ConvertFrom-json
$power_state = $get_content.PowerState

Write-Host "`n- INFO, current server power state: $power_state`n"
}


# DMTF action to perform server virtual a/c power cycle

function dmtf_server_virtual_ac_cycle
{

$JsonBody = @{ "ResetType" = "PowerCycle"
    } | ConvertTo-Json -Compress


$uri = "https://$idrac_ip/redfish/v1/Chassis/System.Embedded.1/Actions/Chassis.Reset"
 if ($x_auth_token)
{
try
    {
    if ($global:get_powershell_version -gt 5)
    {
    
    $result1 = 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
    $result1 = 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)
    {
    
    $result1 = 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
    $result1 = 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 ($result1.StatusCode -eq 204)
{
    Write-Host
    [String]::Format("- PASS, statuscode {0} returned to perform DMTF server virtual a/c power cycle",$result1.StatusCode)
}
else
{
    [String]::Format("- FAIL, statuscode {0} returned",$result1.StatusCode)
    $result1
    return
}
}

# OEM action to perform server virtual a/c power cycle

function oem_server_virtual_ac_cycle
{

if ($power_off)
{
$JsonBody = @{ "ResetType" = "ForceOff"} | ConvertTo-Json -Compress

$uri = "https://$idrac_ip/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset"
if ($x_auth_token)
{
try
    {
    if ($global:get_powershell_version -gt 5)
    {
    
    $result1 = 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
    $result1 = 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)
    {
    
    $result1 = 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
    $result1 = 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 ($result1.StatusCode -eq 204)
{
    Write-Host
    [String]::Format("- PASS, statuscode {0} returned to power off the server",$result1.StatusCode)
    Start-Sleep 10
}
else
{
    [String]::Format("- FAIL, statuscode {0} returned",$result1.StatusCode)
    $result1
    return
}
}

$JsonBody = @{ "ResetType" = "PowerCycle"}

if ($final_power_state)
{
$JsonBody["FinalPowerState"] = $final_power_state
}

$JsonBody = $JsonBody | ConvertTo-Json -Compress

$uri = "https://$idrac_ip/redfish/v1/Chassis/System.Embedded.1/Actions/Oem/DellOemChassis.ExtendedReset"
if ($x_auth_token)
{
try
    {
    if ($global:get_powershell_version -gt 5)
    {
    
    $result1 = 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
    $result1 = 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)
    {
    
    $result1 = 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
    $result1 = 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 ($result1.StatusCode -eq 204)
{
    Write-Host
    [String]::Format("- PASS, statuscode {0} returned to perform OEM server virtual a/c power cycle",$result1.StatusCode)
}
else
{
    [String]::Format("- FAIL, statuscode {0} returned",$result1.StatusCode)
    $result1
    return
}
}

############
# Run code #
############

get_powershell_version 
setup_idrac_creds

# Code to check for supported iDRAC version installed

$uri = "https://$idrac_ip/redfish/v1/Chassis/System.Embedded.1"
   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
    }
}
        if ($result.StatusCode -eq 200 -or $result.StatusCode -eq 202)
        {
        }
        else
        {
        Write-Host "`n- WARNING, iDRAC version detected does not support this feature using Redfish API"
        $result
        return
        }



if ($get_current_power_state)
{
get_current_server_power_state
}
elseif ($dmtf_virtual_ac_cycle)
{
dmtf_server_virtual_ac_cycle
}
elseif ($oem_virtual_ac_cycle)
{
oem_server_virtual_ac_cycle
}
else
{
Write-Host "`n- FAIL, either invalid parameter value passed in or missing required parameter"
return
}

}