Inventorying Servers with PowerShell

Exclusive offer: get 50% off this eBook here
Windows Server 2012 Automation with PowerShell Cookbook

Windows Server 2012 Automation with PowerShell Cookbook — Save 50%

Over 110 recipes to automate Windows Server administrative tasks by using PowerShell with this book and ebook.

$32.99    $16.50
by Ed Goad | April 2013 | Cookbooks Enterprise Articles Microsoft

This article explains how to inventory the hardware and software configurations of Windows 8 Servers and create a detailed inventory and configuration report. Additionally, this article will cover methods to track configuration changes over time, export the configuration report via Word. This article should cover everything necessary to create a centralized hardware and software inventory of all servers in the enterprise.

This article by Ed Goad, author of Windows Server 2012 Automation with PowerShell Cookbook, covers the following topics:

  • Inventorying hardware with PowerShell

  • Inventorying the installed software

  • Inventorying system configuration

  • Reporting on system security

  • Creating a change report

  • Exporting a configuration report to Word

(For more resources related to this topic, see here.)

Inventorying hardware with PowerShell

Often times, a system administrator needs to identify what hardware is installed in their environment. This could be for an asset tracking project, finding available resources, or even identifying older equipment that needs to be retired. Most hardware information on a system is stored in WMI and can be accessed via in-built PowerShell functions or via WMI queries. In this recipe, we will review the various methods for gathering inventory information from our local system. In addition to collecting from the local system, these methods can be expanded to include remote systems.

How to do it...

We can review the various methods for gathering inventory information as follows:

  1. Gather disk information from the local system.

    $TargetSystem="." $myCim = New-CimSession -ComputerName $TargetSystem Get-Disk -CimSession $myCim

    When executed, the results will be displayed similar to the following screenshot:

  2. Review the contents of the Get-Disk function.

    Get-Content Function:Get-Disk

    When executed, the contents of the function will be displayed as shown in the following screenshot:

  3. Retrieve results using WMI.

    Get-WmiObject -Class MSFT_DISK ` -Namespace ROOT\Microsoft\Windows\Storage

    When executed, the results will be displayed similar to the following screenshot:

How it works...

We start by gathering the disk information from the local system. First, we call New-CimSession to create a CIM connection to the target system, in this case we are connecting to the local system, so we can use "." instead of a system name. Next, we call the in-built PowerShell function Get-Disk to return the disk information. By default, PowerShell only returns a subset of information; more can be returned by piping the output through a Select-Object statement and choosing which columns to return.

In the second step, we review the contents of the Get-Disk function to learn how it works. Because this command is a function, we can use Get-Content to view the contents of the function. This will return the full content of the script, but in this case we are only interested in the OutputType. In this line, we see that the function retrieves its content from WMI at the ROOT\Microsoft\Windows\Storage\MSFT_Disk namespace and class.

Finally, we query WMI directly using the namespace and class previously identified. To perform this we use Get-WmiObject to search WMI directly. We use the –Namespace switch to connect to the ROOT\Microsoft\Windows\Storage namespace. We use the –Class switch to return all objects in the MSFT_Disk class. This information is the same as previously returned, confirming the location is the same.

There's more...

In addition to the disk values shown here, there are several additional PowerShell commands and WMI locations to report hardware inventory. Only a few of these inventory types have been converted to in-built PowerShell functions, the remainder needs to be queried directly against WMI. Examples of additional counters are as follows:

  • Logical disk

    Get-Disk -CimSession $myCim

  • Physical disk

    Get-PhysicalDisk -CimSession $myCim

  • Network adapters

    Get-NetAdapter -CimSession $myCim

  • System enclosure

    Get-WmiObject -ComputerName $TargetSystem ` -Class Win32_SystemEnclosure

  • Computer system

    Get-WmiObject -ComputerName $TargetSystem ` -Class Win32_ComputerSystemProduct

  • Processor

    Get-WmiObject -ComputerName $TargetSystem -Class Win32_Processor

  • Physical Memory

    Get-WmiObject -ComputerName $TargetSystem -Class Win32_ PhysicalMemory

  • CD-Rom

    Get-WmiObject -ComputerName $TargetSystem -Class Win32_CDromDrive

  • Sound card

    Get-WmiObject -ComputerName $TargetSystem -Class Win32_SoundDevice

  • Video card

    Get-WmiObject -ComputerName $TargetSystem ` -Class Win32_VideoController

  • BIOS

    Get-WmiObject -ComputerName $TargetSystem -Class Win32_BIOS

Inventorying the installed software

In addition to inventorying a system's hardware, it is often necessary to inventory the installed software. There are two primary methods to query the installed software: using the Microsoft Installer, and the Uninstall registry key. These two locations generally return the same information, however as we will see there are different uses for each.

How to do it...

Complete the following to review the installed software:

  1. Get installed features.

    Get-WindowsFeature | Where-Object Install`State -EQ "Installed"

  2. Return software inventory via MSI.

    Get-WmiObject -Class Win32_Product

  3. Open event viewer and review the system event logs. Note the multiple Event ID 1035 messages as shown in the following screenshot:

  4. Return inventory via a registry.

    $HKLM = 2147483650 (([wmiclass]"root\default:stdregprov").EnumKey($HKLM, ` "Software\Microsoft\Windows\CurrentVersion\Uninstall")).sNames

  5. Return installed patches.

    Get-WmiObject -Class Win32_QuickFixEngineering

How it works...

We start by using Get-WindowsFeature to list the features installed in our Windows 2012 Server. This command returns all of the installed features and roles on the current server. If you are unfamiliar with a system, this is a great method to know what Windows services include.

In the second step, we use WMI to query the Win32_Product class. This class interacts with the MSI packages on your system and returns a list of all packages currently installed. However, this command should be used with caution as it also causes the MSI packages to be reconfigured, or reset to their default configurations. If we open Event Viewer and review the Application, log on your system after executing this task, we will notice a large number of Event ID 1035.

In the fourth step, we use WMI to query the registry and return a list of applications. We start by defining the variable $HKLM and assigning the value 2147483650 which is used by WMI to reference the HKey_Local_Machine registry hive. We then query the results from the Software\Microsoft\Windows\CurrentVersion\Uninstall key. This returns information used by the Programs control panel icon. Often times this list will be different from the previous list because not all applications are installed as MSI packages, and because not all MSI packages appear in the Programs list.

Lastly, we return the installed hotfixes. Most Microsoft hotfixes add an entry to the Win32_QuickFixEngineering WMI namespace when they are installed. This provides a quick and simple method to identify which updates are installed on a system.

Windows Server 2012 Automation with PowerShell Cookbook Over 110 recipes to automate Windows Server administrative tasks by using PowerShell with this book and ebook.
Published: March 2013
eBook Price: $32.99
Book Price: $54.99
See more
Select your format and quantity:

Inventory system configuration

When cataloging your environment, it is also important to inventory the system configuration. To fully document your environment, information such as the network configuration, local users, and service state is necessary. This information is useful when recreating your environment in a DR scenario, or simply for a Dev/Test environment.

In this recipe, we will be returning the configuration for a basic Windows Server.

Getting ready

For this recipe we will be using a Windows Server 2012 system

How to do it...

Perform the following to gather the system information:

  1. Retrieve the network configuration

    $TargetSystem="." $myCim = New-CimSession -ComputerName $TargetSystem Get-NetIPAddress -CimSession $myCim Get-NetRoute -CimSession $myCim

    When executed, the network information will be displayed similar to the following

  2. List the event logs.

    Get-EventLog -List -ComputerName $TargetSystem

  3. Display the local users and groups.

    Get-WmiObject -ComputerName $TargetSystem ` -Query "Select * from Win32_Group where Domain='$TargetSystem'" Get-WmiObject -ComputerName $TargetSystemt ` -Query "Select * from Win32_UserAccount where Domain='$TargetSystem'"

  4. List the services.

    Get-Service Get-WmiObject -Class Win32_Service | ` Select-Object Name, Caption, StartMode, State

  5. List the currently running processes.

    Get-Process

  6. Show the system's shares and printers.

    Get-SmbShare Get-Printer

  7. Display the startup information.

    Get-WmiObject -ComputerName $TargetSystem -Class Win32_ StartupCommand

  8. Show the system time zone.

    Get-WmiObject -ComputerName $TargetSystem -Class Win32_TimeZone

  9. Display information about the registry.

    Get-WmiObject -ComputerName $TargetSystem -Class Win32_Registry

How it works...

We start by retrieving the network configuration of the system. The first Get-NetIPAddress command returns the current IPv4 and IPv6 addresses assigned to the network adapters. The second Get-NetRoute command returns the current routing table, which helps define the network configuration around the system.

In the second step, we use Get-EventLog to retrieve information about the event logs. The –List switch returns information about the event logs, which logs exist on the system, the retention settings, and current sizes.

Next, we retrieve the local users and groups. To return this information we query WMI for the Win32_UserAccount and Win32Group classes. We add a filter to our query to only return users and groups that belong to the local system. Without the filter, the query will return users and groups in the entire domain instead of the local system.

In the fourth step, we retrieve the status of the installed services. To return service information we can use the in-built Cmdlet Get-Service or query the Win32_Service WMI class. The WMI class provides more information for reporting, including the startup setting for the service; however, this method comes at the price of performance.

Next, in the fifth step, we return the currently running processes. On a workstation or a Dev/Test system, the process information will be very dynamic and possibly not very useful. However, on a stable system where the running processes don't change often, such as a mail server, this can help confirm changes to the running configuration of the system.

In the sixth step, we use Get-SmbShare and Get-Printer to return the shares and printers on the system. This information provides a quick snapshot of the file/print configuration that can be used for change tracking as well as security auditing.

In the seventh step, we use Get-WmiObject to return the contents of the Win32_StartupCommand WMI class. The startup commands are additional executables, in addition to services, that are executed at systems startup and when users log in.

Lastly, the Win32_TimeZone class returns the currently configured time zone for the system. The time zone can be useful when using scheduled tasks to execute commands at a certain time of day. And the Win32_Registry class returns the current and maximum sizes of the system.

There's more...

In addition to the basic configuration information shown here, additional applications can be included as well. Built-in features such as IIS and Active Directory can also be inventoried. Additional Microsoft and third-party applications such as Exchange or reporting applications can also be included.

Reporting on system security

In newer versions of Windows, Microsoft has added the Action Center. This area reports on key security and maintenance aspects of your system and quickly alerts Administrators to errors.

In Server 2012, there are five categories tracked in the Action Center:

  • Network Access Protection: This controls network access in secure environments

    • Service name napagent

  • Smart Screen: This controls how unknown applications are handled on the system

    • It is stored in HKLM:\SOFTWARE\Microsoft\Windows\ CurrentVersion\Explorer

  • User Account Control: This controls how elevated permissions are handled on the system

    • It is stored in HKLM:\Software\Microsoft\Windows\ CurrentVersion\Policies\System

  • Windows Activation: It activates Windows with Microsoft

    • It is Stored in the WMI SoftwareLicensingProduct key

  • Windows Update: It provides patching and updates

    • It is stored in HKLM:\SOFTWARE\Microsoft\Windows\ CurrentVersion\WindowsUpdate\Auto Update

In this recipe, we will be reporting on the security status of the system as viewed by the Action Center.

Getting ready

For this recipe we will be using a Windows Server 2012 system.

How to do it...

  1. Retrieve the user's account control settings.

    $secSettings = Get-ItemProperty -Path ` HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System

  2. Report on the UAC values.

    IF($secSettings.PromptOnSecureDesktop -eq 1) { IF($secSettings.ConsentPromptBehaviorAdmin -eq 2) { Write-Host "4: Always notify me" } Else { Write-Host "3: Notify me only when apps try to make changes to my computer (default)" } } Else { IF($secSettings.ConsentPromptBehaviorAdmin -eq 5) { Write-Host "2: Notify me only when apps try to make changes to my computer (do not dim my desktop)" } Else { Write-Host "1: Never notify me" } }

    When executed, the results will be displayed similar to the following screenshot:

  3. Retrieve the smart screen settings.

    $ssSettings = Get-ItemProperty -Path ` HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer

  4. Return the smart screen value.

    $ssSettings.SmartScreenEnabled

    When executed, the results will be returned as shown in the following screenshot:

  5. Retrieve the Network Access Protection settings

    Get-Service napagent

  6. Retrieve the Windows Update settings.

    $auValue = 0 $auLocSettings = Get-ItemProperty ` -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\ WindowsUpdate\Auto Update" $auGpoSettings = Get-ItemProperty ` -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" IF($auGpoSettings) { $auValue = $auGpoSettings.AUOptions } Else { $auValue = $auLocSettings.AUOptions }

  7. Return the Windows update values.

    IF($auValue -eq 4) { Write-Host "4: Install updates automatically (recommended)" } IF($auValue -eq 3) { Write-Host "3: Download updates but let me choose whether to install them" } IF($auValue -eq 2) { Write-Host "2: Check for updates but let me choose whether to download and install them" } IF($auValue -eq 1) { Write-Host "1: Never check for updates (not recommended)" }

    When completed, the results will be displayed similar to the following screenshot:

  8. Retrieve the Windows activation status

    $licResult = Get-WmiObject ` -Query "Select * FROM SoftwareLicensingProduct WHERE LicenseStatus = 1" $licResult.Name

    When executed, the licensed products will be returned as shown in the following screenshot:

How it works...

We start by retrieving the values for User Account Control. These values are stored in two registry keys named PromptOnSecureDesktop and ConsentPromptBehaviorAdmin under the HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\ System key. These two keys work together to define the UAC slider visible in the control panel. The results are returned with the appropriate value and description.

We can review these settings by opening the Action Center on a Server 2012 system. In the Action Center, under Security and User Account Control, click on Change settings to view the current settings as shown in the following screenshot:

In the third and fourth steps, we retrieve the SmartScreen settings. This setting is stored in the registry value SmartScreenEnabled in the HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer key. This value will be one of three values: Off, Prompt, or RequireAdmin.

We can also view these settings by opening the Action Center. In the Action Center, under Security and Windows SmartScreen, click on Change settings to see the screen shown as follows:

In the fifth step, we retrieve our Network Access Protection settings. The Action Center only reports on the state of the NAP client agent, so our script uses Get-Service to query the state of the napagent service.

Next, we retrieve our Windows update settings. These settings can be stored in two different registry locations depending on if the settings are configured locally (HKLM:\SOFTWARE\ Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update) or via a Group Policy (HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU). It is possible for both these locations to contain values, in which case the Group Policy setting takes precedence. The registry value AUOptions returns a number 1 to 4 detailing the current setting. The value is then returned to the PowerShell console.

Lastly, we retrieve the status of Windows activation. The Windows activation status is stored in WMI under the SoftwareLicensingProduct key. Here we query the key for any product with a LicenseStatus of 1 and it returns the name.

Windows Server 2012 Automation with PowerShell Cookbook Over 110 recipes to automate Windows Server administrative tasks by using PowerShell with this book and ebook.
Published: March 2013
eBook Price: $32.99
Book Price: $54.99
See more
Select your format and quantity:

Creating a change report

With computers, the only thing that is consistent is change. A change can be as simple as installing a hotfix or as major as upgrading the operating system. Regardless of the cause or severity, tracking the changes made to your environment is often the key to resolving problems and retaining a secure and stable environment.

In this example, we will be reporting on changes to the network configuration of our system. This sample process can be expanded to include the installed hardware and software, system configuration, and security settings as well.

Getting ready

For this recipe, we will be using a Windows Server 2012 system.

How to do it...

Perform the following to create a change report:

  1. Create a custom PSObject to store our configuration.

    $myComp=@{}

  2. Collect the network information, and add it to the PSObject .

    $ipAddresses = Get-NetIPAddress | ` Select-Object InterfaceIndex, InterfaceAlias, IPAddress, PrefixLength $myComp.IPaddr = $ipAddresses

  3. Save the PSObject to a file.

    Export-Clixml -InputObject $myComp -Path "c:\temp\myComp.clixml"

  4. Change the network settings to simulate a configuration change.

  5. Create a new PSObject to store our new configuration..

    $myComp=@{}

  6. Collect the updated network information, and add to our PSObject.

    $myComp.IPaddr = Get-NetIPAddress | ` Select-Object InterfaceIndex, InterfaceAlias, IPAddress, PrefixLength

  7. Load the original PSObject.

    $oldComp = Import-Clixml "c:\temp\myComp.clixml"

  8. Export and import the new PSObject to ensure the objects are of the same type.

    Export-Clixml -InputObject $myComp -Path "c:\temp\myComp.clixml" $myComp = Import-Clixml "c:\temp\myComp.clixml"

  9. Compare objects.

    $keys = $myComp.GetEnumerator() $keys | ForEach-Object { Write-Host $_.Name Compare-Object $oldComp.($_.Name) $myComp.($_.Name)}

How it works...

We start by creating an empty custom PowerShell object. There are multiple different methods to create custom PowerShell objects, however most methods require the object to be defined at the beginning. By simply creating an empty object we are provided with the flexibility to add attributes and variables as needed in the future.

In the second step, we query the local system for information. We use the Get-NetIPAddress command to query the local network adapters. We use Select-Object to filter the results for the information we are interested in and place the results in the variable $ipAddresses. Next, we extend our custom PowerShell object with a new attribute composed of our IP information.

In the third step, we save our custom PowerShell object to a file. The command Export- CliXml exports PowerShell objects and saves them to XML files. These files can then be retrieved at a later time and accessed as PowerShell objects. At this point, we can close our PowerShell session and reboot the computer. Because the custom object is saved to a file, the values will be consistent after a few minutes or a few years (assuming the file isn't deleted).

In the fourth step, we change the local network information. This step is optional and can be performed manually or via PowerShell. The purpose for this step is to create configuration changes to be reported later.

In the fifth and sixth steps, we repeat our first two steps to gather and catalog the new network information.

In the seventh step, we use Import-CliXml to read into PowerShell our previous custom object, this time into an object named $oldComp. At this point, we have two custom PowerShell objects in memory: $oldComp and $myComp.

Lastly, we compare our custom objects. The PowerShell command Compare-Object performs the comparison for us and returns a list of the information that has changed. Additionally, it returns both the old and new values for visual comparison.

There's more...

By using a custom PowerShell object, we can easily expand it to include additional information. In this case we can add additional attributes to the $myComp object and then automatically compare the current and old configurations. The change report can then be viewed for an individual system, stored in a central database, or scheduled, and e-mailed on a weekly basis.

Exporting a configuration report to Word

Once we have identified all of the information about a system, it can be helpful to create a system information report. This report can take many forms, however often the most generally accepted form is using Microsoft Word. Once created, the configuration can be e-mailed, stored electronically, or printed and stored physically with the system.

In this recipe, we will create the framework for a Microsoft Word inventory report. In this situation, we will only be reporting on the network configuration, however it can be easily expanded to include the hardware, software, configuration, and security information.

Getting ready

For this recipe, we will be using a Windows Server 2012 system with Microsoft Word installed.

How to do it...

Perform the following to create a configuration report in Word:

  1. Create our Word object.

    Function New-WordDoc { $oWord = New-Object -Com Word.Application Set-Variable -Name oDoc -Value $oWord.Documents.Add() -Scope Global $oWord.Visible = $true Return $oWord } $oWord = New-WordDoc

  2. Insert a title.

    Function Insert-WordTitle ($text) { $objSelection = $oWord.Selection $objSelection.Style = "Title" $objSelection.TypeText($text) $oWord.Selection.EndKey(6) > $null $objSelection.TypeParagraph() } Insert-WordTitle $env:COMPUTERNAME

  3. Insert a table of contents.

    Function Insert-TOC { $objSelection = $oWord.Selection $range = $objSelection.Range $toc = $oDoc.TablesOfContents.Add($range) $oWord.Selection.EndKey(6) > $Null $objSelection.TypeParagraph() } Insert-TOC

  4. Insert headers.

    Function Insert-WordH1 ($text) { $objSelection = $oWord.Selection $objSelection.Style = "Heading 1" $objSelection.TypeText($text) $oWord.Selection.EndKey(6) > $null $objSelection.TypeParagraph() } Function Insert-WordH2 ($text) { $objSelection = $oWord.Selection $objSelection.Style = "Heading 2" $objSelection.TypeText($text) $oWord.Selection.EndKey(6) > $null $objSelection.TypeParagraph() } Insert-WordH1 "Network Information" Insert-WordH2 "IP Addresses"

  5. Collect the inventory data.

    $myComp=@{} $IPAddresses = Get-NetIPAddress | ` Select-Object InterfaceIndex, InterfaceAlias, IPAddress, PrefixLength $myComp.IPaddr = $IPAddresses

  6. Insert a table to hold the information.

    Function Insert-WordTable ([System.Array]$text){ $headers = $text | Get-Member -MemberType NoteProperty | ` Select-Object Name $numCols = $headers.count $numRows = $text.count IF($numCols -gt 0){ $oTable = $oDoc.Tables.Add($oWord.Application.Selection. Range, $numRows + 1, $numCols) $oTable.Style = "Light Shading - Accent 1" $oTable.Columns.AutoFit() $i=1 $headers | ` ForEach-Object{ $oTable.cell(1,$i).Range.Text = $_.Name; $i++; } For($row=2; $row -lt $numRows+2; $row++){ For($col=1; $col -lt $numCols+1; $col++){ $oTable.Cell($row,$col).Range.Text=$text[$row-2]. ($headers[$col-1].Name) } } $oWord.Selection.EndKey(6) > $Null } } Insert-WordTable $myComp.IPaddr

  7. Update the table of contents.

    $oDoc.Fields | ForEach-Object{ $_.Update() }

How it works...

We start by creating our Microsoft Word object. To start, we create a reference to the COM object Word.Application. Next, we use Set-Variable to add a new document to our Word object. Here we specify the object to be global so that it can be referenced by other functions within our script. Next, set $oWord.Visible to $true so that our Word object is visible and then return a reference to the object.

We didn't need to make the Word object visible at this step. Instead we could have kept it hidden and automatically save the file when finished.

In the second step, we set up our document. We identify the computer name by calling $env:COMPUTERNAME and pass it to our function which adds the computer name as the title of the document. This function begins at the cursor's current location and configures it to use the Title font style. The document title is added and finally a carriage return is sent to close out the paragraph.

Next, we create a function Insert-TOC to insert a table of contents. At this point the TOC will be empty, but it is put in place so that it can be updated later. The function starts at the cursor's current location and creates a Range to place Word fields and reference objects. The empty TOC is added and the range is closed by sending a carriage return.

In the fourth step, we begin with our Headers. Here, we are using the in-built styles Heading 1 and Heading 2 to signify our sections. These functions operate the same as our Title function, except the style name is changed. The headers styles provide the necessary fonts to segment the different sections, as well as markers for the table of contents to include later. We add two headers: Network Information as Heading 1 to show general network information, and IP Addresses as Heading 2 to show specific IP information.

In the fifth step, we collect our networking information. We start by creating an empty custom PowerShell object named $myComp. We then use the Get-NetIPAddress function to query the system for the local IP addresses. We filter the results for the attributes we are interested in and place the results in a variable. Lastly, we add our results into a new attribute of our PowerShell object.

In the sixth step, we write our values to Word using the Insert-WordTable function. The function starts by identifying the column header names. The number of rows and columns are identified and a new table is added to the document. First the column headers are written, and then each row/column combination is iterated through, with values from the array placed in the boxes. Lastly, the table is exited.

Lastly, we cycle through all of the fields in the document and execute the Update command. This command updates the table of contents to include all of the Heading 1 and Heading 2 objects in the document

At this point, the document can be reviewed, printed, or saved for later review. An example report is shown in the following screenshot:

See also

For more information on using PowerShell to create Word documents, see the following links:

Summary

This recipe explains how to inventory the hardware and software configurations of Windows 8 servers and create a detailed inventory and configuration report. Additionally, this article will cover methods to track configuration changes over time and export the configuration report via Word. This article should cover everything necessary to create a centralized hardware and software inventory of all servers in the enterprise.

Resources for Article :


Further resources on this subject:


About the Author :


Ed Goad

Ed Goad is a systems architect who has been working in various roles in the IT field for 16 years. He first became interested in scripting and automation when presented with a task to uninstall software from over 1,000 systems with limited time and resources. He has worked with scripting and automation on multiple platforms and languages including PowerShell, VBscript, C#, and BASH scripting.

Ed currently holds multiple Microsoft certifications, most recently including the Microsoft Certified IT Professional – Enterprise Administrator. Additional non-Microsoft certifications include VMware Certified Professional (VCP), Red Hat Certified System Administrator (RHCSA), EMC Proven Professional, Brocade Certified Network Engineer (BCNE), and Cisco Certified Network Associate (CCNA).

Ed is currently on a sabbatical and volunteering full time at the Amor Fe y Esperanza school in Honduras(http://www.afehonduras.org). There he is teaching computer and math classes to the kids who live and work in the garbage dump outside of the capital city of Tegucigalpa.

Books From Packt


Microsoft Exchange 2010 PowerShell Cookbook
Microsoft Exchange 2010 PowerShell Cookbook

 Microsoft Windows PowerShell 3.0 First Look
Microsoft Windows PowerShell 3.0 First Look

Microsoft SharePoint 2010 and Windows PowerShell 2.0: Expert Cookbook
Microsoft SharePoint 2010 and Windows PowerShell 2.0: Expert Cookbook

 SQL Server 2012 with PowerShell V3 Cookbook
SQL Server 2012 with PowerShell V3 Cookbook

Instant Windows Powershell 3.0 Windows Management Instrumentation Starter [Instant]
Instant Windows Powershell 3.0 Windows Management Instrumentation Starter [Instant]

Instant Oracle Database and PowerShell How-to [Instant]
Instant Oracle Database and PowerShell How-to [Instant]

 PowerShell 3.0 Advanced Administration Handbook
PowerShell 3.0 Advanced Administration Handbook

 Windows Server 2012 Hyper-V Cookbook
Windows Server 2012 Hyper-V Cookbook


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software