Friday, April 11, 2014

How to use PowerShell to find what computer a user is CURRENTLY logged into.

I recently had an issue where I needed the ability to identify the PC(s) where a user was logged into.  I found part of the answer here: 

http://deployhappiness.com/find-out-what-computer-a-user-logged-into/#comment-5068

I won't reprint it here but the jist is by using a logon/logoff VB script, and a GPO, you can populate a field in the Active Directory record of each PC in a domain.

That part works really well.  After that you would need to search AD for the username to identify the PC.

What I've done is created a PowerShell script with a GUI to do it for you.  Now, it's not finished yet, but it does work.  The code below is dirty and as soon as I get it the way I want I'll update this posting, but for now, it works pretty well.  Don't complain about bugs,I know it has them, I'm working on it.  And, yes, I have no doubt some things could be done better or more efficiently, feel free to post improvements.

What it has (or will have):
  • Self relocates to c:/scripts and drops an icon on the current users desktop for future launches
  • Self relocation is optional
  • Colorized output
  • GUI driven for simplicity
  • Various levels of debugging options
  • Reports items located in both AD "Description" field, and "Managed By" field.

Watch for word wrapping...

<#======================================================================================
         File Name : Find-User.ps1
   Original Author : Kenneth C. Mazie (kcmjr AT kcmjr.com)
                   :
       Description : Will locate the PC a user is currently logged on to.
                   :
             Notes : Normal operation is with no command line options. Requires PowerShell AD module.
                   :
          Warnings :
                   :
             Legal : Public Domain. Modify and redistribute freely. No rights reserved.
                   : SCRIPT PROVIDED "AS IS" WITHOUT WARRANTIES OR GUARANTEES OF
                   : ANY KIND. USE AT YOUR OWN RISK. NO TECHNICAL SUPPORT PROVIDED.
                   :
           Credits : Code snippets and/or ideas came from many sources including but
                   :   not limited to the following:
                   : http://deployhappiness.com/find-out-what-computer-a-user-logged-into/#comment-5068
                   : https://stackoverflow.com/questions/2688547/muliple-foreground-colors-in-powershell-in-one-command
                   :
    Last Update by : Kenneth C. Mazie
   Version History : v1.0 - 04-11-14 - Original
    Change History : v1.1 - 00-00-00 -  
                   :
=======================================================================================#>

clear-host

#-------------------------------[ Begin ]---------------------------------------#>

$Debug1 = $false                                                                    #--[ Set to true to have extra messages to console during run  
$Debug2 = $false                                                                    #--[ Set to true to have extra messages to console during run  
$CurrentVersion = "1.0"
if (!(Get-Module -Name ActiveDirectory)){Import-Module ActiveDirectory}
$ErrorActionPreference = "SilentlyContinue"
$TargetPath = "C:\Scripts"                                                             #--[ Where the script "should" live
$ScriptFullName = ($MyInvocation.MyCommand).Name                                       #--[ EX: script.ps1
$ScriptFullPath = ($MyInvocation.MyCommand).Path                                       #--[ EX: C:\Scripts\script.ps1
$ScriptHomeDir = split-path -parent $ScriptFullPath                                    #--[ EX: C:\Scripts, where the script resides
$ScriptWorkingDir = $pwd.path                                                          #--[ EX: C:\Scripts or C:\temp, where the script executes from
$ScriptShortName = [system.io.path]::GetFilenameWithoutExtension($ScriptFullPath)      #--[ EX: script (no extention)
$osv = [environment]::osversion.VersionString                                          #--[ Get Windows version, not required, just for convenience
$windir = [System.Environment]::ExpandEnvironmentVariables("%WINDIR%")                 #--[ Get %windir% environment variable
#$windir = $env:windir                                                                #--[ Alternate format
$Home = [System.Environment]::ExpandEnvironmentVariables("%USERPROFILE%")           #--[ Get %userprofile% environment variable
$ThisComputer1 = [System.Net.Dns]::GetHostName()                                      #--[ NOTE: No reason for both forms, just because.
$ThisComputer2 = $env:COMPUTERNAME
$Relocate = $false                                                                  #--[ Change to $true to enable automatic relocation of the script

Function LocateUser {
  Param ([string]$TargetUser)
  $TargetUserSAM = $Null
  $TargetUserName = $Null
  $ResultA = @()
  $ResultB = @()
  $FullArray = @()
  $SubArray = @()
  $Computers = Get-ADComputer -Filter * -Properties ManagedBy,Description | Select Name, ManagedBy, Description | Sort -Property Name

  If ($TargetUser.contains(" ")){                                                   #--[ Get name or ID depending on what uer entered.
    [string]$TargetUserSAM = (Get-ADUser -Filter "Name -eq '$TargetUser'" | Select-Object name, samaccountname).samaccountname
    [string]$TargetUserName = $TargetUser.ToUpper()
  }Else{
    [string]$TargetUserName = (Get-ADUser -Filter "samAccountName -eq '$TargetUser'" | Select-Object name, samaccountname).name
    [string]$TargetUserSAM = $TargetUser.ToUpper()
  }
  If ($TargetUserName -eq ""){$TargetUserName = "UNKNOWN"}
  $TargetUser = (Get-Culture).TextInfo.ToTitleCase($TargetUser.ToLower())
  If ($Debug2){Write-Host "Checkpoint 1:  TUname= " $TargetUserName  " TUsam=" $TargetUserSAM " TU=" $TargetUser}

  ForEach ($Computer in $Computers){  #--[ Sort through AD Computers, extract ManagedBy and Description ]--
    $ManagedBy = ""
    $ManagedBy = (((($Computer.managedby).split(",")).split("="))[1]).ToUpper()
    $Description = ""
    $Description = ($Computer.description).ToUpper()
    #If ($Debug2){Write-Host "Checkpoint 2: Comp=" $Computer.name   " ManagedBy=" $ManagedBy   " Description="$Description }  #--[ computer, description, & who is its manager in AD
    If ($Debug2){color-Write Checkpoint 2: -Red Comp = $Computer.name -Yellow LoggedOn = $ManagedBy -Green Description = $Description }  #--[ computer, description, & who is its manager in AD
    $SubArray = $Computer.name,$ManagedBy,$Description
    $FullArray += ,$SubArray    #--[ Fullarray will contain Computer,ManagedBy,Description ]--
  }

  $count = $FullArray.count


 # write-host "B = " $ResultA.count
 # write-host "A = " $ResultB.count

   $ResultA += $TargetUserName + "  (" + $TargetUserSAM + ")`n`nLogged on to:`n"
  $ResultB += "Assigned to:  `n" #$TargetUserName + "  (" + $TargetUserSAM + ")`n"

  While ($count -gt 0){
    #If ($Debug2){Write-Host "Checkpoint 3:  $count   DetectedUser=" $FullArray[$count][1] " TargetUser="  $TargetUserName  "PC-Descr="  $Description}
    If ($Debug2){$FullArrayCount1 = $FullArray[$count][1];color-Write Checkpoint 3: -Magenta $count -Yellow DetectedUser = $FullArrayCount1 -Cyan TargetUser = $TargetUserName -Green PC-Descr = $Description}
  
    If ($FullArray[$count][1] -eq $TargetUserName){
     # If ($Debug2){Write-Host "Checkpoint 4:  FAcount$count-0=" $FullArray[$count][0] "  FAcount$count-1="  $FullArray[$count][1] " User=" $TargetUser }
      $ResultA += $FullArray[$count][0] + "`n"
    }
      
    If ($FullArray[$count][2] -eq $TargetUserName){
      $ResultB += $FullArray[$count][0] + "`n"
      }#write-host $Description}
  
    #If ($Debug2){$FullArrayCount1 = $FullArray[$count][1];$X = $count-1;Write-Host "Checkpoint 5:  FullArrayCount $X = " $FullArrayCount1}
    $count --
  }


  If ($Debug2){$x = $ResultA.count;color-Write Checkpoint 6: -Red ResultACount = $x (includes target)}
    If ($Debug2){color-Write -Red ResultBCount = $ResultB.count (includes target)}

  #write-host "A = " $ResultA.count
  #write-host "B = " $ResultB.count
  #write-host $ResultA[0]  $ResultA[1]
  #write-host $ResultB[0]  $ResultB[1]




  If ($ResultA.count -eq 2){  #-[  If results plus target > 2 then display multiples ]--
    $objLabel.Text = "Results for:   $ResultA[0]"    #--[ Overwrite the text with the username ]--
    $objLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
    $objLabel.Refresh()
  
    $objForm.Controls.remove($objTextBox)
  
    #--[ Add Result 1 Lable ]--
    If ($Debug1){Write-Host "Adding result label 1"}
    $objResult1 = New-Object System.Windows.Forms.Label
    $objResult1.Location = New-Object System.Drawing.Size(10,50)
    $objResult1.Size = New-Object System.Drawing.Size(315,20)
    $objResult1.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
    $objResult1.Text = "Currently logged onto:   " + $ResultA[1]
    $objForm.Controls.Add($objResult1)

    #--[ Add Result 2 Label ]--
    If ($ResultB[1] -eq $Null){$Assigned = "N/A"}Else{$Assigned = $ResultB[1]}
    If ($Debug1){Write-Host "Adding result label 2"}
    $objResult2 = New-Object System.Windows.Forms.Label
    $objResult2.Location = New-Object System.Drawing.Size(10,70)
    $objResult2.Size = New-Object System.Drawing.Size(315,20)
    $objResult2.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter  
    $objResult2.Text = "Assigned to:   $Assigned"
    $objForm.Controls.Add($objResult2)


  
  }ElseIf ($ResultA.count -gt 2){
    $objTextBox.Enabled = $false
    $objTextBox.Text = "       ... More Than One Found ...     "
    $objTextBox.Refresh()
   # $objForm.close()
    [System.Windows.Forms.MessageBox]::Show("$ResultA `n $ResultB","Multiple Logons Found")
    # $objTextBox.Text = "        ... More Than One Found ...     "
  }Else{
    $objTextBox.Text = "          ......  NOT FOUND  ......          "
    $objTextBox.Refresh()
  }
      If ($Debug1){Write-Host "Adding locate another"}
    #$objResult1.Text = "Currently logged onto: "
      $objProcessButton.Text = "Locate Another"
    $objProcessButton.tabindex = 1
    #$objTextBox.Text = " "
    $objTextBox.Enabled = $false
    $objTextBox.Refresh()
    $objProcessButton.Add_Click({$objTextBox.Text = "          ......  RELOADING  ......          ";$objTextBox.Refresh();ResetGUI})





 # $objCloseButton.Location = new-object System.Drawing.Size(($ButtonLeft+60),$ButtonTop)
 # $objForm.Controls.Remove($objProcessButton)
  
  $TargetUserSAM = $Null
  $TargetUserName = $Null
  $Result.Clear()
  $FullArray.Clear()
  $SubArray.Clear()
  $IterationResult.Clear()
  $TargetUser = $False
} #--[ End LocateUser Function ]--


function color-Write
{
    $allColors = ("-Black",   "-DarkBlue","-DarkGreen","-DarkCyan","-DarkRed","-DarkMagenta","-DarkYellow","-Gray",
                  "-Darkgray","-Blue",    "-Green",    "-Cyan",    "-Red",    "-Magenta",    "-Yellow",    "-White")
    $foreground = (Get-Host).UI.RawUI.ForegroundColor # current foreground
    $color = $foreground
    [bool]$nonewline = $false
    $sofar = ""
    $total = ""

    foreach($arg in $args)
    {
        if ($arg -eq "-nonewline") {
        $nonewline = $true
        }elseif ($arg -eq "-foreground"){
            if ($sofar) { Write-Host $sofar -foreground $color -nonewline }
            $color = $foregrnd
            $sofar = ""
        }elseif ($allColors -contains $arg){
            if ($sofar) { Write-Host $sofar -foreground $color -nonewline }
            $color = $arg.substring(1)
            $sofar = ""
        }else{
            $sofar += "$arg "
            $total += "$arg "
        }
    }
    Write-Host $sofar -foreground $color -nonewline:$nonewline
}

Function ResetGUI {
    $objForm.Close()
    $objForm.Dispose()
    CreateGUI
}

Function IsThereText{
  if ($objTextBox.Text.Length -ne 0){
    $objProcessButton.Enabled = $true
  }else{
    $objProcessButton.Enabled = $false
  }
}

Function Relocate {#--------------------[ Assure things run from C:\scripts ]----------------------
  If ($Relocate = False){Break}
  If (!($ScriptHomeDir -eq $TargetPath)){                                                #--[ Paths are NOT same ]--
    If (!(Test-Path -path $TargetPath)){New-Item $TargetPath -type directory}        #--[ Does target path exist?  If not, create it ]--
    If (!(Test-Path "$TargetPath\ScriptFullName" -pathType Leaf )){Copy-Item $ScriptFullPath $TargetPath\$ScriptFullName} #--[ Is the script there?  If not, copy it there ]--

    If (!(Test-Path -path "$Profile\Desktop\$ScriptShortName.lnk")){
    $WshShell = New-Object -comObject WScript.Shell                                    #--[ Place a shortcut on the desktop of current user
    $Shortcut = $WshShell.CreateShortcut("$Profile\Desktop\$ScriptShortName.lnk")
    $Shortcut.TargetPath = 'powershell.exe'
    $Shortcut.Arguments = "-WindowStyle Hidden –Noninteractive -NoLogo -Command `"$TargetPath\$ScriptFullName`""
    $Shortcut.Save()
    If ($Debug1){If (!(Test-Path -path "$Profile\Desktop\$ScriptShortName.lnk")){write-host "FAILED to create icon"}}
    }

    iex -command "$Profile\Desktop\$ScriptShortName.lnk"                                 #--[ re-invoke the script from the new location ]--
    #Remove-Item $MyINvocation.InvocationName                                          #--[ Delete the old copy of this script ]--
    EXIT                                                                              #--[ Force termination ]--
  }Else{                                                                                #--[ Paths ARE good, script exists, OK to execute ]--
    #if (!(Test-Path "$ScriptPath\ThisScriptName"))                                   #--[ an optional additional check ]--
  }
}

Function CreateGUI{
#--------------------------[ Create The GUI ]-----------------------------------
[void] [reflection.assembly]::loadwithpartialname("System.Windows.Forms")
[void] [reflection.assembly]::loadwithpartialname("System.Drawing")
[int]$Width = (Get-WmiObject -Class Win32_DesktopMonitor | Select-Object ScreenWidth,ScreenHeight).ScreenWidth
[int]$FormWidth = 350
[int]$FormHeight = 170
[int]$Center = ($Width / 2)
[int]$ButtonLeft = 55
[int]$ButtonTop = 99

#--[ Create Form ]--
If ($Debug1){Write-Host "Creating form"}
$objForm = new-object System.Windows.Forms.form
$objForm.Text = "User Locatinator.  v$CurrentVersion"
$objForm.size = new-object System.Drawing.Size($FormWidth,$FormHeight)
$objForm.minimumSize = New-Object System.Drawing.Size($FormWidth,$FormHeight)
$objForm.maximumSize = New-Object System.Drawing.Size($FormWidth,$FormHeight)
$objForm.StartPosition = "CenterScreen"
$objForm.KeyPreview = $true
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter"){$TargetUser=$objTextBox.Text;$objTextBox.Text = "           ......  Working  ......";$objTextBox.Refresh();LocateUser $TargetUser}})
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape"){$objForm.Close()}})

#--[ Add Form Lable ]--
If ($Debug1){Write-Host "Creating label 1"}
$objFormLabelBox = new-object System.Windows.Forms.Label
$objFormLabelBox.Font = new-object System.Drawing.Font("New Times Roman",9,[System.Drawing.FontStyle]::Bold)
$objFormLabelBox.Location = new-object System.Drawing.Size(3,5)
$objFormLabelBox.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
$objFormLabelBox.size = new-object System.Drawing.Size(325,20)
$objFormLabelBox.Text = "This tool will locate the PC a user is logged on to:"
$objForm.Controls.Add($objFormLabelBox)

#--[ Add Form Lable ]--
If ($Debug1){Write-Host "Creating label 2"}
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Point(25,25)
$objLabel.Size = New-Object System.Drawing.Size(280,20)
$objLabel.Text = "Enter the First && Last Name, or Logon ID of the user:"
$objLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
$objForm.Controls.Add($objLabel)

#--[ Add Text Input Box ]--
If ($Debug1){Write-Host "Creating input box"}
$objTextBox = New-Object System.Windows.Forms.TextBox
$objTextBox.Location = New-Object System.Drawing.Size(78,58)
$objTextBox.Size = New-Object System.Drawing.Size(175,20)
$objTextBox.TabIndex = 0
$objTextBox.add_TextChanged({IsThereText})
$objForm.Controls.Add($objTextBox)

#--[ Add LOCATE Button ]--
If ($Debug1){Write-Host "Adding LOCATE button"}
$objProcessButton = new-object System.Windows.Forms.Button
$objProcessButton.Location = new-object System.Drawing.Size($ButtonLeft,$ButtonTop)
$objProcessButton.Size = new-object System.Drawing.Size(100,25)
$objProcessButton.Enabled = $false
$objProcessButton.Text = "Locate The User"
$objProcessButton.tabindex = 1
$objForm.Controls.Add($objProcessButton)
$objProcessButton.Add_Click({$TargetUser=$objTextBox.Text;$objTextBox.Text = "             ......  Working  ......";$objTextBox.Refresh();LocateUser $TargetUser})

#--[ Add STOP Button ]--
If ($Debug1){Write-Host "Adding STOP button"}
$objCloseButton = new-object System.Windows.Forms.Button
$objCloseButton.Location = new-object System.Drawing.Size(($ButtonLeft+125),$ButtonTop)
$objCloseButton.Size = new-object System.Drawing.Size(100,25)
$objCloseButton.Text = "Cancel/Close"
$objCloseButton.Add_Click({$objForm.close()})
$objCloseButton.tabindex = 2
$objForm.Controls.Add($objCloseButton)

#--[ Open Form ]--
If ($Debug1){Write-Host "Opening form"}
$objForm.topmost = $true
$objForm.Add_Shown({$objForm.Activate();$objTextBox.Focus})
[void]$objForm.ShowDialog()
}

CreateGui

Thursday, April 10, 2014

Understanding Microsoft Volume Licensing

Throughout my career I've battled with Microsoft licensing as have many of you.  I've heard lot's of complaints about it from almost everyone I've ever worked with.  It's extremely confusing, extremely complex, and it's one of those evils that we have to live with, sort of like toe fungus.  It just never goes away. I've worked with volume licensing and KMS (Key Management Server) for years.  I've never fully understood how it worked, it just sort of did. It's taken me years to truly understand this thing and now that I have I want to document it for anyone else struggling to understand it.  This is for my own reference if for no one else.
I always wondered about that warning (see image below) that you got when adding what I always thought was a valid VL key to a server was.  Now I finally get it, and all the servers I've built over the years with a CVLK key that were never intended to be licensing servers.  Thankfully almost all of those were development servers that would be deleted by now.
I've included links to Microsoft docs that refer to where I got the information.  Its a bit spotty in that it references a lot of different versions of Windows but it all works the same. 

Here is the kindergarten version of how it all works:
Computers that are running volume licensing editions of Windows 8.1, Windows Server 2012 R2, Windows 8, Windows Server 2012, Windows 7, Windows Server 2008 R2, Windows Vista, and Windows Server 2008 are, by default, KMS clients with no additional configuration needed.

If you are converting a computer from a KMS host, MAK, or retail edition of Windows to a KMS client, install the applicable setup key (GVLK) from the tables at http://technet.microsoft.com/library/jj612867.aspx.  All GVLK keys are blocked on the Internet based Microsoft clearinghouse and therefore cannot be used to activate any systems except in a KMS environment.

To use the keys listed there (which are GVLKs), you must have a KMS host running in your deployment first.

By default, Windows and Office volume license editions install a Generic Volume License Key (GVLK). The GVLK enables Windows and Office to automatically discover and activate against your KMS host or Active Directory infrastructure.

What this tells me is that when installing Windows via Volume Licensed DVD media the installation should by default (automatically) include the GVLK license that is used within a Volume Licensed environment.  In theory you should not need to push these out to client machines, it should occur automatically.  The GVLK key must be explicitly pushed to the client if it is being converted from MAK or Retail to VL.
By default you shouldn’t need to do anything assuming you have a properly licensed and function KMS server in your environment.  New clients should automatically register and activate as long as they can detect the DNS record of a KMS server.  At the same time pushing the GVLK out to volume clients causes no issues since that's they key they "should" have anyway.

Proof here:
By default, the Windows 7 and Windows Server 2008 R2 and later operating systems use KMS for activation. In volume installations, the setup key is installed by default, which makes the system a KMS client. If you are converting a computer from a KMS host, MAK, or retail edition of Windows to a KMS client, install the applicable setup key (GVLK) from Appendix A: KMS Client Setup Keys by using slmgr /ipk <setup key>.

Volume Licensing Overview:

Volume Activation is a product activation technology used to activate Windows Vista, Windows Server 2008, Windows Server 2008 R2, Windows 7, Office 2010, Windows 8.1, Windows Server 2012 R2, Windows Server 2012 R2 for Embedded Systems, and Office 2013. It enables Volume Licensing customers to automate the activation process in a way that is transparent to end users. Volume Activation applies to systems that are covered under a Volume Licensing program and is used strictly as a tool for activation; it is in no way tied to license invoicing or billing.

Volume Activation provides two different models for completing volume activations: Key Management Service (KMS) and Multiple Activation Key (MAK). KMS allows organizations to activate systems within their own network. MAK activates systems on a one-time basis, using Microsoft’s hosted activation services. Customers can use either or both activation methods in their environment. The type of key entered in the product determines the activation method. There is a third model for completing volume activation called Active Directory-Based activation.
Volume License Keys (VLK), including MAK and KMS, are issued to you under a specific license agreement and enable your organization to use the software that you have licensed.
VLKs can be used only with Volume Licensing products; they cannot be used with retail software or software that is preinstalled on a new computer (original equipment manufacturer, or OEM, products).
If your organization has fewer than 50 PCs, the best option is to use Multiple Activation Keys (MAK) with Volume Activation Management Tool (VAMT).


KMS activation requires TCP/IP connectivity. By default, KMS hosts and clients use DNS to publish and find the KMS service.

KMS activations are valid for 180 days. This is called the activation validity interval. To remain activated, KMS clients must renew their activation by connecting to the KMS host at least once every 180 days. By default, KMS client computers attempt to renew their activation every seven days

The KMS service uses service (SRV) resource records (RR) in DNS to store and communicate the locations of KMS hosts. KMS hosts use DNS dynamic update protocol, if available, to publish the KMS SRV RRs.

By default, KMS clients query DNS for KMS service information. The first time a KMS client queries DNS for KMS service information, it randomly chooses a KMS host from the list of SRV RRs that DNS returns.

By default, client computers connect to the KMS host for activation by using anonymous RPCs through TCP port 1688

KMS hosts on the network need to install a KMS key, and then be activated with Microsoft. Installation of a KMS key enables the Key Management Service on the KMS host. After installing the KMS key, complete the activation of the KMS host by telephone or online. Beyond this initial activation, a KMS host does not communicate any information to Microsoft.
KMS keys are only installed on KMS hosts, never on individual KMS clients. Windows 7 and Windows Server 2008 R2 have safeguards to help prevent inadvertently installing KMS keys on KMS client computers. Any time users try to install a KMS key, they see the warning shown in Figure 1.



Each KMS key can be installed on up to six KMS hosts, which can be physical computers or virtual machines. After activating a KMS host, the same host can be reactivated up to nine more times with the same key.

KMS hosts that are running Windows Server 2003, Windows Vista, or Windows Server 2008 can be configured to support KMS clients that are running Windows 7 and Windows Server 2008 R2. For Windows Vista and Windows Server 2008, it is necessary to update the KMS host with a package with files that support the expanded KMS client.  Once the package is installed on the KMS host, a KMS key that is designed to support Windows 7 and Windows Server 2008 R2 can be installed and activated as described earlier in this guide.

By default, computers that are running Volume License editions of Windows Vista, Windows 7, Windows Server 2008, and Windows Server 2008 R2 are KMS clients, and no additional configuration is needed. KMS clients can locate a KMS host automatically by querying DNS for SRV RRs that publish the KMS service.

A MAK is used for one-time activation with Microsoft’s hosted activation services. Each MAK has a predetermined number of allowed activations; this number is based on Volume Licensing agreements and does not match the organization’s exact license count. Each activation using a MAK with Microsoft’s hosted activation service counts toward the activation limit.

Included in the Windows Automated Installation Kit (Windows AIK), VAMT is a stand-alone application that collects activation requests from several computers, and then sends them to Microsoft in bulk. VAMT allows IT pros to specify a group of computers to activate using AD DS, workgroup names, IP addresses, or computer names. After receiving the activation confirmation codes, VAMT distributes them to the computers that requested activation. Because VAMT also stores these confirmation codes locally, it can reactivate a previously activated computer after it is reimaged without contacting Microsoft. Additionally, VAMT can be used to easily transition computers between MAK and KMS activation methods.


Software License Manager, sometimes referred to as SL Manager (Slmgr.vbs), is a script used to configure and retrieve Volume Activation information. The script can be run locally on the target computer or remotely from another computer, but it should be run from an elevated command prompt. If a standard user runs Slmgr.vbs, some license data may be missing or incorrect, and many operations are prohibited.
Slmgr.vbs can use Wscript.exe or Cscript.exe, and administrators can specify which script engine to use. If no script engine is specified, Slmgr.vbs runs using the default script engine, wscript.exe.

slmgr.vbs /dli    Retrieves the current KMS activation count from the KMS host. 
If no arguments are included the script will display 5 pages of available options. 
Hopefully that explanation is pretty clear.  If not let me know and I can try to edit it some more.