Errors when trying to run a script that checks remote machines

This topic contains 8 replies, has 3 voices, and was last updated by Profile photo of Richard Siddaway Richard Siddaway 5 months ago.

Viewing 9 posts - 1 through 9 (of 9 total)
  • Author
    Posts
  • #38242
    Profile photo of Ed O'Connor
    Ed O’Connor
    Participant

    Hi,

    I am trying to customize a script to give me a report on certain items on multiple servers and having issues.

    Basically I am running a command against AD to get me the servers I want to run against. The problem is as follows:

    For any Get-WMIObject commands I get a: "Get-WMIObject : The RPC server is unavailable.

    For any Get-WindowsFeature commands I get a : "Get-WindowsFeature : Cannot convert 'System.String[]' to the type 'System.String' required by the parameter 'ComputerName'.

    Here is my code:

    function Write-HTML
    {
    
          [CmdletBinding(SupportsShouldProcess=$True)]
    param([Parameter(Mandatory=$false,
          ValueFromPipeline=$true)]
          [string]$FilePath = "C:\temp\PreDSCServerCheck.html",
          [string[]]$Computername = $env:COMPUTERNAME,
    $Css='table{margin:auto; width:95%}
                  Body{background-color:Green; Text-align:Center;}
                    th{background-color:black; color:white;}
                    td{background-color:Grey; color:Black; Text-align:Center;}
         ' )
    
    Begin{ Write-Verbose "HTML report will be saved $FilePath" }
    
    Process{ $Hardware = Get-WmiObject -class Win32_ComputerSystem -ComputerName $Computername | 
             Select-Object Name,Domain,Manufacturer,Model,NumberOfLogicalProcessors,
             @{ Name = "Installed Memory (GB)" ; Expression = { "{0:N0}" -f( $_.TotalPhysicalMemory / 1gb ) } } |
             ConvertTo-Html -Fragment -As Table -PreContent "Hardware" | 
             Out-String
    
    $PercentFree = Get-WmiObject Win32_LogicalDisk -ComputerName $Computername | 
                   Where-Object { $_.DriveType -eq "3" } | Select-Object SystemName,VolumeName,DeviceID,
                   @{ Name = "Size (GB)" ; Expression = { "{0:N1}" -f( $_.Size / 1gb) } },
                   @{ Name = "Free Space (GB)" ; Expression = {"{0:N1}" -f( $_.Freespace / 1gb ) } },
                   @{ Name = "Percent Free" ; Expression = { "{0:P0}" -f( $_.FreeSpace / $_.Size ) } } |
                   ConvertTo-Html -Fragment -As Table -PreContent "Available Disk Space" | 
                   Out-String
        
    $Restarted = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Computername | Select-Object Caption,CSName,
                 @{ Name = "Last Restarted On" ; Expression = { $_.Converttodatetime( $_.LastBootUpTime ) } } |
                 ConvertTo-Html -Fragment -As Table -PreContent "Last Boot Up Time" | 
                 Out-String
    
    Function WindowsFeatures
    {
        $WindowsFeaturesData = Get-WindowsFeature -ComputerName $Computername | 
                            Where-Object {$_.Installed -eq $True} | 
                            Select-Object Name,DisplayName
    
        $WindowsFeaturesDisplayName = Get-WindowsFeature -ComputerName $Computername | 
                            Where-Object {$_.Installed -eq $True} | 
                            Select-Object DisplayName |
                            Export-csv c:\temp\WindowsFeatures.csv
    
    
        $list = $null
    
        $list = diff -ReferenceObject $(get-content "C:\temp\WindowsFeatures.csv") -DifferenceObject $(get-content "C:\Scripts\PreDSCConfigCheck\WindowsFeaturesMasterList.csv") -PassThru
    
        $WindowsFeaturesCompliance = If ($list -eq $null)  {[pscustomobject]@{ ' Status ' = "The required Windows Features are present"; "DSC Compliant" = "Yes" }}
                                    Else {[pscustomobject]@{ ' Status ' = "***NON COMPLIANT*** One or more of the required Windows Features are not present"; "DSC Compliant" = "No" }}
    
    
        Remove-Item "C:\temp\WindowsFeatures.csv"
    
        $WindowsFeaturesData | ConvertTo-Html -Fragment -PreContent "Installed Windows Features" | Out-String
        $WindowsFeaturesCompliance | ConvertTo-Html -Fragment -As table  | out-string
       
    }
    
    $WindowsFeatures = WindowsFeatures 
    
    
    
    $Report = ConvertTo-Html -Title "$Computername" `
                             -Head "PowerShell Reporting$ComputernameThis report was ran: $(Get-Date)" `
                             -Body "$Hardware $PercentFree $Restarted $WindowsFeatures $Css" }
    
    End{ $Report | Out-File $Filepath ; Invoke-Expression $FilePath }
    
    }
    
    #Get computer objects from AD
    function GetComputers 
    {
        import-module ActiveDirectory
        Get-ADComputer -SearchBase "OU=DSC Managed Nodes,OU=SERVERS,OU=NYC,OU=Americas,DC=lab2,DC=mckinsey,DC=com" -Filter * | select-object name
    }
    $computers = GetComputers | out-string
    
    
    
    Foreach ($ServerNames in $Computers) {
    
    
    Write-HTML -ComputerName $ServerNames
    
    }
    

    This does work perfectly if don't query AD for the computer names and just hard code them in. Please assist.

    Thanks in advance, Ed

    #38243
    Profile photo of Don Jones
    Don Jones
    Keymaster

    So, the WMI problem will be expected on newer services as the WMI service is disabled by default. Keep in mind that it's deprecated, with CIM being the way forward.

    For Get-WindowsFeature, it sounds as if your'e feeding -ComputerName an array of strings, which it can't accept. It can only run against one computer at a time. You've defined $ComputerName as a [string[]] array, not a [string] of one object. Even if the array only has one object, it's still an array.

    #38245
    Profile photo of Ed O'Connor
    Ed O’Connor
    Participant

    Is there a way to get the output from my code below to output it as a string of one object somehow? I am trying all different things but it is not working. I thought the 'foreach' command would break it down to a individual item.

    #Get computer objects from AD
    function GetComputers 
    {
        import-module ActiveDirectory
        Get-ADComputer -SearchBase "OU=DSC Managed Nodes,OU=SERVERS,OU=NYC,OU=Americas,DC=lab2,DC=mckinsey,DC=com" -Filter * | select-object name
    }
    $computers = GetComputers | out-string
    
    
    
    Foreach ($ServerNames in $Computers) {
    
    
    Write-HTML -ComputerName $ServerNames
    
    }
    
    #38247
    Profile photo of Don Jones
    Don Jones
    Keymaster

    So, $computers is just a collection of strings, which were converted from Computer objects. What is it you want, just computer names? But why as one object? I guess I'm confused about the goal, about how this information is supposed to be consumed.

    $computers is already a single string object – Out-String will have arranged that for you. There's nothing to enumerate. ForEach can't "break down" a single object; it's job is to enumerate through a collection of objects.

    #38249
    Profile photo of Ed O'Connor
    Ed O’Connor
    Participant

    Sorry Don, I think I am getting confused.

    As I mentioned this script works fine if I remove the portion where I get the computer names from AD and the foreach command and simply hard code it into the 'Write-HTML' function as such:

    If I do this everything works perfectly.

    Write-HTML -ComputerName xxxServer01

    It will also work if I do:

    Write-HTML -ComputerName xxxServer01, xxxServer02, etc....

    What I am trying to do is to find a way to get all the servers I want to get information on (run the script against) automatically from AD as this script will become a scheduled task and if machines are added that meet the criteria specified in my Get-ADComputer command they will automatically be added to the report and I don't have to keep modifying the report every time a server is added (assuming I am even told).

    #38260
    Profile photo of Don Jones
    Don Jones
    Keymaster

    Yeah. I think I know why you're confused. Let's take it as given that your Write-HTML works if you pass it a single string, or if you pass it a collection of strings (which is how PowerShell interprets comma-separated lists).

    This line:

    Get-ADComputer -SearchBase "OU=DSC Managed Nodes,OU=SERVERS,OU=NYC,OU=Americas,DC=lab2,DC=mckinsey,DC=com" -Filter * | select-object name

    Does not do what you think it does. This does not return a collection of strings which are computer names. It returns a collection of objects, which have a Name property, which contains a string that is a computer name.

    This:

    $computers = GetComputers | out-string

    Is compounding the problem by rendering those objects as one giant string, which PowerShell no longer sees as structured data. ForEach can't enumerate across the contents of a string, which is what you tried to do.

    Try using this:

    $computers = GetComputers | Select -Expand Name

    Instead. You should then be able to:

    Write-HTML -ComputerName $computers

    http://powershell.org/kb/properties-vs-values/ for background on why -Expand is different.

    #38261
    Profile photo of Ed O'Connor
    Ed O’Connor
    Participant

    FYI, I was able to get this working as I liked. Here is what i had to do:

    1) Where I call the function and specified the computer name I had to enter $($ServerNames.name)
    ex... Write-HTML -ComputerName $($ServerNames.name)

    2) For the Get-Windows feature commands I had to have it like this:
    ex.... Get-WindowsFeature -ComputerName $($ServerNames.name)

    By doing it like this everything works perfectly.

    My code:

    function Write-HTML
    {
    
          [CmdletBinding(SupportsShouldProcess=$True)]
    param([Parameter(Mandatory=$false,
          ValueFromPipeline=$true)]
          [string]$FilePath = "C:\temp\$($ServerNames.name).html",
          [string[]]$Computername = $env:COMPUTERNAME,
    $Css='table{margin:auto; width:95%}
                  Body{background-color:Green; Text-align:Center;}
                    th{background-color:black; color:white;}
                    td{background-color:Grey; color:Black; Text-align:Center;}
         ' )
    
    Begin{ Write-Verbose "HTML report will be saved $FilePath" }
    
    Process{ $Hardware = Get-WmiObject -class Win32_ComputerSystem -ComputerName $Computername | 
             Select-Object Name,Domain,Manufacturer,Model,NumberOfLogicalProcessors,
             @{ Name = "Installed Memory (GB)" ; Expression = { "{0:N0}" -f( $_.TotalPhysicalMemory / 1gb ) } } |
             ConvertTo-Html -Fragment -As Table -PreContent "Hardware" | 
             Out-String
    
    $PercentFree = Get-WmiObject Win32_LogicalDisk -ComputerName $Computername | 
                   Where-Object { $_.DriveType -eq "3" } | Select-Object SystemName,VolumeName,DeviceID,
                   @{ Name = "Size (GB)" ; Expression = { "{0:N1}" -f( $_.Size / 1gb) } },
                   @{ Name = "Free Space (GB)" ; Expression = {"{0:N1}" -f( $_.Freespace / 1gb ) } },
                   @{ Name = "Percent Free" ; Expression = { "{0:P0}" -f( $_.FreeSpace / $_.Size ) } } |
                   ConvertTo-Html -Fragment -As Table -PreContent "Available Disk Space" | 
                   Out-String
        
    $Restarted = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Computername | Select-Object Caption,CSName,
                 @{ Name = "Last Restarted On" ; Expression = { $_.Converttodatetime( $_.LastBootUpTime ) } } |
                 ConvertTo-Html -Fragment -As Table -PreContent "Last Boot Up Time" | 
                 Out-String
    
    Function WindowsFeatures
    {
        $WindowsFeaturesData = Get-WindowsFeature -ComputerName $($ServerNames.name) | 
                            Where-Object {$_.Installed -eq $True} | 
                            Select-Object Name,DisplayName
    
        $WindowsFeaturesDisplayName = Get-WindowsFeature -ComputerName $($ServerNames.name) | 
                            Where-Object {$_.Installed -eq $True} | 
                            Select-Object DisplayName |
                            Export-csv c:\temp\WindowsFeatures.csv
    
    
        $list = $null
    
        $list = diff -ReferenceObject $(get-content "C:\temp\WindowsFeatures.csv") -DifferenceObject $(get-content "C:\Scripts\PreDSCConfigCheck\WindowsFeaturesMasterList.csv") -PassThru
    
        $WindowsFeaturesCompliance = If ($list -eq $null)  {[pscustomobject]@{ ' Status ' = "The required Windows Features are present"; "DSC Compliant" = "Yes" }}
                                    Else {[pscustomobject]@{ ' Status ' = "***NON COMPLIANT*** One or more of the required Windows Features are not present"; "DSC Compliant" = "No" }}
    
    
        Remove-Item "C:\temp\WindowsFeatures.csv"
    
        $WindowsFeaturesData | ConvertTo-Html -Fragment -PreContent "Installed Windows Features" | Out-String
        $WindowsFeaturesCompliance | ConvertTo-Html -Fragment -As table  | out-string
       
    }
    
    $WindowsFeatures = WindowsFeatures 
    
    
    
    $Report = ConvertTo-Html -Title "$Computername" `
                             -Head "PowerShell Reporting$ComputernameThis report was ran: $(Get-Date)" `
                             -Body "$Hardware $PercentFree $Restarted $WindowsFeatures $Css" }
    
    End{ $Report | Out-File $Filepath ; Invoke-Expression $FilePath }
    
    }
    
    #Get computer objects from AD
    
    $computers = Get-ADComputer -SearchBase "OU=DSC Managed Nodes,OU=SERVERS,OU=NYC,OU=Americas,DC=lab2,DC=mckinsey,DC=com" -Filter * | select-object name
    
    
    Foreach ($ServerNames in $Computers) {
    
    #$NodeName = $ServerNames
    
    Write-HTML -ComputerName $($ServerNames.Name)
    
    }
    

    Thanks for the input

    #38262
    Profile photo of Ed O'Connor
    Ed O’Connor
    Participant

    Thank You Don, I updated just around the same time you did. I understand what you are saying now.

    #38286

    If your remote machines are using PowerShell 3.0 or later you should swap to using a CIM session rather than using separate WMI calls

Viewing 9 posts - 1 through 9 (of 9 total)

You must be logged in to reply to this topic.