MSMVPs Blog News and Stuff

If you are a coder on MSMVPs Blogs, here are some instructions to assist you with the use of the Mivhak Plugin, including information on what is needed on your existing posts to show syntax highlighting.

Setup your plugin defaults:

Please log in to your dashboard.

1. Select the Mivhak plugin from the menu on the left.

2. In the General tab, you can turn Line Numbers On/Off and turn the Meta Bar On/Off – this is a global setting for YOUR blog.

3. You can pick a default language (go ahead and do this) and all your existing posts will recognize that language if you’ve used PRE or one of the other tags (see #4). You can override this setting for each individual post if you need to specify a language other than your default.

4. If you use (and have used) PRE for formatting your code in your posts turn PRE on in Auto Assign. When you turn it on, it will turn grey (it is off in the screen shot below)

5. Select Save.

setup1

Next, go to the Appearance tab.

1. There are many themes (which preview as you select them). Select the one you like. This is a global setting for YOUR blog.

2. Select Save.

3. You can change this later, and it will change all existing and new content.

setup2

Your Pre-existing blog posts with code if you used PRE formatting:

1. If you used PRE in the HTML (editor or Live Writer) there is a good chance that if you turn on PRE in Auto Assign that most of your existing posts will now display with syntax highlighting. Check a few and if needed, use the instructions below to tweak. (See test 3 for mivhak post for sample).

Fixing/Editing Your Existing Blog Posts if you didn’t use PRE formatting:

Log in to your blog dashboard.

1. If you edit a post in the dashboard, you’ll find that code in your post is already highlighted in blue. Click inside the blue area lightly anywhere and then hit the pencil icon.

miv1

2. Select language from the drop down

miv2

3. Once the language is selected, you can (if you want) scroll the code in the window  and then hit insert. When done, the right column will let you hit update.  Then you can visit the page to see your posts.

4. If you see …. in your code, see the post on C# Test for Gary Bushey for information on what we think causes this issue.

miv3

Creating NEW Blog Posts:

Using the WordPress Dashboard

Creating a new post is pretty much the same.You can create directly in the WordPress dashboard (when you do that, paste your code into the editor directly using the </> icon on the editor toolbar.

Using OneLive Writer

1. You can “cheat” in OneLive Writer. Insert your code and then highlight it using “  which will insert blockquote tags. Go to the Source tab and change to PRE tags. If you do this, you may not need to tweak in the WordPress dashboard at all.

2. If you use OneLive Writer and don’t want to mess with changing HTML tags, if you turn on PRE in mivhak, it might work “as is” – it worked for Charlie’s powershell code (see test 4)

See the test posts 1 and 2 (I used content from Charlie Russel’s blog for testing; both were authored in OneLive Writer and then edited in the dashboard withOUT PRE formatting turned on. This included a proof of concept to insure that when multiple blocks of code appeared in a single post that it was handled correctly.

Bottom Line:

Testing seems to indicate that the best experience is to turn PRE on for auto assign. At least for Powershell. We hope this works for other languages.

Please select a Theme from https://wordpress.org/themes/ and complete the full URL to that Theme, e.g.: https://wordpress.org/themes/writee/ (replacing the xxx with the theme) - Please don't pick a TwentyXXX Theme or anything that has not been updated in the past 90 days or anything with less than 1000 installs. We will check out the theme and either install it and let you know or tell you why we rejected it. Also, please do not select Premium Themes - only pick Free (trust you understand) .

We will try to check out your request as quickly as possible.

* indicates required field

Using One Live Writer Offline

1. Opened a .js file in VS2015

2. I copied the source to the clipboard

olw1

3. I switched to the Source tab on the bottom of the editor

4. pasted the code that follows below in to OLW Window below (again, in the Source tab, NOT the Edit tab):

5. I added the pre open and closing tags manually

olwolw3

The above shows two screen shots from OLW showing the actual code inserted. You will see the final result at the end of this post – I will upload it from OLW (post as Draft to blog) and then check it in the online editor in the dashboard and finish using the Mivhak plugin.

Online in the Dashboard Editor:

1. Put your cursor inside the code field so that you can select the pencil edit icon

2. Select JavaScript (or your chosen language) from the drop down and scroll the code to check. When satisfied, select Insert.

online2

//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
////
//// Copyright (c) Microsoft Corporation. All rights reserved

(function () {
    "use strict";

    // Helper variables.
    var app = WinJS.Application;
    var activation = Windows.ApplicationModel.Activation;
    var nav = WinJS.Navigation;
    var networkInfo = Windows.Networking.Connectivity.NetworkInformation;
    WinJS.strictProcessing();

    app.onactivated = function (args) {
        if (args.detail.kind === activation.ActivationKind.launch) {
            if (app.sessionState.history) {
                nav.history = app.sessionState.history;
            }

            args.setPromise(WinJS.UI.processAll().then(function () {
                return IO.loadStateAsync();
            }).done(function () {
                // Initialize network related event handlers and variables.
                networkInfo.addEventListener("networkstatuschanged", function () {
                    _updateConnectionInfo();
                }, false);
                _updateConnectionInfo();

                if (nav.location) {
                    nav.history.current.initialPlaceholder = true;
                    return nav.navigate(nav.location, nav.state);
                } else {
                    return nav.navigate(Application.navigator.home);
                }
            }, function () {
                // Error loading state.
                Status.error = true;
                Status.message = "Unable to load the list of available feeds.";

                // Disable the appbar and navbar since the app is not usable at this point.
                var appbar = document.getElementById("appbar").winControl;
                appbar.disabled = true;

                var navbar = document.getElementById("navbar").winControl;
                navbar.disabled = true;

                return nav.navigate(Application.navigator.home);
            }));
        }
    };

    app.oncheckpoint = function (args) {
        // This application is about to be suspended. Save any state
        // that needs to persist across suspensions here. If you need to 
        // complete an asynchronous operation before your application is 
        // suspended, call args.setPromise().
        app.sessionState.history = nav.history;
        args.setPromise(IO.saveStateAsync());
    };

    function _updateConnectionInfo() {
        /// 
<summary>
        /// Determine the network status and the user's connection cost.  The connection cost
        /// will decide whether or not we bypass the cache when retrieving feeds.
        /// For more information on managing metered network cost constraints, see:
        /// http://go.microsoft.com/fwlink/?LinkId=296673
        /// </summary>

        var connectionProfile = networkInfo.getInternetConnectionProfile();

        if (connectionProfile) {
            Status.error = false;
            Status.message = "";

            // Determine whether to bypass the cache based on the data transfer limitations
            // set by the carrier.  Using the cache helps protect the user from accumulating
            // monetary charges if they're on a metered network.
            Status.bypassCache = true;
            var connectionCost = connectionProfile.getConnectionCost();
            if (connectionCost) {
                if (connectionCost.overDataLimit || connectionCost.roaming) {
                    Status.bypassCache = false;
                }
            }
        } else {
            Status.error = true;
            Status.message = "Not connected to the Internet.";
        }
    };

    app.start();

    // Constants defining limits of the ListView.
    WinJS.Namespace.define("ListViewLimits", {
        subLimit: 10,
        maxItems: 15
    });

    // Status of the app.
    WinJS.Namespace.define("Status", {
        error: false,
        message: "",
        bypassCache: true
    });
})();

WLW may have inserted SPAN tags – depending how you used Windows Live Writer or Open Live Writer or how you formatted your code originally, you might have code that looks like:

gary2

Here’s what the source of that code looks like:

garyb

We think it’s the SPAN tags that create the issue. If the source is edited to look like:

gary3

then you get a better result:

namespace BaseTranslate.Interfaces
{
    public interface IBaseTranslate : IActor
    {
        Task TranslateData(string data);
    }
}

code from Charlie

just pasted this into One Live Writer. Didn’t format

<#
.Synopsis
Renames the Active Directory users
.Description
Rename-myADUser reads a CSV file to identify an array of users. The users are then renamed to the new name in Active Directory.
.Example
Rename-myADUser
Renames the AD Accounts of the users in the default "ADUsers.csv" source file
.Example
Rename-myADUser -Path "C:\temp\ChangedUsers.txt"
Renames the AD accounts of the users listed in the file C:\temp\ChangedUsers.txt"
.Parameter Path
The path to the input CSV file of format:
OldSam,NewName,GivenName,Surname,DisplayName,SAMAccountName,UserPrincipalName,EmailAddress

The default value is ".\ADUsers.csv".  
.Inputs
[string]
.Notes
    Author: Charlie Russel
 Copyright: 2017 by Charlie Russel
          : Permission to use is granted but attribution is appreciated
   Initial: 03/09/2017 (cpr)
   ModHist: 
          :
#>
[CmdletBinding()]
Param(
     [Parameter(Mandatory=$False,Position=0)]
     [string]
     $Path = ".\ADUsers.csv" 
     )

$ADUsers = @()
If (Test-Path $Path ) {
   $ADUsers = Import-CSV $Path
} else { 
   Throw  "This script requires a CSV file with user names and properties."
}
$PDC = (Get-ADDomain).PDCEmulator
Write-Verbose "The PDC Emulator has been identified as $PDC"
Write-Verbose " "

ForEach ($User in $ADUsers ) {
   Write-Verbose "Modifying $user.OldSam to $user.NewName" 
   Sleep 3
   Get-ADUser -Identity $User.OldSam -Properties * | `
   Set-ADUser -Server $PDC `
              -DisplayName $user.DisplayName `
              -EmailAddress $User.EmailAddress `
              -SamAccountName $User.SamAccountName `
              -GivenName $User.GivenName `
              -Surname $User.Surname `
              -UserPrincipalName $user.UserPrincipalName `
              -PassThru | `
   Rename-ADObject -NewName $user.NewName -Server $PDC -PassThru
}

some powershell from Charlie:

  
function Get-myFreeSpace {
<#
.Synopsis
Gets the disk utilization of one or more computers
 
.Description
Get-myFreeSpace queries an array of remote computers and returns a nicely formatted display of 
their current disk utilization and free space. The output can be redirected to a file or other 
output option using standard redirection, or can be piped to further commands.

.Parameter ComputerName
An array of computer names from which you want the disk utilization

.Example
(Get-VM -Name “*server*” | Where-Object {$_.State -eq ‘Running’).Name } | Get-myFreeSpace
Gets the free disk space of the running virtual machines whose name includes 'server'

.Inputs
[string[]]

.Notes
 Original Author: Charlie Russel
Secondary Author: Robert Carlson
Copyright: 2017 by Charlie Russel
         : Permission to use is granted but attribution is appreciated
  Initial: 26 Nov, 2014 (cpr)
  ModHist: 29 Sep, 2016 — Changed default to array of localhost (cpr)
         : 18 Apr, 2017 — Changed to use Write-Output,accept Pipeline,added man page, (cpr)
         : 20 Apr, 2017 — Changed output to pscustomobject rather than string, etc.(RC)
#>

[CmdletBinding()]
Param(
[Parameter(Mandatory=$False,Position=0,`
           ValueFromPipeline=$True,`
           ValueFromPipelineByPropertyName=$True,`
           ValueFromRemainingArguments=$True)]
           [alias(“Name”,”Computer”)]
           [string[]]
           $ComputerName = @(“localhost”)
           )

Begin {
   if ($Input) {
      $ComputerName = @($Input) 
   }
}

Process {
   ForEach ( $Computer in $ComputerName ) {
      $volumes = Get-WmiObject -ComputerName $Computer -Class Win32_Volume -ErrorAction SilentlyContinue 
      foreach ($volume in $volumes) {
         $volumeData = [pscustomobject]@{
            ComputerName=$Computer 
            Drive=$volume.DriveLetter
            VolumeLabel=$volume.Label
            VolumeSize=”{0:N0}” -f ($volume.Capacity / 1GB)
            FreeSpace=”{0:N0}” -f ($volume.FreeSpace/1GB)
            }
         if ($volume.Capacity) {
            $percentage = “{0:P0}” -f ($volume.FreeSpace / $volume.Capacity)
            $volumeData | Add-Member -NotePropertyName “PercentageFree” -NotePropertyValue $percentage
         } else {
            $volumeData | Add-Member -NotePropertyName “PercentageFree” -NotePropertyValue “n/a”
         }
         Write-Output $volumeData
      }
   }
 }
}
  

From Charlie Russel’s Blog, without using the “” in OneLive Writer.

So, as I showed in the previous post, I’ve got my new VM built, but it’s not really ready for use yet. For one thing, it needs a DVD attached and the boot order set, plus I want to add a second NIC, and change the number of processors assigned to it. First, setting up the memory, processors, static MAC address for the NIC and configuring the DVD if we’re booting from DVD. (Which, I admit, I don’t often do. Mostly I copy over a SysPrep’d VHDX file.)

To do this, I have a function, of course, called Set-myVMConfig, to do most of it, and a separate one that I use to configure the second NIC, Add-myNetAdapter

Function Set-myVMConfig {
   Write-Verbose "Setting Processor Count to 4 for $VMName"
   Set-VMProcessor      -VMName $VMName -Count 4
   Write-Verbose "Enabling Dynamic Memory"
   Set-VMMemory         -VMName $VMName -DynamicMemoryEnabled $True
   Write-Verbose "Assigning static MAC address of $MacAdd"
   Get-VMNetworkAdapter -VMName $VmName `
      | Set-VMNetworkAdapter -StaticMacAddress "$MacAdd"
   if ($DVD) {
      Write-Verbose "Building from DVD, so adding DVD drive, and configuring boot order"
      if (! $client) { 
         Add-VMDvdDrive -VMName $VmName
         Set-VMDvdDrive -VMName $VmName  -Path $ServerISO 
         $vmDVD     = Get-VMDvdDrive -VMName $VmName
         $vmDrive   = Get-VMHardDiskDrive -VMName $VmName 
         Set-VMFirmware -VMName $VmName  -FirstBootDevice $vmDVD 
         Set-VMFirmware -VMName $VmName -BootOrder $vmDVD,$vmDrive
      } else {
         Add-VMDvdDrive -VMName $VmName
         Set-VMDvdDrive -VMName $VmName -Path $ClientISO 
         $vmDVD     = Get-VMDvdDrive -VMName $VmName
         $vmDrive   = Get-VMHardDiskDrive -VMName $VmName 
         Set-VMFirmware -VMName $VmName   -FirstBootDevice $vmDVD 
         Set-VMFirmware -VMName $VmName   -BootOrder $vmDVD,$vmDrive
      }
   }
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span>

This sets the # of processors to 4, enables dynamic memory, sets a static MAC address on the first NIC, adds a DVD drive if appropriate, and sets the boot order to boot from the specified ISO file.

Almost done – now, all we need to do is add a second network adapter, and set it to a fixed MAC address as well.

Function Add-myNetAdapter {
   Write-Verbose "Adding second network adapter"
   Add-VmNetworkAdapter -VMName $VmName `
                        -SwitchName '199 Network' `
                        -StaticMacAddress "$199MacAdd" `
                        -Name '199 Ethernet'
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span>

Now, that we have all the functions, all we need to do is execute them, and that all happens with:

If (! ( Get-VM -Name $VmName -ErrorAction Continue 2>$NULL) ) {
   Test-SourcePath
   Test-Clean
   Copy-myVHD -wait
   Write-Verbose "VHD's copied if we were doing that, now creating the VM..."
   Create-myVM
   Write-Verbose "VM Created"
   $myVM = Get-VM -VMName $VMName
}
Set-myVMConfig
Add-myNetAdapter
$myVM | Format-List<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span>

And, since this whole thing has been broken up across a couple of posts, here’s the whole script, including full Get-Help support.

New-myVM.ps1

<# 
.Synopsis
    Creates a new VM
.Description
    New-myVM and New-myClientVM make a new VM of Name $1 and MAC Address in the $MacBase 
    range of MAC addresses. If the command is run as New-myClientVM, then the -Client 
    parameter is assumed unless overridden at the command line. 
.Example 
   New-myVM -VMName Trey-DC-02 02
   Creates a new Server VM of name "Trey-DC-02" in the default MAC address range
   with the "02" as the final octet of MAC address. 
.Example 
   New-myVM -Name trey-client-22 -MacFinal 16 -DVD -Client $True
   Creates a new Client VM of name "trey-client-22" in the default MAC address range
   with 16 as the final octet of MAC address. The VM is installed from the default 
   Server 2016 DVD. 
.Example 
   New-myVM Trey-DC-02 02 -DVD
   Creates a new Server VM of name "Trey-DC-02" in the default MAC address range
   with the "02" as the final octet of MAC address. The VM is installed from the 
   default Server 2016 DVD. 
.Example
    New-myVM -VmName "Trey-WDS-11" -MacFinal "0B" -MacBase "00-15-5D-32-64-"
    Creates a new Server VM of name Trey-WDS-11 in a non-default MAC address range. 
.Example
   New-myClientVM -Name trey-client-01 
   Creates a new Windows 10 client VM, 'trey-client-01' using the default VHD, and 
   will prompt for the final 2 digits of the MAC address. 
.Example
   New-myVM -Name trey-client-01 -MACFinal 65 -Client $True -Source 'V:\Source' -Path 'Y:\'
   Creates a new Windows 10 client VM, 'trey-client-01' using the sysprep'd image at V:\Source, 
   and creating the VM at Y:\trey-client-01. The final two digits of the MAC address will 
   be 65. 
.Parameter VmName
   The name of the new VM
.Parameter MacFinal
   The last two digits in the MAC address of the VM
.Parameter MacBase
   The base MAC address for this VM. The default base is  "00-15-5D-32-10-"  
.Parameter DVD
   A switch that controls whether a DVD is added to the VM and used to mount an ISO for the 
   install. The default is to build the VM with no DVD drive. 
.Parameter Client
  A Boolean. When run as New-myVM, $Client defaults to False. If run as New-myClientVM, 
  the default is true. In either case, the command line parameter overrides the default. 
.Parameter Path
   The target path for the virtual machine. Default is to V:\. This is the base path, to 
   which the VMName is added to build the final path.
.Parameter Source
   The source path of the DVD or VHD used to build the virtual machine. Default is V:\Source. 
.Parameter vmSwitch
   The Hyper-V network switch to connect the VM to. New-myVM creates two network adapters. 
   One is connected to the 199 Network, and the second is controlled by the vmSwitch parameter. 
   The default is "Local-10", the internal lab switch. 
.Parameter 2012R2
   The 2012R2 switch specifies the use of the Server 2012 R2 image. 
.Inputs 
    [string]
    [string]
    [string]
    [switch]
    [Boolean]
    [string]
    [string]
    [string]
.Notes
    Author: Charlie Russel
 Copyright: 2017 by Charlie Russel
          : Permission to use is granted but attribution is appreciated
   ModHist: 1/1/2014 Initial
          : 1/31/2015 Mod for new parameter handling and comment header
          : 3/20/2015 Mod to use Sysprepped VHD and -10 MAC
          : 4/19/2015 Mod for verbose and running in a wrapper
          : 5/16/2016 Mod for new labhost 
          : 9/24/2016 Mod for New-myClientVM
          : 12/21/2016 Added additional parameters, updated help. (cpr)
          : 02/17/2017 Fixed problem with DVD and Gen2, updated help. (cpr)
#>

[CmdletBinding()]
Param ([Parameter(Mandatory = $True,Position = 0)][alias("Name")][string]$VmName,
       [Parameter(Mandatory = $True,Position = 1)][alias("Final")][string]$MacFinal,
       [Parameter(Mandatory = $False)][alias("Base")][string]$MacBase = "00-15-5D-32-0A-",
       [Parameter(Mandatory = $False)][string]$199MacBase = "00-15-5D-32-CE-",
       [Parameter(Mandatory=$False)][Switch]$DVD,
       [Parameter(Mandatory=$False)][Boolean]$Client=($myInvocation.myCommand.Name -match "Client"),
       [Parameter(Mandatory=$False)][alias("Target")][string]$Path = "V:",
       [Parameter(Mandatory=$False)][alias("VHDSource","DVDBase")][string]$Source = "V:\Source",
       [Parameter(Mandatory=$False)][alias("LocalSwitch","Network")][string]$vmSwitch = "Local-10",
       [Parameter(Mandatory=$False)][switch]$2012R2
       )

$MacAdd = $MacBase + $MacFinal
$199MacAdd = $199MacBase + $MacFinal
Write-Verbose "MacFinal is $MacFinal" 
Write-Verbose "MacAdd is $MacAdd on switch $vmSwitch" 
Write-Verbose "VMName is $VMName"
Write-Verbose "Client is $Client"
Write-Verbose "Path is $Path, Source is $Source, and 199 MAC address is $199MacBase + $MacFinal"
Write-Verbose "Sleeping for 5 seconds to give you a chance to exit..."
sleep 5

$VMBase     = "$Path\$VMName"
$VHDSource  = $Source
$DVDBase    = $Source
$VHDBase    = "$VMBase\Virtual Hard Disks"
$SysVHD     = "$VMBase\Virtual Hard Disks\$VmName-System.vhdx"
$MachineBase= "$VMBase\Virtual Machines"
$ServerISO  = "$DVDBase\en_windows_server_2016_x64_dvd_9718492.iso"
$ClientISO  = "$DVDBase\en_windows_10_enterprise_version_1607_updated_jan_2017_x64_dvd_9714415.iso"
$ClientVHD  = "$Source\Generalized-client.vhdx"
if ($2012R2) { 
   $ServerVHD = "$Source\Generalized-2012R2.vhdx"
} else {
   $ServerVHD  = "$Source\Generalized-System.vhdx"
}

Function Test-SourcePath () {
   if ($Client) {
      if ($dvd) {
         if (Test-Path $ClientISO) {
            Write-Verbose "Install DVD found at $ClientISO"
         } else {
            Throw "Client ISO not found at $ClientISO" 
         }
      } elseif (Test-Path $ClientVHD) { 
         Write-Verbose "Source VHD found at $ClientVHD"
      }
   } else {
      if ($dvd) {
         if (Test-Path $ServerISO) {
            Write-Verbose "Install DVD found at $ServerISO"
         } else {
            Throw "Server ISO not found at $ServerISO" 
         }
      } elseif (Test-Path $ServerVHD) { 
         Write-Verbose "Source VHD found at $ServerVHD"
      }
   }
}

if (! (Test-Path $VHDBase ) ) { 
   mkdir $VHDBase
}
if (! (Test-Path $MachineBase ) ) { 
   mkdir $MachineBase
}

Function Test-Clean () {
   If (Test-Path $VHDBase\*.vhdx ) {
      Throw "Found an existing VHD. Please clean up the target path and try again."
   }
}

function Copy-myVhd () {
      if ( $DVD ) {
         Write-Verbose "DVD specified. Not copying source VHD to $SysVHD"
      } else { 
         if ( $Client ) { 
            Write-Verbose "Creating VM from Sysprep'd VHD base $ClientVHD"
            cp $ClientVHD $SysVHD 
         } else { 
         Write-Verbose "Creating VM from Sysprep'd VHD base $ServerVHD"
            cp $ServerVHD $SysVHD
         } 
      }
}

function Create-myVM () { 
if ($DVD ) { 
  Write-Verbose "Creating $vmname from DVD with the following command:"
  Write-Verbose "New-VM -Name $VmName -MemoryStartupBytes 1024MB -BootDevice VHD -Generation 2 -SwitchName $vmSwitch -NewVHDPath $SysVHD -NewVHDSize 200GB -Path $MachineBase "
  Sleep 3
  New-VM -Name $VmName `
       -MemoryStartupBytes 1024MB `
       -BootDevice VHD `
       -Generation 2 `
       -SwitchName $vmSwitch `
       -NewVHDPath $SysVHD `
       -NewVHDSize 200GB `
       -Path $MachineBase
} else { 
  New-VM -Name $VmName `
       -MemoryStartupBytes 1024MB `
       -BootDevice VHD `
       -Generation 2 `
       -SwitchName $vmSwitch `
       -VHDPath $SysVHD `
       -Path $MachineBase
  }
}

Function Set-myVMConfig {
   Write-Verbose "Setting Processor Count to 4 for $VMName"
   Set-VMProcessor      -VMName $VMName -Count 4
   Write-Verbose "Enabling Dynamic Memory"
   Set-VMMemory         -VMName $VMName -DynamicMemoryEnabled $True
   Write-Verbose "Assigning static MAC address of $MacAdd"
   Get-VMNetworkAdapter -VMName $VmName `
      | Set-VMNetworkAdapter -StaticMacAddress "$MacAdd"
   if ($DVD) {
      Write-Verbose "Building from DVD, so adding DVD drive, and configuring boot order"
      if (! $client) { 
         Add-VMDvdDrive -VMName $VmName
         Set-VMDvdDrive -VMName $VmName  -Path $ServerISO 
         $vmDVD     = Get-VMDvdDrive -VMName $VmName
         $vmDrive   = Get-VMHardDiskDrive -VMName $VmName 
         Set-VMFirmware -VMName $VmName  -FirstBootDevice $vmDVD 
         Set-VMFirmware -VMName $VmName -BootOrder $vmDVD,$vmDrive
      } else {
         Add-VMDvdDrive -VMName $VmName
         Set-VMDvdDrive -VMName $VmName -Path $ClientISO 
         $vmDVD     = Get-VMDvdDrive -VMName $VmName
         $vmDrive   = Get-VMHardDiskDrive -VMName $VmName 
         Set-VMFirmware -VMName $VmName   -FirstBootDevice $vmDVD 
         Set-VMFirmware -VMName $VmName   -BootOrder $vmDVD,$vmDrive
      }
   }
}

Function Add-myNetAdapter {
   Write-Verbose "Adding second network adapter"
   Add-VmNetworkAdapter -VMName $VmName `
                        -SwitchName "199 Network" `
                        -StaticMacAddress "$199MacAdd" `
                        -Name "199 Ethernet"
}

If (! ( Get-VM -Name $VmName -ErrorAction Continue 2>$NULL) ) {
   Test-SourcePath
   Test-Clean
   Copy-myVHD -wait
   Write-Verbose "VHD's copied if we were doing that, now creating the VM..."
   Create-myVM
   Write-Verbose "VM Created"
   $myVM = Get-VM -VMName $VMName
}
Set-myVMConfig $myVM
Add-myNetAdapter $myVM
$myVM | Format-List<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span>

I hope you find this script useful, and I’d love to hear comments, suggestions for improvements, or bug reports as appropriate. As always, if you use this script as the basis for your own work, please respect my copyright and provide appropriate attribution.

Next up in the Building a Lab with PowerShell series will how to configure your DHCP server with PowerShell. This will take advantage of the fixed MAC addresses I create for all my lab machines and use these to populate DHCP Reservations.

Test post with OneLive Writer using the “” function from toolbar. This is content from Charlie Russel’s blog…

This came up at work the other day. Another admin had attempted to rename an AD User account and it had only partially gotten renamed — the SAM Account, Name and Display name were all correct, but the old user name was still showing up in a couple of places, including the login screen. The user was not happy, so I was asked to fix it, and provide a script that would handle it correctly. I poked around a bit and found the issue – even if you set all of the obvious properties correctly (and the other admin had missed UPN), it still won’t show correctly on that logon screen — you need to actually rename the AD object itself. So, after I fixed the problem user’s account, I wrote up a script to solve the problem for the next time. I chose to use a CSV file as the input, but you could easily re-work this to work off either a CSV file or a set of command-line parameters. But honestly, I don’t ever want to have to enter that many command-line parameters for a simple script. Especially if I have more than one to change.

The script uses Get-ADUser with the old name, then pipes it to Set-ADUser, and finally pipes it to Rename-ADObject to finish the process.  I even gave it basic help. 🙂

<#
.Synopsis
Renames the Active Directory users
.Description
Rename-myADUser reads a CSV file to identify an array of users. The users are then renamed to the new name in Active Directory.
.Example
Rename-myADUser
Renames the AD Accounts of the users in the default "ADUsers.csv" source file
.Example
Rename-myADUser -Path "C:\temp\ChangedUsers.txt"
Renames the AD accounts of the users listed in the file C:\temp\ChangedUsers.txt"
.Parameter Path
The path to the input CSV file of format:
OldSam,NewName,GivenName,Surname,DisplayName,SAMAccountName,UserPrincipalName,EmailAddress

The default value is ".\ADUsers.csv".  
.Inputs
[string]
.Notes
    Author: Charlie Russel
 Copyright: 2017 by Charlie Russel
          : Permission to use is granted but attribution is appreciated
   Initial: 03/09/2017 (cpr)
   ModHist: 
          :
#>
[CmdletBinding()]
Param(
     [Parameter(Mandatory=$False,Position=0)]
     [string]
     $Path = ".\ADUsers.csv" 
     )

$ADUsers = @()
If (Test-Path $Path ) {
   $ADUsers = Import-CSV $Path
} else { 
   Throw  "This script requires a CSV file with user names and properties."
}
$PDC = (Get-ADDomain).PDCEmulator
Write-Verbose "The PDC Emulator has been identified as $PDC"
Write-Verbose " "

ForEach ($User in $ADUsers ) {
   Write-Verbose "Modifying $user.OldSam to $user.NewName" 
   Sleep 3
   Get-ADUser -Identity $User.OldSam -Properties * | `
   Set-ADUser -Server $PDC `
              -DisplayName $user.DisplayName `
              -EmailAddress $User.EmailAddress `
              -SamAccountName $User.SamAccountName `
              -GivenName $User.GivenName `
              -Surname $User.Surname `
              -UserPrincipalName $user.UserPrincipalName `
              -PassThru | `
   Rename-ADObject -NewName $user.NewName -Server $PDC -PassThru
}