Can my code be more compact,I am still learning PS,Please Guide me on Logging.

This topic contains 5 replies, has 2 voices, and was last updated by Profile photo of Matt McNabb Matt McNabb 2 years ago.

  • Author
    Posts
  • #20612
    Profile photo of Sikander Mirza
    Sikander Mirza
    Participant

    Hi,
    I have started learning PS few months back, i don't have any prior scripting experience.
    I just have the knowledge of Programming basics.I started with Technet/scriptcenter recorded
    webcasts, Don Jones all PS tutorials available on YouTube, MVA JumpStarts and i also follow
    DJones Ebooks PS in a month of lunches and PS ToolMaking in a ....,
    After months of practice i feel good with the ps cmdlets and its syntax but where scripting is
    concerned i am lost, don't know the approach, the thought process, the script which i have
    uploaded is totaly written by me in PS ISE, the requirement is to get bios,processor,ip,memory,
    drive,process,info, i want a log to be created each time i run it.
    My question is please have a look on the code and suggest is this a good approch or can the code
    be more compact, and after each Cmdlet instead of writing output can't we have only one line to
    log all the info?
    This may not be a challenging question to you but i need proper guidance.
    and a request to Don Jones , Sir please record a free scripting tutorial, and it should be a senario
    based such as take a fictious company and some fictious requirements like getting the ipaddress,
    dns,domain info and many other things and logging it to a text file, the how to approach, how to start
    the script from the scratch etc, how the thought process should be.
    It will be a great help for the PS Beginners like me and many others.
    Thanks,
    Sikander Mirza.

  • #20618
    Profile photo of Matt McNabb
    Matt McNabb
    Participant

    Don (and others) have been providing exactly what you're requesting for years now. I haven't read the Month of Lunches book, but from the chapter list it looks like you would want to focus on Chapter 19, 'Input and Output.' There is a ton out there on best practices. Try here to start:

    I have used Don's video instruction on CBT Nuggets and it's fantastic, but his books also cover a lot of the same material so you should be good to go with the one you already have.

    One of the most important rules you need to grasp right off the bat is to output objects, not text. It appears you have heard that you shouldn't be using Write-Host since you are using Write-Output instead, but you are mostly just outputting strings which is essentially the same thing. There are tons of articles out there that detail how you can combine output from several sources into a single object to output. Here is a classic article that details the many ways you can create your own objects and even shows examples of combining output from different WMI classes:

    http://technet.microsoft.com/en-us/magazine/hh750381.aspx

    Some tips I would start with:

    1. Do less. Separate all of the logging to a file and displaying to the console stuff out. You only need to get information and output objects in your function. You can worry about writing to a file later when you run the function.

    2. You're using Write-Output on almost every line. You don't need it. Anything in your function that doesn't get captured in a variable will be output to the console.

    3. Forget about all the Select-Objects your are using when creating your variables. Just capture the object you need and you can access it's properties later. Most of the time you only need Select-Object at the command line. It can be used as a shortcut to custom objects, but the syntax is god-awful.

    4. Don't use square brackets in your property names. These are special characters in Powershell so accessing the properties could become confusing.

    5. Figure out what you want to do before you start writing a function. This is the most important lesson I have learned. We tend to run commands to figure things out and then simply copy those commands into a script try to turn that into a function. Sometimes what you end up with is a huge mish-mash of related commands and duplicated work. I have started to outline every single function I write prior to actually writing any code. I write comments into the function that describe what it should be doing and then I find the appropriate way to achieve that behavior.

    Here is a short example of what I might do with some of the information you are gathering:

    Function Get-ComuterInfo {
    [CmdletBinding()]
    Param (
        [Parameter(ValueFromPipelineByPropertyName = $true,ValueFromPipeline = $true)]
        [string]$Computername = $env:ComputerName
    )
    
        PROCESS {
            $Computer = Get-WmiObject -Class win32_ComputerSystem -ComputerName $ComputerName
            $Bios = Get-WmiObject -Class Win32_Bios -ComputerName $ComputerName
            $Processor = Get-WmiObject -Class Win32_Processor -ComputerName $ComputerName
    
            [PsCustomObject]@{
                ComputerName   = $ComputerName
                MemorySizeInGB = $Comp.TotalPhysicalMemory / 1GB
                BIOSVersion    = $Bios.SMBIOSVersion
                Processor      = $Processor.Name
            }
        }     
    }

    Later if you need to output this to a file you can just run it either use Out-File, or even better use Export-Csv:

    Get-ComputerInfo | Export-Csv c:\ComputerInfo.csv
  • #20627
    Profile photo of Sikander Mirza
    Sikander Mirza
    Participant

    Thanks Matt for the reply, the tips you mentioned are valuable, i will work on that and the code example which you have posted gives me an insight on how the script process should be.

  • #20636
    Profile photo of Matt McNabb
    Matt McNabb
    Participant

    Please feel free to reply if you need further clarification on any points of the example. I'll check back in later to see if you're doing ok.

    Good luck!

  • #20688
    Profile photo of Sikander Mirza
    Sikander Mirza
    Participant
    {Function Get-CompuInfo {
        [CmdletBinding()]
    
        Param (
            [Parameter(Mandatory=$true,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName)]        
            [String[]]$ComputerName ='localhost'
    
        )
    
        Begin{}
        
        Process{
            
        foreach ($Computer in $ComputerName) {
            $Date = Get-Date
            $Memory = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computer
            $Bios = Get-WmiObject -Class Win32_Bios -ComputerName $computer
            $Processor = Get-WmiObject -Class Win32_processor -ComputerName $computer
            $Service = Get-Service -Name BITS
            $OS = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computer
            $IP = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -ComputerName $computer
            $Disk =  Get-WmiObject -Class Win32_LogicalDisk -ComputerName $computer
            $Process = Get-Process |sort CPU -Descending | select -First 3
            $EventLog = Get-EventLog -LogName Application -EntryType Error -Newest 2
    
            [PsCustomObject]@{
                'Date'                    = $Date.DateTime;
                'ComputerName'            = $env:COMPUTERNAME;
                'MemorySizeinGB'          = $Memory.TotalPhysicalMemory /1GB -as [Int];
                'BiosVersion'             = $Bios.SMBIOSBIOSVersion;
                'Processor'               = $Processor.Name;
                'ServiceName'             = $Service.Name;
                'ServiceStatus'           = $Service.Status;
                'OSVersion'               = $OS.Version;
                'IPAddress'               = $IP.IPAddress[7];
                'LoggedUser'              = $env:USERNAME;
                'Top3CpuUtilizingProcess' = $Process.name;
                'New2AppErrorLog'         = $EventLog.EventID; 
                
                }
    
         
                     
            
            
            }
        }
        
        End{}
         
     }}

    Hi Matt,
    I have uploaded above my revised code, please have a look and guide me what more i can do to
    make things look ease.
    2.second thing is i came across a blog post on Using Advance Functions in PS, the author used $Properties@{} instead of [PSCusumObject]@{}.
    what is the difference between these two, i have tried both methods for custom output data and both
    works fine and when i do Get-Member as below the result are same.
    . ./CompuInfo.ps1
    Get-CompuInfo -ComputerName localhost | gm
    TypeName: System.Management.Automation.PSCustomObject

    {$Properties=@{ 
                'Date'                    = $Date.DateTime;
                'ComputerName'            = $env:COMPUTERNAME;
                'MemorySizeinGB'          = $Memory.TotalPhysicalMemory /1GB -as [Int];
                'BiosVersion'             = $Bios.SMBIOSBIOSVersion;
                'Processor'               = $Processor.Name;
                'ServiceName'             = $Service.Name;
                'ServiceStatus'           = $Service.Status;
                'OSVersion'               = $OS.Version;
                'IPAddress'               = $IP.IPAddress[7];
                'LoggedUser'              = $env:USERNAME;
                'Top3CpuUtilizingProcess' = $Process.name;
                'New2AppErrorLog'         = $EventLog.EventID;
             
                }
             
             $obj=New-Object -TypeName psobject $Properties
             Write-Output $obj }

    Now we can type as Get-CompuInfo -ComputerName localhost | Out-File e:\data.txt or a ExportCSV,
    but what, say a company wants to just run this script and do-not want to dot source it or use it as a module,
    just run as "./CompuInfo.ps1" at the console, and each time they run they want a log file or say results file
    txt or csv automatically created by present date, they don't want to specify file path or name, it should be
    handled inside the script, so what changes we have to make in the script to make it work.

    Many Thanks

  • #20713
    Profile photo of Matt McNabb
    Matt McNabb
    Participant

    Looks like you're on the right track! I think you're fine with what you have here.

    As far as the method used to create the output object, both are acceptable. In Powershell 1.0 New-Object was the only way to do this, but we have lots of ways to create custom objects now. I like the [PsCustomObject] type accelerator because it's fast and concise, but New-Object is perfectly fine. BTW, you don't necessarily have to capture the custom object in a variable and then output it – you can just run New-Object and it will go to Out-Host by default:

    New-Object -TypeName psobject $Properties

    Keep on scripting!

You must be logged in to reply to this topic.