Centralize ESXi Core Dumps

In my environment the majority of the hosts boot from SD cards, so persistent log storage is a big deal. I recently ran into a PSOD issue. VMWare requested the coredumps, which of course, I did not have. Thankfully we were able to sort out my issue via the PSOD screenshot I took from the console.

Obviously wanting to avoid this scenario in the future, I set off to find the best way to keep this from happening again. Interestingly, I found an easy way to do this. There is no direct way via GUI or even advanced host settings. Eventually I was able to get it setup. Below is my method. Enjoy.

Setup vCenter

Note: We are running VCSA 6.5 in my environment so the core dump location will be different than if you are using a windows based vCenter server.

First we setup the esxi dump collector on the vCenter server. While logged into the vCenter web interface with an administrator account, click on the Administration link in the vCenter home menu. Next under the Deployment section, click the System Configuration link. From here choose Services under the system configuration header on the left side of the screen. Finally choose “VMware vSphere ESXi Dump Collector.”

From here, click the Manage tab. Then click the pencil icon to change the startup type and set it to run automatically. Next hit the green play button. I can confirm this will not in anyway affect the vCenter server itself, so no worries about affecting production. I kept the defaults for port and size. As noted, if you try to change either of these two settings, it does require a vCenter reboot.

That’s it! Now the vCenter server is ready. Next we need to change the setting on the esxi hosts. I was surprised to find that this must be done at the command line via esxcli. There is no way via advanced settings on the host. The process is straight forward but heavy with administrative overhead. You must ssh to each host and run the following commands. You can also use Host Profiles but that is beyond the scope of this particular post.

Setup ESXi Hosts

### Gets the current coredump configuration ###
esxcli system coredump network get

### Sets server address and port to send kernel dump to ###
### vmk0 is management network in my environment
esxcli system coredump network set -v vmk0 -i vCenter IP -o 6500

### Enables sending of coredumps to vCenter server ###
esxcli system coredump network set -e true

### Shows new core dump configuration ###
esxcli system coredump network get

It now lists the settings compared to the first time we ran it.

### Sends test coredump to vCenter Server ###
esxcli system coredump network check

OK, we are done with the host setup. Now to confirm the last step on the vCenter server. Ssh to the vCenter server and check the following log file:


You should see similar entries:

“Posting back a status check reply to…”    SUCCESS!!!

Quick credit to @lamw for this https://blogs.vmware.com/vsphere/2012/12/network-core-dump-collector-check-with-esxcli-5-1.html. This article lays out the steps of this setup in great detail.

Automate it

Having gotten it to work on one host, I now had to figure out how to get it working on the rest of my hosts with less typing and less time. Enter powercli. Borrowing a lot from this guy’s technique, I used the Get-EsxCli and a foreach loop to apply the above settings to each host. At the same time, I did a tail -f on the log file to witness the fruits of my labor. It was a good feeling knowing I had saved myself so much work. So without further adieu:







My last couple of posts have been about powershell. Its utility has really be obvious in my day to day job, so I have been writing about what I’m working on.

If you have ever worked in a Xendesktop environment you are familiar with machines going unregistered for whatever reason. I wrote this function to produce five possible outcomes that I needed when dealing with unregistered machines and machines in maintenance mode in a XenDesktop Environment. The parameters are setup as five switches that will kick off Citrix management actions.

The first switch is -MachineUnregistered This searches a Citrix site to find powered on and unregistered machines. It will not perform any action on the machines. It only show you the unregistered machines.

The second switch is -RestartMachineUnregisteredPrompt This searches a Citrix site to find powered on, unregistered machines and prompts you if they should be restarted.

The third switch -RestartMachineUnregistered is the same as the second switch but does not prompt asking if a reboot should be performed. It just reboots all machines that are listed as unregistered. Use this one with caution.

The fourth switch is -MachineInMaint. This searches a Citrix site to find machines in maintenance mode.

Finally, the fifth switch is -TurnOffMaintOnMachinePrompt. This searches a Citrix site to find machines in maintenance mode and prompts asking if maintenance mode should be turned off. I hard coded yes and no, with no validation. So if you type anything besides a literal ‘yes’ or ‘no’, you will not receive an error, but the action will not happen.

Update the variable $AdminAddress to match your DDC. After that you should be good to go.

Function Set-Citrix {
<# .SYNOPSIS Function that uses parameters as switches to trigger typical Citrx Xendesktop maintenance actions.
.DESCRIPTION Switch Parameters to start predefined Xendesktop actions. .PARAMETER MachineUnregistered Gets unregistered machines. 
.PARAMETER RestartMachineUnregisteredPrompt Prompts asking to restart unregisterd machines. 
.PARAMETER RestartMachineUnregistered Restarts unregisterd machines without prompt. 
.PARAMETER MachineInMaint Finds machines in maintence mode. 
.PARAMETER TurnOffMaintOnMachine Prompts asking to turn off maintenance modes on machines. .NOTES Author : Jamey .LINK https://jamey.info .EXAMPLE Set-Citrix -MachineUnregistered Searches Citrix site to find powered on and unregistered machines 
.EXAMPLE Set-Citrix -RestartMachineUnregisteredPrompt Searches Citrix site to find powered on, unregistered machines and prompts asking if they should be restarted. 
.EXAMPLE Set-Citrix -RestartMachineUnregistered Searches Citrix site to find powered on, unregistered machines and restarts them without prompt.
.EXAMPLE Set-Citrix -MachineInMaint Searches Citrix site to find machines in maintence mode
.EXAMPLE Set-Citrix -TurnOffMaintOnMachinePrompt Searches Citrix site to find machines in maintenance mode and prompts asking if maintenance mode should be turned off, #>

[Parameter(Mandatory=$False, ParameterSetName="MachineUnregistered")]

[Parameter(Mandatory=$False, ParameterSetName="RestartMachineUnregisteredPrompt")]

[Parameter(Mandatory=$False, ParameterSetName="RestartMachineUnregistered")]

[Parameter(Mandatory=$False, ParameterSetName="MachineInMaint")]

[Parameter(Mandatory=$False, ParameterSetName="TurnOffMaintOnMachinePrompt")]

##Load Citrix Modules
Add-PSSnapin Citrix.*
$AdminAddress = 'ddc'

If ($machineUnregistered) {
    Get-BrokerDesktop -adminaddress $AdminAddress -MaxRecordCount 5000 | where-object {($_.PowerState -eq 'On') -and ($_.RegistrationState -eq 'Unregistered')} | Select-Object MachineName

ElseIf ($RestartMachineUnregisteredPrompt) {
    $UnregisteredDesktops = (Get-BrokerDesktop -adminaddress $AdminAddress -MaxRecordCount 5000 | where-object {($_.PowerState -eq 'On') -and ($_.RegistrationState -eq 'Unregistered')} | Select-Object MachineName)
foreach ($unregisteredDesktop in $unregisteredDesktops){
    Write-host $UnregisteredDesktop.machinename
    $answer = Read-Host -prompt 'Restart Unregistered Machine?'
if ($answer -eq 'yes'){
    New-BrokerHostingPowerAction -MachineName $unregisteredDesktop.MachineName -Action Reset
    Write-Host "Unregistered machine name is: $unregisteredDesktop.MachineName"
elseif ($answer -eq 'no'){
    Write-Host 'Did not restart machine'

ElseIf ($RestartMachineUnregistered) {
    $unregisteredDesktops = (Get-BrokerDesktop -adminaddress $AdminAddress -MaxRecordCount 5000 | where-object {($_.PowerState -eq 'On') -and ($_.RegistrationState -eq 'Unregistered')} | select MachineName)
foreach ($unregisteredDesktop in $unregisteredDesktops){
    New-BrokerHostingPowerAction -MachineName $unregisteredDesktop.MachineName -Action Reset
    Write-Host "Unregistered machine name is: $unregisteredDesktop.MachineName"

ElseIf ($MachineInMaint) {
    Get-BrokerDesktop -AdminAddress $AdminAddress -MaxRecordCount 5000 | Where-Object {($_.InMaintenanceMode -eq $true)} | Select-Object machinename
ElseIf ($TurnOffMaintOnMachinePrompt) {
foreach ($desktop in (Get-BrokerDesktop -AdminAddress $adminAddress -Filter {(inmaintenancemode -eq $true) -and (desktopkind -eq 'shared' )})) {
    Write-host $desktop.machinename
$answer = Read-Host -prompt 'Disable Maint for this machine? Answer yes or no'
if ($answer -eq 'yes'){
    Set-BrokerSharedDesktop -machinename $desktop.machinename -InMaintenanceMode $false -AdminAddress $AdminAddress
    New-BrokerHostingPowerAction -MachineName $desktop.machinename -Action TurnOn
($answer -eq 'no'){
    Write-Host 'Did not put machine in Maintenance Mode'
Else {
    Return "Choose Something "

Import AD User to Learn PowerShell

I wrote this script for someone who did not use PowerShell on a daily basis. I knew they wanted to create active directory user accounts in bulk from a csv file, so I wrote this with a few goals in mind:

1. Fix their issue. I wanted to show how useful it is learn PowerShell by using it to fix a real problem they had. This allowed them to create the users as desired.

2. Show the steps as plainly as possible, skipping any fancy functions or modules. I tried to write it as a plain sequence of steps.

3. Make it so they can edit the script if their csv input changes, despite them having no scripting background.

I think this should be run from the powershell ISE instead of the command line. If you open it on the left in the ISE and then open ADUC on the right, you can observe the results right away. You can “see” what the script does.

#Imports users from HR csv and sets it to variable

$users = Import-Csv -Path 'c:\path\to\csv' 

#Loops through each user, creates variable for each property and creates the user

foreach ($user in $users){

#These match the column names in the csv

$SamAccountName = $user.Username
$Name = "$($user."Last Name"), $($user."First Name") $($user.Int)."
$GivenName = $user."First Name"
$Surname = $user."Last Name"
$Initials = $user.Int
$DisplayName = "$($user."Last Name"), $($user."First Name") $($user.Int)."
$UserPrincipalName = "$($user.Username)@company.org"
$Description = "$($user."Position") - $($user."Department")"
$EmployID = $user."EE #"
$OU = "ou=ou,dc=company,dc=org"

#Creates the user

$NewUser = New-ADUser -SamAccountName $SamAccountName -Name $Name -GivenName $GivenName -Surname $Surname -Initials $Initials -DisplayName $DisplayName -UserPrincipalName $UserPrincipalName -Description $Description -OtherAttributes @{'EmployeeID' = $EmployID} -AccountPassword $newpwd -CannotChangePassword $false -ChangePasswordAtLogon $true -Enabled $true -Path $OU -PassThru

#Adds new user to group one

Add-ADGroupMember -Identity 'group one' -Members $SamAccountName

#Adds new user to group two

Add-ADGroupMember -Identity 'group two' -Members $SamAccountName


Splunk in My Homelab: Part Two

Kicking off the second part of setting up Splunk in my home lab, I will be installing the Splunk app and a universal forwarder on the Windows 2012 server from which I want to index logs. If you do not have a Splunk server up and running in your lab, check out my first post “Splunk in My Homelab: Part One” at https://jamey.info/splunk-in-my-homelab/.

From your Splunk web console choose Splunk Apps.

Now search the apps using Windows as your search term. The official Splunk app is called Splunk Addon for Microsoft Windows. Installing this prepares Splunk to accept machine data from a Windows source.

After selecting install, you will be prompted to login to Splunkbase using your Splunk account and then prompted to restart Splunk. Once Splunk is back up and running, log back into the server. Next we will be configuring a listening port to receive the Windows data. From the Splunk home screen, click settings, then choose “forwarding and receiving”.

Now click the blue hyperlink “Configure receiving”.

Enter the port number on which you want Splunk to listen. I chose the default Splunk listening port 9997.


Now the Splunk server is prepared to receive data to index. Next, move to installing the forwarder on the windows server you want to collect data from.

First, go to https://www.splunk.com/en_us/download/universal-forwarder.html and download the universal forwarder that is appropriate for the OS you will exporting data from. I chose Windows 64-bit. Splunk recommends not installing the 32-bit version on 64-bit machines.

Now run the MSI locally on the Windows server. Accept the license and choose customize options.

I ran it as a local system account. If you have an internal domain, check out this link for best practices on using domain accounts. Link

Choose what data you want exported. I chose a lot. Be aware that the system log on Windows will generate a ton of data.

Finally, I pointed it at my Splunk server. If you used anything besides the default port when setting up the listener on the Splunk server, then enter it in the box. Otherwise, leave it blank.

Now navigate to your search app and host=”windows server name”. It may take a few minutes to populate. Now that we have everything up and running,  we will be focusing on Splunk search in the next post.



Splunk in My Homelab: Part One

Over time my homelab has grown to over 40 virtual machines spread over three subnets. All the boxes are active and I want more insight into what is going on, so I’m adding a Splunk server. Over the next three blog posts, I’ll walk you through setting up and searching Splunk. This first post is going to cover registering for, downloading, and installing the Splunk software. The next post will cover installing the forwarders giving Splunk something to index. Lastly, the third post will cover adding apps and querying data.

Before we begin, lets quickly look at the parts of Splunk. The layer closest to us is the search head. This is the front end that we interact with via search and the Splunk web UI. The indexer is the heart of Splunk. The indexer receives data, compresses it, and then indexes it. The last part is the forwarder. This is a Splunk instance on an end point that generates machine data. The forwarder forwards the data to the indexer. Typically you would separate the roles, but because of the small amount of data and for simplicity sake, I’m putting search head and indexer on one box.


First go to splunk.com and register an account. Its free, but be aware they may ask to validate your email, in case you like to use throw away ones. Once logged in, click the big green free Splunk button. I chose the Splunk enterprise server free download.


I’m using Ubuntu 16.04 for my Splunk server, so I will chose Linux. At this point, you can download the .deb file, but as the second screen shot shows, you can also use wget to download the bits right to your box. That is what I will be doing. You’ll also notice that I pointed out the data limitation. This is not an issue for me since I’m just curious about my traffic and don’t actually need more than that. But if you want more and are willing to follow up, Google “Splunk Developer License.”

After running wget from the screen shot above run the following:

#Install splunk enterprise server
dpkg -i splunk-6.6.2-4b804538c686-linux-2.6-amd64.deb
#If you receive any errors looking for dependencies run this
sudo apt-get install -f
#Now start the splunk server
sudo /opt/splunk/bin/splunk --accept-license
#Finally set splunk to start on boot
sudo /opt/splunk/bin/splunk enable boot-start

Now navigate to http://splunkserver:8000.  Success!!!

In the next post, I will be installing a universal forwarder which will provide some data that I will later query in part three of this series.

VMware PowerCLI to enable management services

I wrote this once when I needed to pull as much information as possible about the vmware environment I found myself in that day. It enables ssh and sets it to start at boot. It also enables SNMP, sets it to star at boot and sets the community string you define. I split it up into multiple for loops so I could easily track what I was doing with on screen commentary via write-host. I used it in a vSphere 5.5 environment. Beware results might vary by version.

I begin by letting you define your vCenter server, your datacenter object and your snmp community string
Next this script starts SSH, makes SSH start on boot, hides SSH warnings, starts SNMP, makes SNMP start on boot,
and sets the SNMP community string on each host in your defined data center.

#Your Datacenter specific variables
$vCenter = "FQDN of vcenter server"
$datacenter = "datacenter object name"
$community = "SNMP Community String"

#Load VMware Cmdlets and connect to vCenter
Add-PSSnapin vmware*
Connect-VIServer -Server $vCenter

#Set VMHosts variable
$VMHosts = Get-VMhost -Location $datacenter | Sort-Object
# Start SSH on each host in the datacenter
ForEach ($HostMachine in $VMHosts) {
    Write-Host "Starting the SSH Service on $HostMachine " 
    $HostMachine | Get-VMHostService | Where-Object {($_.Key -eq "TSM-SSH") -and ($_.Running -eq $False)} | Start-VMHostService
# Change SSH to start on boot
ForEach ($HostMachine in $VMHosts) {
    Write-Host "Changing SSH to start on boot for $HostMachine " 
    $HostMachine | Get-VMHostService | where { $_.key -eq "TSM-SSH" } | Set-VMHostService -Policy "On" -Confirm:$false -ea 1
# Supress SSH warning
ForEach ($HostMachine in $VMHosts) {
    Write-Host "Hiding SSH Warning on $HostMachine "
    $HostMachine | Get-AdvancedSetting | Where-Object {$_.Name -eq "UserVars.SuppressShellWarning"} | Set-AdvancedSetting -Value "1" -Confirm:$false

#Start SNMP service on hosts
ForEach ($HostMachine in $VMHosts) {
    Write-Host "Starting SNMP on $HostMachine " 
    $HostMachine | Get-VMHostService | Where-Object {($_.Key -eq "snmpd") -and ($_.Running -eq $false)} | Start-VMHostService | Out-Null

#Change SNMP to start on boot
ForEach ($HostMachine in $VMHosts) {
    Write-Host "Changing SNMP to start with $HostMachine " 
    Get-VMHostService $HostMachine  | Where-Object { $_.key -eq "snmpd" } | Set-VMHostService -Policy "On" -Confirm:$false -ea 1 | Out-Null

#Set snmp community string and opens firewall for snmp
ForEach ($HostMachine in $VMHosts) {
    Write-Host "Setting snmp community string and opening firewall for $HostMachine"
    $esxcli = Get-EsxCli -VMHost $HostMachine
    $esxcli.network.firewall.ruleset.set($true, $true, "snmp")

Write-Host "Good Job"

PuTTYgen and an Ubuntu Server

Recently I used PuTTYgen to create a key pair which I intended to use to connect to one of the Ubuntu servers I have in my home lab. Not paying attention to detail, I uploaded the public key, ran

cat key.pub >> .ssh/authorized_keys

and was surprised to find that I was not able to connect. It turns out the formatting of the key is different in subtle ways. I’m going to cover how I manually modified the key so it would work, and then later found an easier way via ssh-keygen.

Manual Process

First I opened the key in Atom, an alternative to sublime text which I favor (when not using Vim that is 😉 ).

Turns out *Nix systems expect no other formatting or characters apart from the key itself. So I had to remove white spaces, comments and control characters. I manually removed all the words and comments. Then I hit find and searched via regex for carriage return and line feed. \r\n in the picture. I left the replace blank as I wanted to remove everything except for the key and replace it with nothing. You can see where Atom highlighted the space we would be removing.

Then I added ssh-rsa followed by a space at the start of the file. This signals the file encoding being used. Check RFC 4253 secion 6.6 (link) for a detailed, albeit boring, read about why we use ssh-rsa. If I want a comment, I can add a space followed by a comment at the end of the line.

Finally, just to be sure there were no issues with EOL between Windows and Linux, I installed an Atom package called line ending converter. To do this in Atom, I went to settings, chose install and searched community packages for the package. Once it is installed, it can be found under packages at Convert Line Endings to.

So now I have an acceptable key. I ssh to the box via username/password, open .ssh/authorized_keys, paste the key, and I’m good to go.

Or, if you’re the more streamlined type, this whole blog post can be done in one step. But, you wouldn’t learn what’s going on under the hood, and that’s the fun part.

ssh-keygen -i -f putty_key > new_key

Proxy Variables

While setting up squid proxy on my pfSense home lab gateway, I had trouble getting apt-get update to work on my Ubuntu/snort box which was behind the proxy. After some quick Googling, I tried the first response (because the first response is always the best right 😉 ), and the first response failed. After reading a few more blogs, I noticed there were many different ways to setup a proxy properly. Here’s what I found.

Setting up a proxy on the command line starts by declaring the proxy environment variable. Applicable variable options are:


Next, check if you currently have a proxy already set.

$ env | grep -i proxy 

If you get nothing from the command above, you know you don’t have a proxy yet. If you only want http and ftp to go through your proxy, export those variables instead of some of the other options above.

$ export {http,ftp}_proxy="http://proxy_name_or_ip:port_number"

This will only export the variable for the current session. If you log out or reset the computer, you will lose the proxy setting. If you need to make it permanent, use /etc/environment which is Ubuntu’s system-wide location for environment variables. You could also put it in /etc/profile.d as a file since this directory is ultimately read by /etc/profile. It’s not best practice to set it in /etc/bash.bashrc because variables in this file are specific to shells. Finally, if you want only a specific user to receive the proxy, you should set it at ~/.bashrc

If you want to read more about proper placement of environment variables, read this Ubuntu Environment Variables.

Now add your settings to /etc/environment

 echo "http_proxy=http://proxy_name_or_ip:port_number" >> /etc/environment;\
 echo "ftp_proxy=http://proxy_name_or_ip:port_number" >> /etc/environment 

If your proxy requires a username and password, the following format is often used:

echo "http_proxy=http://username:password@proxy_name_or_ip:port_number" >> /etc/environment

Most of the time you would be done at this point. But I had an issue with APT where I had to set the proxy in the APT configuration file.

I had to edit /etc/apt/apt.conf and add

Acquire::http::proxy "http://proxy_name_or_ip:port_number";

If you are curious about how to configure YUM similar to APT, you need to edit /etc/yum.conf

Once the file is open, add these lines to the section [main]


While we’re talking about proxies, CNTLM is another proxy that you install locally and point your proxy variable to localhost. It is a middle-man proxy that sits between you and a proxy that requires NTLM authentication. I have found this incredibly helpful when using Linux in a Windows environment. It’s a really cool piece of software and really easy to setup.

It’s in the Ubuntu repositories.

apt-get install cntlm

Its configuration file is found at /etc/cntlm.conf

Add the following:

Username        jamey
Domain          jamey.info
Proxy           proxy_ip:proxy_port

Next we create password hashes.

cntlm -H

The output should look like this

PassLM          ACF337F47B2E22ED552C4BCA4AEBFB11
PassNT          2A22CC95E275BE3150326D0C1E86A58E
PassNTLMv2      F001B46C503A3A01611D2859EBEA8762    # Only for user 'jamey', domain 'jamey.info'   

Copy/paste your output to /etc/cntlm.conf

Finally configure the local proxy variable as we did above to point to localhost instead of an external proxy.

export http_proxy=

Powershell Get-Person

Where I work I do a lot of querying AD for users. I work for a large organization with many departments. When I first started, someone would say something like, “Hey Jamey, pull up Mr. Jolgue or Mr. Joluge or was it Mr. Joleguge,” and I would have no idea where to start my search. So I wanted to write something that would let me type in almost any criteria and get something besides an error back if there were no matches.

You can invoke this with no parameters if you want any result that is close to your search, or you can invoke this with the -FirstName, -LastName, or -UserName parameters if you want specific matches.

Remember to download RSAT tools to query AD remotely. They can be found here RSAT Tools.

Copy and paste this to a file and save with a .psm1 extension. Look up your module path, which can be found by typing $Env:PSModulePath into your shell. Create a folder of the same name in your module folder, then copy the file to that folder. After that you can call it as any other cmdlet.

Function Get-Person { 
.SYNOPSIS This script is used to search AD for users with more liberal filtering that I could get with Get-Aduser -Filter
.DESCRIPTION I use LDAP queries and wildcards to allow liberal search criteria and attempt to return anything close to what the user was thinking. If they want to be more specific, -FirstName -LastName or -UserName parameters can be used which require exact matches for input.
.PARAMETER LastName Accepts last name as search criteria. 
.PARAMETER FirstName Accepts first name as search criteria. 
.PARAMETER UserName Accepts SamAccountName as search criteria. 
.NOTES Author : Jamey Email : jamey@jamey.info 
.INPUTS Strings 
.OUTPUTS New custom user object with predefined attributes 
.LINK Script posted over: https://jamey.info 
.EXAMPLE Get-Person Jon Searches Active Directory for users that Last Name, First Name or Sam Account Name contain the string Jon 
.EXAMPLE Get-Person -LastName Jon Searches Active Directory for users with the last name Jon 
.EXAMPLE Get-Person -FirstName Jon Searches Active Directory for users with the first name Jon 
.EXAMPLE Get-Person -UserName Searches Active Directory for users with the SamAccountName Jon #>

#Requires –Modules ActiveDirectory
#Requires –Version 3

[CmdletBinding(DefaultParameterSetName = "All")] 

    [Parameter(Mandatory=$False,  Position=0,  ParameterSetName="All")]
    [Parameter(Mandatory=$False,  ParameterSetName="LastName")]

    [Parameter(Mandatory=$False,  ParameterSetName="FirstName")]  
    [Parameter(Mandatory=$False,  ParameterSetName="UserName")]  

If ($All) {
    $UserInfo = Get-ADUser -LDAPFilter "(|(Sn=*$All*)(givenName=*$All*)(sAMAccountName=*$All*))" -Properties *
ElseIf ($LastName) {
    $UserInfo = Get-ADUser -LDAPFilter "(Sn=$LastName)" -Properties *
ElseIf ($FirstName) {
    $UserInfo = Get-ADUser -LDAPFilter "(GivenName=$FirstName)" -Properties *
ElseIf ($UserName) {
    $UserInfo = Get-ADUser -LDAPFilter "(samaccountname=$UserName)" -Properties *
Else {
    Return "Please enter some value to search on"

foreach ($User in $UserInfo) {
    $EachUser = Get-ADUser $User.samaccountname -Properties *
    $Properties = [PSCustomObject]@{
        "First Name" = $EachUser.Givenname
        "Last Name" = $EachUser.Surname
        "Display Name" = $EachUser.Displayname
        "User Name" = $EachUser.samaccountname
        "Employee ID" = $EachUser.employeeid
        "Account Created Date" = $EachUser.Created
        "Department" = $EachUser.Department
        "Description" = $EachUser.Description
        "OU Info" = $EachUser.Distinguishedname
        "Email Address" = $EachUser.emailaddress