Create your own Wifi-hotspot Windows 7 / Windows 8

The topic of making your Windows box a wireless AP, and then sharing internet connection with your wireless devices connected, is not something new, but I’ve never seen anyone wrap Powershell around it. Also this script is designed to work on Windows 7, will work under Windows 8, but with windows 8 and Powershell v. 3.0 some parts will be easier to script. There are 3 parts to creating your personal Wifi-Hotspot:

First allow Windows to control the power state of your Wireless Card. You can either do this from the GUI, or if you’re a geek, you might be looking to do this via Powershell, which is what I’ve done.

Second Enable the HostedNetwork feature available in Windows 7. Again, the technical bits of how this works, and what hosted network can do, is available from Microsoft, here. Good part about the hosted network is that is comes with its own DHCP, so Internet Access will pretty much work out of the box.

Finally Enable Internet Connection Sharing (ICS) – as much as I would like to automate this in Powershell, this just isn’t possible in Windows 7 (I’ll dig inside Windows 8, see if it can be done there). To enable ICS,  follow these Instructions from Microsoft.

The Script

To wrap Steps 1 and 2 up I’ve written a Powershell script that will enable what is needed automatically (so you still have to enable ICS by hand, but that’s easy). Click the link to download Enable-Wifi-HotSpot. You must run this script from an elevated powershell console, it won’t fully work unless you do.

Read on to get some learning points on how I did this.

CAUTION: If you just run the script out of the box, please read the instructions it spits out (it has some commands to temporarily disable your Wifi, so if you are on a wifi only connection you will get disconnected)

Learning Points

First step says….

Enable_TurnOff

I wanted to tick the “Allow the computer to turn off this device to save power” check box programatically. This tick box corresponds to the following registry key:

HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}\<ID>\PnpCapabilities

<ID> is a device ID given by Windows to the network adapter when it is installed. When the tick box is checked the first bit of the DWORD value of PNPCapabilities is 0. If the tick box is unchecked the first bit becomes 8.

PNP-capabilities

Also this value is not accepted automatically by the OS, after changing it, you have to reboot (so the Internet says)…but I just went with disabling and enabling the WLAN Adapter and it worked for me. I figured if changing the setting works from the Windows GUI with no reboot there was a way around rebooting.

So our first order of business is to find that <ID> parameter that maps each Wifi adapter to the registry keys. I had the script find all possible Wifi adapters on the system:

$WifiAdapters = Get-WmiObject -Namespace root\wmi -Class MSNdis_PhysicalMediumType -Filter `
 "(NdisPhysicalMediumType=1 OR NdisPhysicalMediumType=8 OR NdisPhysicalMediumType=9) AND NOT InstanceName LIKE '%virtual%'"

I also included integer values for NDISSPhysicalMediumType are included at the top of the script. For reference they can be obtained from 2 places:

  • Windows SDK or WDK (more info here).
  • You can cheat a little and run this command on a Windows 8/ Windows 2012
(Get-NetAdapter | Get-Member PhysicalMediaType).Definition

Once  we have the list of all Wifi adapters, we take each adapter and see if its configuration is OK and it is not disabled. I’m using a filter on the ConfigManagerErrorCode property.  The possible values for this property can be found here.

$PhysicalAdapter = Get-WmiObject -Class Win32_NetworkAdapter -Filter "Name='$($WifiAdapter.InstanceName)'" -Property * |`
 ? {$_.ConfigManagerErrorCode -eq 0 -and $_.ConfigManagerErrorCode -ne 22}

The ID parameter we are looking for is stored in “$PhysicalAdapter.DeviceID” but unfortunately it is not stored in the format we need, (in my case DeviceID = 15, and I needed to transform into 0015). I did it with this line:

$AdapterDeviceNumber = $("{0:D4}" -f [int]$($PhysicalAdapter.DeviceID))

From here on, things get a little simpler. once you get the registry key, I just check if the last HEX digit is 0 or 8.

$PnPCapabilitiesValue = (Get-ItemProperty -Path $KeyPath).PnPCapabilities
 #convert decimal string to HEX to compare first bit
 $PNPCapHEX = [convert]::tostring($PnPCapabilitiesValue,16)

I compare the PNPCapHEX value to see what the first digit is,and decide to just do a “disable/enable” of the wifi adapter, or change the value and then “disable/enable”. Disabling the NIC can be done easily once you have the network adapter object.

$PhysicalAdapter.Disable() | out-null
 $PhysicalAdapter.Enable() | out-null

Note that the commands above return no output. If you take out the out-null, you should see return value = 0. If you get “return value = 5” that is an access denied, and it means you didn’t run the script from an elevated prompt.

Now the registry settings are done, all that is left is to build the netsh command to enable the hosted network. What is “of interest, in this section” is how we read out the wifi password ( I wanted the script to be a little secure, and then how we pass the wifi password to the netsh (that involves converting from secure string to plaintext). For this last conversion I used the function described here.

#now that WiFi adapter is configured, let's add our hotspot
$WifiPassSec = Read-host -Prompt "Enter password for your Wifi, must be at least 8 chars long, complex" -AsSecureString
#enable hosted network
$WifiPass = ConvertFrom-SecureToPlain $WifiPassSec
$SetupHN = "netsh wlan set hostednetwork mode=allow ssid=Rivnet-Wifi key=`"$WifiPass`" keyUsage=persistent`nnetsh wlan start hostednetwork"
Invoke-expression $SetupHN
$SetupHN = $null
$WifiPass = $null

So now you should be all set, just connect your devices to the Wifi and enjoy Internet access via your laptop. Finally, you might want to turn off the hosted network at some time. To do this, run this command:

netsh wlan stop hostednetwork

netsh wlan set hostednetwork mode=disallow

Hopefully this will  help someone out there, looking for a scripted way to do this. For me it was quite a learning journey, since I got to dig inside windows’s internals while scripting this.

How to use KMS server across Active Directory Forests

Recently I made a slight career change and also with it came a small challenge. We were given 2 Active Directory Forests, one was actively being used and the other one had very few users, but was going to get much larger very quick. People were also deploying Windows 7, Windows 2008 R2 and Office 2010. All of these products canuse KMS license keys, which basically means you have one Key Management Service Server in your organization to which all Windows and other MS products refer to for validating their license periodically. That server must have a valid Windows License, which gets activated to the Internet, then you just enable that server as a KMS host Server. Enough with the background, you can read more about deploying a KMS server here.

Now back to the problem at hand. Since the 2 organizations to whom the 2 forests belonged to had pretty loose security requirements, we wanted to save us the hassle of creating and managing a second KMS server, and just using the KMS server we had available. KMS is also not so restrictive when it comes to accepting license validation requests.

KMS clients have 2 ways in which they determine where the KMS service is located (i will use contoso.com as the “main” forest for this example):

  • Specify it manually using a built in windows command line script. For example to specify the kms.contoso.com server for a machine just run from administrator command prompt this command:
cscript %windir%\system32\slmgr.vbs /skms kms.contoso.com:1688
  • Windows uses DNS to determine the KMS servers (pretty much like Windows does to determine which servers offer AD Authentication). When KMS host is installed in creates an SRV record in the DNS in _.tcp.contoso.com. This record looks like this:

ServiceName: _vlmcs

Port: 1688 (default)

Host offering the service: kms.contoso.com

The Final srv record looks like this: _vlmcs._tcp.contoso.com

As you can see there is not so much rocket science in the way a KMS host is published in DNS. Also there is no requirement that the computer trying to validate a license against KMS be joined to a domain. All the computer needs does is a srv DNS query to determine where the KMS licensing host is. Based on this information it talks to KMS and validates the licenses.

So to make sure computers in forest rivnet.org, for example, can find KMS in DNS do following:

1. Create a new A Record for the IP address of the KMS server kms.contoso.com, in rivnet.org DNS, for example kms.rivnet.org

2. Create a new SRV Type record in _tcp.rivnet.org DNS, with following details

ServiceName: _vlmcs

Port: 1688 (default)

Host offering the service: kms.rivnet.org

The Final srv record looks like this: _vlmcs._tcp.rivnet.org

You can do all this by using dnscmd (available in Windows7/2008) run this command:

dnscmd <DNSServerName> /RecordAdd <ZoneName> _vlmcs._tcp SRV 0 100 1688 <HOST-Offering-Service>

3. Test from a client computer that the SRV record is available in DNS, by running this on a command prompt:

nslookup -type=srv _vlmcs._tcp.rivnet.org

You should get an output that points to the DNS record you created in step 1.

4. Test the client computer can validate his license to the KMS host by running this command from an elevated command prompt:

cscript %windir%\system32\slmgr.vbs /ato

cscript %windir%\system32\slmgr.vbs /dli

There should be a line like this:

KMS machine name from DNS: kms.rivnet.org:1688

And that’s it with using KMS from any other forest in your own AD. In short:

1. Add Host record for KMS host

2. Add SRV record for KMS host

3. Attempt activation, verify activation was done using KMS host.

Log Battery and Power Levels using Powershell

This is a let’s say lighter post, I came up while trying to compare battery life of my laptop and some buddies of mine. I wanted to know, how fast my battery depleted using different settings, use profile and power saving modes. Then I did some digging around Microsoft’s MSDN site, and I found some interesting WMI classes, that apparently provide a lot of “power related data”. I also wanted to have a way to log this data, and that’s how I ended up learning how to create a new event-log file and write data to it to use that as a log. So this is what I will try to show: get power related data and write it to the Event-Log.

“Energy” Related WMI Classes

Here are a few interesting classes I stumbled upon. Some of them are only available under Windows7 probably also Vista, but I’m not sure.

  • WmiMonitorBrightness – gives information about monitor brightness. For example these line give the max. “value” and current value of brightness
$MaxBrightness = get-wmiobject -class WmiMonitorBrightness -Namespace root/wmi).level | measure-object -Maximum).maximum
$CrtBrightness = "{0:P0}" -f ((get-wmiobject -class WmiMonitorBrightness -Namespace root/wmi).CurrentBrightness/$MaxBrightness)
  • Win32_PowerPlan – provides information and identifiers about the powerplans defined. In this class ALL powerplans are defined, and just the active plan has an “IsActive” flag attached it, here’s how to get it:
$powerplan = (Get-WmiObject -Class win32_powerplan -Namespace 'root/cimv2/power' | where {$_.IsActive -eq $true}).ElementName
  • Win32_Processor – gets information about the CPU (I was interested in the CPU load for statistical purposes). This one was pretty easy to find, the value was written in plain sight. Take a look:
$cpu = (Get-WmiObject Win32_Processor).LoadPercentage
  • Win32_Battery – Provides information about the battery itself (estimated time, remaining load, power status). Running “Get-WmiObject -Class Win32_Battery | gm” take a closer look at these members:
    • BatteryStatus – this will toggle between ‘1’ meaning on Battery and ‘2’ meaning on AC Power
    • EstimatedRuntime – this will be the number of minutes running on battery, as the OS estimates it, and if you get a very high value (tens of thousands) when you plug the AC Power, it means the battery is charging
    • EstimatedChargeRemaining – percentage-wise representation of battery charge remaining

Powershell + Event-Log “101”

I used this battery and power experiment to learn more about working and writing data to the Event-Log. I wanted to create a new “Event-Log” in Windows (windows 7 as you probably know allows for a lot of application logs) and then write events to it. Then at any point you can export the Event-log to csv. The following creates an Event-Log, with the name “BatteryMonitor” from the Information category (for my uses “Source” was not needed but it is a required parameter:

New-EventLog -Source BattMon -LogName BatteryMonitor -CategoryResourceFile Information

You can also check if an Event-log is created exists you can use this scriptlet (the answer lies in WMI this time, I didn’t find a cmdlet that does it faster):

(get-wmiobject -class "Win32_NTEventlogFile" | where {$_.LogFileName -like 'BatteryMonitor'} | measure-object ).count -eq '1'

Finally here’s how to write to the event-log, a new event. This bit I used in a script to mark the execution of the script in the event-log:

Write-EventLog -LogName BatteryMonitor -Source BattMon -EventID 65533 -Message 'Starting new Execution of BatteryCharge Monitor Script. The script will pump here CSV values. Values are listed in this order, as CSV: PowerPlan,PowStatMsg,ChargeRemMsg,RemTimeMsg,RAM,CPU,CrtBrightness' -EntryType Information -ComputerName $env:computername -ErrorAction:SilentlyContinue

So that is about it, as usual I tried to tie all of these scriptlets into a usable script, you can download it from here.