"PS-Helper" to execute Power-Shell script as part of an Extended-Object including Server_Info.ps1 example script

Version 3
    Share:|

    Hi,

     

    in order to check certain settings on a Windows Target server that are not available with the default Bladelogic Server Objects by using a Compliance-Job, we have created a Server_Info PowerShell script to do that. This script outputs  in an XML format, as this gives us a good method of structuring the information.

     

    The idea was to use a single script which is registered as a single Extended-Object (either local or global) instead of having the need to have new script/EO combination whenever a new requirement comes in.

     

    The first issue that we had to solve was, how to store & manage that Server_Infro-Script in a central location and execute it on the target whenever needed...

     

    We have chosen to store such a script on the BL-FileServer and created a PS-Helper script which in some way mimics the "scriptutil" command.

     

    This PS-Helper basically does the following:

     

    - Check if the target server has Power-Shell available

    - Copy the script to the target

    - Execute the script

    - Deleting the script from the target again.

     

    PS_HELPER:

    #!/bin/nsh

    #Statics

    BLFS=[YOUR_BL FILESERVER]

    SENSORS_DIR=[PLACE WHERE YOUR PS-SCRIPT LIVES]

     

    #Input Vars

    SENSORS_SCRIPT=$1

    TARGET=$2

    DIR=$3

     

    [ $# -ne 3 ] && echo "Check input variables" && exit 1

     

    #As a first step we have to verify if PowerShell exists on the target

    nexec $TARGET c:\\Windows\\system32\\reg query "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\PowerShell"  > NUL 2>&1

     

    # If PowerShell does not exist we post a stament about it in XML format. Then we exit with "0" to make sure things like compliance don't fail du to "Asset Collection" errors

    if [ $? -ne 0 ] ; then

            echo "<Status><StatusExtendedObject>failed</StatusExtendedObject><Error>Powershell not found</Error></Status>"

            exit 0

    fi

     

    # If PS is found we copy the sensors script to our target server

    cp //$BLFS/$SENSORS_DIR/$SENSORS_SCRIPT //$TARGET/$DIR/$SENSORS_SCRIPT

     

    # Change directory to the target

    cd //$TARGET/$DIR

     

    # Now execute the script and redirect the Error-Output to NUL

    # The mode setting is required as nexec otherwise truncates output

    nexec -i -e cmd /c "mode 1000,50 &&  echo . | c:\\windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe ./$SENSORS_SCRIPT" 2>NUL

     

    [ $? -ne 0 ] && echo "<Status><StatusExtendedObject>failed</StatusExtendedObject><Error>Powershell Script exited with a Non-Zero ExitCode</Error></Status>"

     

    # Finally delete the script and exit with 0

    rm $SENSORS_SCRIPT

    exit 0

     

     

    This is a very stripped down version of the Server_Info PS-Script, which requires PowerShell 2 on the target and outputs in an XML format.

    Copy it to a place like the File-Server and reference to its name in the EO-definition.

    #requires -version 2

     

    #This function will be used on the final $xmlDoc to pretty-print it

    function Format-XML ([xml]$xml, $indent=2)

    {

        $StringWriter = New-Object System.IO.StringWriter

        $XmlWriter = New-Object System.XMl.XmlTextWriter $StringWriter

        $xmlWriter.Formatting = "indented"

        $xmlWriter.Indentation = $Indent

        $xml.WriteContentTo($XmlWriter)

        $XmlWriter.Flush()

        $StringWriter.Flush()

        Write-Output $StringWriter.ToString()

    }

     

    #initial XML Document creation

    [xml]$xmlDoc = New-Object system.Xml.XmlDocument

    $xmlDoc.PreserveWhitespace = $true

    $xmlDoc.LoadXml("<?xml version=`"1.0`" encoding=`"utf-8`"?><ServerInfo></ServerInfo>")

     

     

    #Status##############################################

    # As we run in Powershell at this point we set the Status of the Extended Object to success

    # and will use this inside the top-level element "Satus"

    #Create top element

    $top_status_element = $xmlDoc.CreateElement("Status")

     

    # Create the blank Status hash

    $status_hash =@{}

    $status_hash.Add("StatusExtendedObject","success")

     

    # Now generate the status XML from the Hash Table

    # This may look a bit overkill here but offers the oppurtunity to add more elements to the top_status_element later

    foreach ($key in $status_hash.keys) {

            $status_entries = $xmlDoc.CreateElement($key)

            $status_entries_text = $xmlDoc.CreateTextNode($status_hash.$key)

            $status_entries.AppendChild($status_entries_text)  | Out-Null

            $top_status_element.AppendChild($status_entries)  | Out-Null

            }

     

    # Add the status node to the top XML doc

    $xmlDoc.LastChild.AppendChild($top_status_element)  | Out-Null

    #ENDOF Status##############################################

     

     

     

    #BuildInfo##############################################

    # create top element

    $top_buildinfo_element = $xmlDoc.CreateElement("BuildInfo")

     

    # Create the blank buildinfo hash

    $buildinfo_hash =@{}

     

    #Verify if we run this in a domain user context

    $domain=[Environment]::UserDomainName

    $machine=[Environment]::MachineName

     

    if ($domain.CompareTo($machine) -match "1")

            {

                    # Query the OU of this server from AD

                    $objDomain = New-Object System.DirectoryServices.DirectoryEntry

                    $strFilter = "(&(objectCategory=computer)(name=" + $env:computername + "))"

                    $objSearcher = New-Object System.DirectoryServices.DirectorySearcher

                    $objSearcher.SearchRoot = $objDomain

                    $objSearcher.Filter = $strFilter

                    $OU = $objSearcher.FindOne().Path

                    } else {

                    $OU="unkown. Make sure the ExtendedObject was executed in a domain user context"

            }

     

    # Store the Hostname

    $HOSTNAME=$env:computername

     

    # Figure Out the PowerShellVersion

    $PowerShellVersion =get-host | % {$_.Version}

     

    # First we add the common items that exist for each type of build to the buildinfo_hash

    $buildinfo_hash.Add("Hostname",$HOSTNAME)

    $buildinfo_hash.Add("OU",$OU)

    $buildinfo_hash.Add("LastBootUptime",(([WMI]'').ConvertToDateTime((Get-WmiObject Win32_OperatingSystem).lastbootuptime).toUniversalTime()).ToString('u'))

    $buildinfo_hash.Add("InstallDate",(([WMI]'').ConvertToDateTime((Get-WmiObject Win32_OperatingSystem).InstallDate).toUniversalTime()).ToString('u'))

    $buildinfo_hash.Add("PowershellVersion",$PowerShellVersion)

     

    # now create the xml stuff from the buildinfo hash

    foreach ($key in $buildinfo_hash.keys) {

            $buildinfo_entries = $xmlDoc.CreateElement($key)

            $buildinfo_entries_text = $xmlDoc.CreateTextNode($buildinfo_hash.$key)

            $buildinfo_entries.AppendChild($buildinfo_entries_text)  | Out-Null

            $top_buildinfo_element.AppendChild($buildinfo_entries)  | Out-Null

            }

     

    # Add the buildinfo element node to the top XML doc

    $xmlDoc.LastChild.AppendChild($top_buildinfo_element)  | Out-Null

    #ENDOF BuildInfo##############################################

     

     

    #AppliedPolicies#############################################

    #Create top element

    $top_policies_element = $xmlDoc.CreateElement("AppliedPolicies")

     

    # Now create elements for each Applied Policy

    foreach ($policy in (Get-WmiObject -Namespace "root/rsop/computer" -Class "RSOP_GPO" | Where { $_.filterallowed -match "True"} ))

            {

            $policies_entries = $xmlDoc.CreateElement("Policy")

            $policies_entries_text = $xmlDoc.CreateTextNode($policy.name)

            $policies_entries.AppendChild($policies_entries_text)  | Out-Null

            $top_policies_element.AppendChild($policies_entries) | Out-Null

            }

     

    # Add the policies node to the top XML doc

    $xmlDoc.LastChild.AppendChild($top_policies_element)  | Out-Null

    #ENDOF AppliedPolicies##############################################

     

     

     

     

    # ComplianceInfo####################################################

    # create top element

    $top_complianceinfo_element = $xmlDoc.CreateElement("ComplianceInfo")

     

    #Create the blank complianceinfo hash

    $complianceinfo_hash=@{}

     

    # Check for whatever you are looking for and add the results to the hash

    $complianceinfo_hash.Add("IsTheServerCompliant","Yes")

    $complianceinfo_hash.Add("DoesItUseAFancyWallpaper","No")

    $complianceinfo_hash.Add("DidYouHaveCheezburersForLunch","Yes")

     

     

    # Now generate the complianceinfo XML from the Hash Table

    foreach ($key in $complianceinfo_hash.keys) {

            $complianceinfo_entries = $xmlDoc.CreateElement($key)

            $complianceinfo_entries_text = $xmlDoc.CreateTextNode($complianceinfo_hash.$key)

            $complianceinfo_entries.AppendChild($complianceinfo_entries_text)  | Out-Null

            $top_complianceinfo_element.AppendChild($complianceinfo_entries)  | Out-Null

            }

     

    # Add the complianceinfo node to the XML doc

    $xmlDoc.LastChild.AppendChild($top_complianceinfo_element)  | Out-Null

    #ENDOF ComplianceInfo####################################################

     

     

     

    #Finally write out the XML that we have created using the nice format-xml function################################################################################

    format-xml($xmlDoc)

     

    Once you have created the PS-Helper script in a place of your choice (We use the File-Server here), you can go ahead and create an Extended-Object like this. Note: Use the name and path that you have given your script.:

    EO.jpg

     

    Once everything is in place you should be able to browse that Extended-Object and the output should be similiar to this:

     

    EO_1.jpg

     

    I hope this helps you getting started when you have to achieve a similiar task.

    Please let me know if you have any questions or problems and i will be happy to help.

     

    Cheers

    Steffen