Clint Colding    Archive    Feed    About

Generate a Windows Memory Dump from VMware

This is a simple process to generate a full memory dump for a Windows server running on VMware. There are no actions required within the VM itself, the only requirement is that you’re able to suspend the VM.

  1. Download the Vmss2core tool from VMware Flings.
  2. Suspend the VM you want to collect the memory dump for, this will generate a .vmss file.
  3. Navigate to the datastore your VM resides on and download the .vmss and .vmem files.
  4. Move the VM and the Vmss2core files into the same folder.
  5. Run the following command to generate a .dmp file:
.\vmss2core-sb-8456865.exe -W8 TESTVM-4409dd21.vmss TESTVM-4409dd21.vmem

You will now have a memory.dmp file that you can further analyze.

Finding Memory Leaks with PoolMon

While troubleshooting high memory use, I came across the situation where Windows Resource Monitor wasn’t reporting hardly any memory use at all, yet there was only about 10% free. I used the process outlined below to find kernel memory leak.

Download the Windows Driver Kit from Microsoft.

You only need the WDK, disregard the Visual Studio downloads.

Install the WDK on your workstation.

You can install the WDK anywhere, once installed we’ll grab the actual PoolMon file.

Navigate to C:\Program Files (x86)\Windows Kits\10\Tools\x64 and copy poolmon.exe to the target machine.

Now run poolmon /b to start PoolMon and sort by number of bytes.

PoolMon

Usually the best way to determine if a driver is leaking memory is if its allocating memory faster than its freeing.

Once you’ve found a suspect process, note the Tag assigned to it, in my case its MFeS.

Next run the following to determine which driver the tag is associated with:

Set-Location "C:\Windows\System32\drivers"
Select-String -Path *.sys -Pattern "MFeS" -CaseSensitive | Select-Object FileName -Unique

The MFeS tag was associated with mfeavfk.sys, which turned out to be a McAfee driver from the Endpoint Security Platform component.

Working with Environment Variables

Getting Variables

To view all of your environment variables run dir env:. This should return the Names and Values of all your environmnent variables.

To display all the values of a specific variable you can either use PowerShell’s $env variable:

$env:Path.split(";")

Or the .NET method GetEnvironmentVariable:

[Environment]::GetEnvironmentVariable("Path").split(";")

Both will return and easy to read list of values for the Path variable.

Creating Variables

To create a new variable we can use the .NET method SetEnvironmentVariable:

[Environment]::SetEnvironmentVariable("TestVariable", "Test Value", "User")

Where TestVariable is our name, Test Value our value, and User creates a user-level variable. (Machine, User or Process)

Removing Variables

If we wanted to delete this variable we simply run the same command, this time setting the value to $null.

[Environment]::SetEnvironmentVariable("TestVariable", $null, "User")

You can confirm it was removed by using the Get method:

[Environment]::GetEnvironmentVariable("TestVariable", "User")

Adding Values

What if you wanted to add a new value to an existing variable while still preserving the current values? For example, we have an exisitng variable (MyVariable), with three values (Value1, Value2, Value3). And now we want to add Value4.

C:\> [Environment]::GetEnvironmentVariable("MyVariable", "User")
Value1; Value2; Value3

We could simply run the following command to overwrite the values:

[Environment]::SetEnvironmentVariable("MyVariable", "Value1;Value2;Value3;Value4", "User")

But if we had multiple lines of values this could get out of hand pretty quickly. Instead we can add our new value to $env:MyVariable:

[Environment]::SetEnvironmentVariable("MyVariable", $env:MyVariable + ";Value4", "User")

Removing Values

To remove a single value we have to rebuild our value list, excluding the value we want to remove. To do this we start by capturing our current values:

C:\> $values = [Environment]::GetEnvironmentVariable("MyVariable", "User")
C:\> $values
Value1;Value2;Value3;Value4

Next we split our values string, remove the value we want, and then rebuild our new string:

C:\> $values = ($values.Split(';') | Where-Object { $_ -ne 'Value4' }) -join ';'
C:\> $values
Value1;Value2;Value3

Using our new values, we can set the variable:

[Environment]::SetEnvironmentVariable("MyVariable", $values, "User")

And we can confirm by using the Get method:

C:\> [Environment]::GetEnvironmentVariable("MyVariable", "User")
Value1;Value2;Value3

Deploying an OVF with PowerCLI

To install an OVF template with PowerCLI you can use Import-VApp. But before you do, you’ll need to update our OVF configuration for deployment.

First, start by creating a variable containing your OVF path:

$ovfpath = "C:\bin\LoadMaster-VLM-7.2.39.1.15589.RELEASE-VMware-VBox-OVF-FREE.ovf"

Using Get-OvfConfiguration, view the configurable properties:

$ovfconfig = Get-OvfConfiguration -Ovf $ovfpath
$ovfconfig.ToHashTable() | ft

Mine only had two properties:

Name                   Value
----                   -----
NetworkMapping.Farm
NetworkMapping.Network

To set the values I ran:

$ovfconfig.NetworkMapping.Farm.Value = "DMZ"
$ovfconfig.NetworkMapping.Network.Value = "VM Network"

Before running Import-VApp I captured my host and datastore in variables:

$vmhost = get-vmhost labhost1.a1.local
$ds = Get-Datastore datastore1

And finally, using Import-VApp, deploy the OVF template:

Import-VApp -Source $ovfpath -OvfConfiguration $ovfconfig -Name KEMPLB -VMHost $vmhost -Datastore $ds -DiskStorageFormat Thin

Deploying Apache on CentOS Core

Since I’ve been working a lot with Jekyll and static sites, I figured it was time I actually hosted one on my own. I recently built a wiki site for my company’s IT documentation and decided I needed to host it somewhere. We’re mainly a Microsoft shop, so to try something new, I deployed it with Apache on CentOS 7 Core.

Installing CentOS

This should be straightforward, you can download CentOS 7 Minimal here.

I installed CentOS on VMware. To install VMware Tools you’ll need to run:

yum install open-vm-tools

Configure Network

Next, you’ll need to configure your network. To view all devices run nmcli d, which should return something like this:

DEVICE  TYPE      STATE      CONNECTION
ens192  ethernet  connected  Wired connection 1
lo      loopback  unmanaged  --

Confirm your device is listed. If not you probably need to install drivers as I did with VMware Tools.

To configure your adaptor, run nmtui:

nmtui

Continue to Edit a connection:

nmtui

Select your connection and then configure your settings as needed. I configured a static IP:

nmtui

Finally, back on the main NetworkManager screen, select Activate a connection. On the next screen Deactivate and then Activate your connection. You could also run service network restart.

To confirm your settings run ip a.

Install Apache

First make sure your CentOS install is up to date with:

sudo yum -y update

And then install Apache with:

sudo yum -y install httpd

Configure Apache

First, we need to allow port 80 through the firewall:

sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --reload

Next, we’ll configure Apache to start on boot:

sudo systemctl start httpd
sudo systemctl enable httpd

And confirm the status with:

sudo systemctl status httpd

You should now be able to browse to your server IP and view the default Apache website, confirming your configuration.

Upload Website Files

Finally, we’re ready to upload our website files. I used WinSCP to copy my files to /var/www/html.

And that’s it! Your site should now be hosted on your new Apache server!

Python in Visual Studio Code

A quick overview of setting up and running Python in Visual Studio Code on Windows 10…

Installation

  1. Install VS Code and Python.
  2. Set environment variable via PowerShell:
    [Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\Python27\", "User")
    
  3. Install pylint via Shell:
    python -m pip install pylint
    
  4. Install the VS Code Python extension.

Running Code

When working with .py files your terminal should automatically update to Python.

Python Terminal

To run your code you can open the Command Palette Ctrl+Shift+P and search for Python: Run Python File in Terminal.

Command Palette

You can also create a keybind:

  • File Preferences Keyboard Shortcuts
  • Search for python.execInTerminal

Before running your code using this method, you must save your file each time.

Mocking Multiple Outputs

Lately, I’ve been diving into Pester and today I ran into a function that called the same command twice, each time outputting something different (or it should). The function tests and if needed attempts to correct the status of a service. Below is the specific scenario I was trying to simulate:

Tests the service with Get-Service.
Service is stopped.
Attempts to start service.
Tests the service with Get-Service.
Service is running.
Script exits.

I was having a hard time with the fact that Get-Service was being run twice. The first time I needed it to return Stopped, but the second time Running. After a bit of research, I found an example using a script scoped variable as an execution counter. This is what it looks like:

Context 'When service is stopped and successfully started' {

### Initial count ###

  $Script:MockCounter = 0

  Mock -CommandName Get-Service -MockWith {

### Increments counter by 1 each time ###

    $Script:MockCounter++

### Mocks the first time Get-Service is run ###

    if ($Script:MockCounter -eq 0) {
        return @{Status='Stopped'}
    }

### Mocks the second time Get-Service is run ###

    if ($Script:MockCounter -eq 1) {
        return @{Status='Running'}
    }
  }
}

The first time Get-Service is mocked the counter is set to zero, returning @{Status='Stopped'}. $Script:MockCounter++ is executed, setting the counter to one, mocking Get-Service with @{Status='Running'} the second time.

This is only the second Pester test I’ve written. If there’s something I could have done better I’d love to know. You can contact me @theclintcolding or leave a comment below!

You can find the complete function and tests on Github.

Splatting for Readability

Splatting was introduced way back in PowerShell v2 and gave us a new way of passing parameters to our commands. I’ve found splatting to be the most helpful in scripts, where maximum readability is crucial. For example, before I learned about splatting, I had a command like this:

New-ADUser -Name $Displayname -DisplayName $Displayname -GivenName $FirstName -Surname $LastName -SamAccountName $Username -UserPrincipalName "$Username@mycompany.com" -AccountPassword $SecurePassword -Description $Title -Title $Title -Department $Department -Manager $Manager -Company "My Company" -HomeDrive "H:" -HomeDirectory "\\server\$Username" -Enabled $true

I passed 15 parameters to New-ADUser, and because it’s a production script, I named each parameter without truncating.

To increase readability via splatting, we first organize our parameter and value pairs into a hashtable assigned to a variable:

    $userparams = @{
        Name              = $Displayname;
        DisplayName       = $Displayname;
        GivenName         = $FirstName;
        Surname           = $LastName;
        SamAccountName    = $Username;
        UserPrincipalName = "$Username@mycompany.com";
        AccountPassword   = $SecurePassword;
        Description       = $Title;
        Title             = $Title;
        Department        = $Department;
        Manager           = $Manager;
        Company           = "My Company";
        HomeDrive         = "H:";
        HomeDirectory     = "\\server\$Username";
        Enabled           = $true
    }

Now we pass our parameters using @ followed by our hashtable parameter. Like so:

New-ADUser @userparams

By splatting our parameters we can now easily view them all neatly at once. If we ever need to make changes we don’t have to scroll for days and hope we stay on the right line. Future readers of our code will thank us too.

To splat a switch, pass the value as $true.

"UseDefaultCredential" = $true;

Quick Tip: Navigate a Datastore

This is a quick tip for managing files on a VMware datastore. We’re simply mapping a datastore to a PSDrive that we can then cd to.

$DS = Get-Datastore MyDatastore

New-PSDrive -Location $DS -Name ds -PSProvider VimDatastore -root "\"

Set-Location ds:\

After that, you can dir, Copy-DatastoreItem, Remove-Item, etc. Basically any command you can run against a file system, you can run against a datastore.

It comes in handy quite a bit, I’ve even wrapped it in a function.

Getting Started with API’s in PowerShell

PowerShell is an impressively powerful tool and one of its most powerful features is its ability to call API’s. For those of us that have a primarily infrastructure focused background, we understand what an API is and does, but we’re a little foggy on how to use them.

The first API project I completed used the GoDaddy API. I was frustrated with the tedious process of using the GoDaddy web portal to update DNS records across our various domains. So I decided to create more efficient PowerShell commands.

I started off doing some research, thankfully GoDaddy has wonderful documentation of their API’s. I decided a logical place to start was to return all the records associated with a specific domain. Under the /v1/domains API, I found a Get request that retrieves DNS records for the specified domain:

GoDaddy Get API

The first step of making any API call is finding the API URL and what type of authentication is needed, if any. By expanding the documentation pane above, I found both.

The URL was https://api.godaddy.com/v1/domains/mydomain.com/records and the authentication was in the request header:

GoDaddy API Header

Now that I knew what I needed, I could start building my PowerShell script. To make the API call I used Invoke-WebRequest with the following parameters:

  • URI: Specifies the Uniform Resource Identifier (URI) of the Internet resource to which the web request is sent.
  • Method: Specifies the method used for the web request. (Get, Post, Put, etc)
  • Headers: Specifies the headers of the web request. Enter a hash table or dictionary.

First I needed to create a “Authorization” table with my key/secret pair that I could pass into the request header. You can get your API keys here. (Use a production key.)

$apiKey = '2s7Yn1f2dW_W5KJhWbGwuLhyW4Xdvgb2c'
$apiSecret = 'oMmm2m5TwZxrYyXwXZnoN'

$Headers = @{}
$Headers["Authorization"] = 'sso-key ' + $apiKey + ':' + $apiSecret

Remember, we need to recreate the request header that we found in the documentation.

Once I was confident that I’d be able to authenticate, I ran Invoke-WebRequest using the Get method to retrieve all records for my domain. The API successfully returned the data in JSON:

C:\> Invoke-WebRequest https://api.godaddy.com/v1/domains/clintcolding.com/records/ -Method Get -Headers $Headers

StatusCode        : 200
StatusDescription : OK
Content           : [{"type":"A","name":"@","data":"192.30.252.153","ttl":600},{"type":"A","name":"@","data":"19
                    2.30.252.154","ttl":600},{"type":"CNAME","name":"email","data":"email.secureserver.net","ttl
                    ":3600},{"type":...
RawContent        : HTTP/1.1 200 OK
                    Access-Control-Allow-Credentials: true
                    Vary: Origin,Accept-Encoding
                    x-newrelic-app-data: PxQPUVdRCwcTVlRXDgkOVVATGhE1AwE2QgNWEVlbQFtcCxYkSRFBBxdFXRJJJH1nH0sXUxh
                    VWAsFWFhATVwHDV0DUQwX...
Forms             : {}
Headers           : {[Access-Control-Allow-Credentials, true], [Vary, Origin,Accept-Encoding],
                    [x-newrelic-app-data, PxQPUVdRCwcTVlRXDgkOVVATGhE1AwE2QgNWEVlbQFtcCxYkSRFBBxdFXRJJJH1nH0sXUx
                    hVWAsFWFhATVwHDV0DUQwXSlFRXBddEh5bRxsUUwhOXA1ZXlVbQ04HHQdIVQAGC1ReW1cFWwFbAQENCwpJG1cIVxFORg
                    5UVQRbDAIAXQRVBgMPREhXV18RAz4=], [Access-Control-Allow-Origin, *]...}
Images            : {}
InputFields       : {}
Links             : {}
ParsedHtml        : mshtml.HTMLDocumentClass
RawContentLength  : 696

To get a more legible output, I reran the command. This time, piping the output to ConvertFrom-Json.

C:\> Invoke-WebRequest https://api.godaddy.com/v1/domains/clintcolding.com/records/ -Method Get -Headers $Headers | ConvertFrom-Json

type  name           data                                 ttl
----  ----           ----                                 ---
A     @              192.30.252.153                       600
A     @              192.30.252.154                       600
CNAME email          email.secureserver.net              3600
CNAME ftp            @                                   3600
CNAME www            @                                   3600
CNAME _domainconnect _domainconnect.gd.domaincontrol.com 3600
MX    @              mailstore1.secureserver.net         3600
MX    @              smtp.secureserver.net               3600
NS    @              ns53.domaincontrol.com              3600
NS    @              ns54.domaincontrol.com              3600

After successfully retrieving the DNS records for my domain, I repeated the process, this time using the Put method.

In the end, this made updating DNS entries across our domains much more efficient. I also built custom DNS failover scripts for disaster recovery.

I have a GitHub project that includes the basic Get, Add, and Set commands for working with your GoDaddy DNS.

This is just a glimpse of what you can do with GoDaddy’s API. Make sure to check out the documentation, I’d love to see what you automate!