Help with ForEach-Object

This topic contains 3 replies, has 3 voices, and was last updated by  Ryan 3 months, 4 weeks ago.

  • Author
    Posts
  • #77760

    Ryan
    Participant

    Hi, I'm a bit of a scripting newbie and I'm having trouble with using ForEach-Object. In the script below I'm getting a list of servers from a txt file and then iterating through the list to get information like server clock time and last reboot time. I feel like I'm doing the ForEach-Object loop correctly but I must be missing something because I'm getting an error and my html file turns out blank. Thanks for any guidance you can offer!

    ****Begin Script Snippet*****

    #Get computer list
    #$computers = Get-Content 'D:\SWLIB\Scripts\PoSh\servers_test.txt'
    $computers = DEVSQL01

    function get-uptime ( $computers ){
    #Get ServerName & Clock Time
    $computers | ForEach-Object {$_}
    $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $_
    $cdt = @{Name='Server Clock Time';Expression={$_.ConvertToDateTime($os.LocalDateTime)}}
    $CSName = @{Name='ServerName';Expression={$os.CSName}}
    #Get Uptime and LastReboot
    $LastBootUpTime = @{Name='LastBootUpTime';Expression={$os.ConvertToDateTime($os.LastBootUpTime)}}

    #Export to HTML with some formatting
    $a = ""
    $a = $a + "BODY{background-color:powderblue;}"
    $a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
    $a = $a + "TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;}"
    $a = $a + "TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;}"
    $a = $a + ""
    Select-Object -Property $CSName, $cdt, $LastBootUpTime | ConvertTo-HTML -head $a
    }

    #Create html file
    function create-html(){
    get-uptime $computers | Out-File C:\scripts\Ryan\test\ServersTime.htm
    }

    create-html

    *****End Script Snippet****

    Here's the error being returned:

    DEVSQL01 : The term DEVSQL01' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if
    a path was included, verify that the path is correct and try again.
    At line:4 char:14
    + $computers = DEVSQL01
    + ~~~~~~~~~~~~~~
    + CategoryInfo : ObjectNotFound: (DEVSQL01:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

    Get-WmiObject : Cannot validate argument on parameter 'ComputerName'. The argument is null or empty. Provide an argument that is not null or empty, and then try the
    command again.
    At line:10 char:67
    + $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $_
    + ~~
    + CategoryInfo : InvalidData: (:) [Get-WmiObject], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetWmiObjectCommand

  • #77761

    Don Jones
    Keymaster

    So, I guess I'm a little confused about what the intent is.

    In your Get-Uptime function, ForEach-Object is simply emitting each computer name to the pipeline, which becomes your function's output. $_ is only evaluated inside the ForEach-Object script block; $_ has no intrinsic meaning anyplace else. You probably should be using a ForEach _construct_ here, versus the ForEach-Object _cmdlet_.

    ForEach ($computer in $computers) {
      # do everything in here, using $computer
    }
    

    Your error, however, is from something entirely different. It's because DEVSQL01 isn't in quotation marks, so PowerShell is attempting to run a command called DEVSQL01, which obviously isn't a thing.

    At the risk of coming across mean, I can see you heading down a couple of dark and stormy paths. Realizing that you have a job that you need to get done, it would – in my opinion – be good for you to backtrack a bit. Books like my "Learn Windows PowerShell in a Month of Lunches" and "Learn PowerShell Scripting in a Month of Lunches" will go a LOOOONG way toward course-correcting you, here. Some of what you're running into is the Internet's fault; PowerShell has some subtleties under the hood that are not easy to pick up on your own, and it's easy when copying code from the Internet to not realize all of those gotchas. The books are designed to stop that and get you on the right path. It doesn't require some up-front investment, but it'll save you a ton of bloody foreheads from smashing your head into your desk.

    For example, you're doing a lot of unnecessary work in manually creating HTML; my EnhancedHTML2 module (covered in our free ebook on HTML reports) would make that whole task infinitely simpler, if you took the right coding approach. "Create" is also not a legit verb name in PowerShell :).

  • #77769

    Aaron Hardy
    Participant

    If I may concur with Don...

    As a Don Jones 'PowerShell alumni', I can say that diving into his books (with other co-authors) is the best investment you can make for learning PowerShell properly and its best practices. I hated PowerShell until I got to these books – now I love it.

    There is no hype with the 'Month of Lunches' series – you can actually get through a chapter over a lunch break and they're not super heavy. You'll also get up to speed quickly. I have tried many materials on PowerShell but these books are, by far, the best.

    A lot of internet resources spawn confusion to no end and commonly illustrate bad examples far from best practices, so I tend to get the 'idea' of what to do and implement it based on what I've learned from the books or other quality materials.

    Another excellent book is The PowerShell Scripting and Toolmaking Book. Tackle this after the others Don mentioned. There are many other good materials but these are the best to get started with.

    If you're in IT, you need to learn PowerShell. If you're learning PowerShell, you need to get the books, at minimum.

    All the best.

  • #77782

    Ryan
    Participant

    Hi Don,

    Thank you for taking the time to respond. I just ordered the book on Amazon and it should arrive in two days. Once I get through that I'll likely pre-order the other book.

    With my newbie, at best, abilities I tried to follow your guidance with the modifications below.

    *****begin snippet****
    #Get computer list
    $computers = "DEVSQL01"

    function get-uptime ( $computers ){
    #Get ServerName & Clock Time
    Foreach ($computer in $computers) {
    $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computer
    $cdt = @{Name='Server Clock Time';Expression={$computer.ConvertToDateTime($os.LocalDateTime)}}
    $CSName = @{Name='ServerName';Expression={$os.CSName}}
    #Get Uptime and LastReboot
    $LastBootUpTime = @{Name='LastBootUpTime';Expression={$os.ConvertToDateTime($os.LastBootUpTime)}}

    #Export to HTML with some formatting
    $a = ""
    $a = $a + "BODY{background-color:powderblue;}"
    $a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
    $a = $a + "TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;}"
    $a = $a + "TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;}"
    $a = $a + ""
    Select-Object -Property $CSName, $cdt, $LastBootUpTime | ConvertTo-HTML -head $a
    }}

    #Create html file
    function create-html(){
    get-uptime $computers | Out-File C:\scripts\Ryan\test\ServersTime.htm
    }

    create-html
    *****end snippet*****

    I'm no longer receiving any error messages but the html file is blank. It would be really helpful if I could get this script running for my immediate needs. Later, I'll enjoy re-writing it from scratch after reading the book. I'll also check out the HTML module you mentioned.

    Regards,

    Ryan

You must be logged in to reply to this topic.