How to create DHCP scopes with PowerShell

Recently I was approached by a network engineer who was adding scopes to DHCP for our move to Detroit. He had around 80 of them to configure, including all of the different options for the subnets. I did a little research and couldn’t find a native snapin for PowerShell to handle creating the scopes and setting options, but I did know that it could be done via netsh dhcp.

So, I went the second best route and wrote a script that would write a script that would add the scopes for me. The network engineer had a spreadsheet with all of the scopes, their subnet mask, name, and VLANID. Also, for each of the types of scope (data, voice, misc), there were certain DHCP options that had to be set. Those weren’t in the spreadsheet, but he included them in an email and said “for X type, use these settings.”

I can probably improve upon my script by having it look at the spreadsheet for the option values, but this suited our needs and worked pretty well.

The script takes in four parameters.

-csvpath – The path to the CSV file containing the scope info. Since the script uses import-csv, it’s important to make sure that the header columns on the csv are “Subnet” “Mask” “Name” and “VLANID.” MASK should be in a 255.255.255.0 or 255.255.254.0 format (or 253, etc).
-scriptpath – This is the path where you want to output the script file.
-logpath – This is for debugging, but lets you go through in an organized manner to verify the data used to create the netsh script.
-reverseScriptPath – This creates a “delete” script. It’ll go through and remove all fo the subnets you just created…you know, just in case.

It’s worth mentioning that this script is only good for creating scopes on a 16-bit subnet. In this case, we were adding to a Class A network with a 16-bit subnet mask. (10.X.Y.0/24) We were adding a bunch of scopes like 192.168.x.0. It can easily be modified (by you :) to work for different subnets.

Run this from powershell, use the above arguments, and it’ll give you something to work with. It took about an hour to create the 75 scopes.

Feel free to post questions and your own results!

The output will look like this, but with your IPs and data…obviously…

rem *********************************************
rem Creating script for 3rd Floor North Data
netsh dhcp server 192.168.10.10 add scope 192.168.1.1 255.255.255.0 "3rd Floor North Data"
netsh dhcp server 192.168.10.10 scope 192.168.1.1 add iprange 192.168.1.1 192.168.1.254
netsh dhcp server 192.168.10.10 scope 192.168.1.1 add excluderange 192.168.1.1 192.168.1.10
netsh dhcp server 192.168.10.10 scope 192.168.1.1 add excluderange 192.168.1.225 192.168.1.254
netsh dhcp server 192.168.10.10 scope 192.168.1.1 set optionvalue 003 IPADDRESS 192.168.1.1
netsh dhcp server 192.168.10.10 scope 192.168.1.1 set optionvalue 051 DWORD 691200
netsh dhcp server 192.168.10.10 scope 192.168.1.1 set optionvalue 230 STRING "timeserver=192.168.5.10;timezone=5;dst= -1 8 2 2 1 1 10 2"
netsh dhcp server 192.168.10.10 scope 192.168.1.1 set optionvalue 241 IPADDRESS Vendor="My Vendor Name" 192.168.2.10 192.168.2.11
netsh dhcp server 192.168.10.10 scope 192.168.1.1 set state 1
rem Done creating script for 3rd Floor North Data
rem *********************************************

And here’s the script:

###################################################################################################
#
#  PowerShell script to create a batch script that runs netsh dhcp commands to create new scopes
#  - Tom Moser
#  - Last Modified: 18 June 2010
#
#  - Feel free to reuse, modify, and redistribute, but give credit where credit is due
#
#  http://www.phishthis.com
#
###################################################################################################

param($csvpath, $scriptpath, $logpath, $reverseScriptPath)

$networks = import-csv $csvpath

#for each line in the csv - I call the imported object "network." Maybe $scope would be better...meh.
foreach($network in $networks)
{
    if($network.Name.contains("Data") -or $network.Name.contains("Voice") -eq $false -or $network.Name.contains("Misc") -eq $false)
    {
    	$networktype = "Data"
    }

    if($network.Name.contains("Voice"))
    {
    	$networktype = "Voice"
    }

    if($network.Name.contains("Misc"))
    {
    	$networktype = "Misc"
    }

    #enter your DHCP IP here
    $dhcpServerIp = "X.X.X.X"

    #put info in to vars
    $subnet = $network.subnet
    $mask = $network.mask
    $name = $network.Name
    $vlan = $network.VLANID

    #split subnet address in to octets
    $octets = $subnet.split('.')

    #create a base for the current subnet (a string like 192.168.1)
    $subnetBase = "$($octets[0]).$($octets[1]).$($octets[2])"

    #create DHCP range start and end - change these to fit your needs
    $rangeStart = "$($subnetBase).1"
    $rangeEnd = "$($subnetBase).254"

    #create DHCP exclusion range - This example will exclude 192.168.0.1 - 20 and 192.168.220 - 250.
    #change it to fit your needs. You can also get rid of the exclusionend variable and just have one
    #range if that's all you need
    $exclusionStart = "$($subnetBase).1 $($subnetBase).10"
    $exclusionEnd = "$($subnetBase).220 $($subnetBase).250"

    #sets optionvalue 003 - Router. Assumes that you're using X.X.X.1 for your gateway.
    $optionValue003 = "$($subnetBase).1"

    #I use a switch statement here because I had different option values for each type of scope
    #change this as you need. My scope names actually contained the words data, voice, and misc.
    switch($networkType)
    {
        "Data"
	   {
		  #option 006 is DNS servers - You don't need to set this if you're setting it on the server options. You can override, though.
		  $optionValue006 = "X.X.X.X Y.Y.Y.Y Z.Z.Z.Z"
          #option 015 is DNS Domain Name - Also not needed, unless you're going to change it from the server options.
		  $optionValue015 = "domain.forest.root.com"
          #option 044 is WINS/NBNS Servers - again, not needed unless different than the server options.
		  $optionvalue044 = "X.X.X.X Y.Y.Y.Y Z.Z.Z.Z"
          #option 046 is WINS/NBT Node Type...same deal as above.
		  $optionvalue046 = "8"
          #dhcp lease time in seconds - this is 8 days
          $optionvalue051 = 691200
          #option 020 - Timeserver"
		  $optionvalue230 = "timeserver=X.X.X.X;timezone=5;dst= -1 8 2 2 1 1 10 2"
          #this is an example of adding a vendor and IPADDRESS - see the netsh below for details.
		  $optionvalue241 = "X.X.X.X Y.Y.Y.Y"
	   }
	   "Voice"
       {
          #option 006 is DNS servers
		  $optionValue006 = "X.X.X.X Y.Y.Y.Y Z.Z.Z.Z"
          #option 015 is DNS Domain Name
		  $optionValue015 = "domain.forest.root.com"
          #option 044 - WINS/NBNS Servers
		  $optionvalue044 = "X.X.X.X Y.Y.Y.Y Z.Z.Z.Z"
          #option 046 - WINS/NBT Node Type
		  $optionvalue046 = "8"
          #dhcp lease time in seconds - this is 8 days
          $optionvalue051 = 691200
          #option 020 - Timeserver"
		  $optionvalue230 = "timeserver=X.X.X.X;timezone=5;dst= -1 8 2 2 1 1 10 2"
	   }

	   "Misc"
	   {
          #optionValue006 is different for the misc scopes
    	  $optionValue006 = "A.A.A.A B.B.B.B"
          #optionvalue051 is lease time and set to 8 hours instead of days
          $optionvalue051 = 28800
	   }
    }

    ### output log info - this is just to verify that you wrote out the values you expected. Helps with auditing.
    add-content $logpath "`r`n`r`n*********************************************"
    add-content $logpath "Creating scope with the following parameters: "
    add-content $logpath "Scope name: $name"
    add-content $logpath "   Address: $subnet"
    add-content $logpath "      Mask: $mask"
    add-content $logpath "ScopeClass: $networkType"
    add-content $logpath "   VLAN ID: $vlan"
    add-content $logpath "     Range: $rangeStart to $rangeEnd"
    add-content $logpath "Exclusion1: $exclusionStart"
    add-content $logpath "Exclusion2: $exclusionEnd"
    add-content $logpath "       003: $optionValue003"
    add-content $logpath "       006: $optionvalue006"
    add-content $logpath "       051: $optionvalue051"
    if($networkType -ne "Misc")
    {
    	add-content $logpath "       015: $optionvalue015"
    	add-content $logpath "       044: $optionvalue044"
    	add-content $logpath "       046: $optionvalue046"
    	add-content $logpath "       176: $optionvalue176"
    	add-content $logpath "       230: $optionvalue230"
    }

    if($networkType -eq "Data")
    {
    	add-content $logpath "       241: $optionValue241"
    }
    add-content $logpath "`*********************************************"

    #### end logging

    #### create script
    # This is where we actually write out all of the netsh commands
    # you need to run this from your DHCP server.
    add-content $scriptpath "rem *********************************************"
    add-content $scriptpath "rem Creating script for $name"

    #add scope
    add-content $scriptpath "netsh dhcp server $dhcpServerIp add scope $subnet $mask `"$name`""

    #add distribution range
    add-content $scriptpath "netsh dhcp server $dhcpServerIp scope $subnet add iprange $rangeStart $rangeEnd"

    #add exclusion range 1
    add-content $scriptpath "netsh dhcp server $dhcpServerIp scope $subnet add excluderange $exclusionStart"

    #add exclusion range 2 - remove this if you don't have a second exclusion range
    add-content $scriptpath "netsh dhcp server $dhcpServerIp scope $subnet add excluderange $exclusionEnd"

    #set scope option 003 - Router
    add-content $scriptpath "netsh dhcp server $dhcpServerIp scope $subnet set optionvalue 003 IPADDRESS $optionValue003"

    #this sets the different DNS servers for the Misc scopes
    if($networkType -eq "Misc")
    {
        #set scope option 006 - DNS Servers
        add-content $scriptpath "netsh dhcp server $dhcpServerIp scope $subnet set optionvalue 006 IPADDRESS $optionValue006"
    }

    #set dhcp  option 051 - DHCP Lease length
    add-content $scriptpath "netsh dhcp server $dhcpServerIp scope $subnet set optionvalue 051 DWORD $optionvalue051"

    #these are only set on the data or voice scopes - not on misc.
    if($networkType -ne "Misc")
    {
        #the first three are commented because they aren't needed unless you're overriding the DHCP server options.

        #set scope option 015 - domain Name
        #add-content $scriptpath "netsh dhcp server $dhcpServerIp scope $subnet set optionvalue 015 STRING $optionvalue015"

        #set scope option 044 - WINS/NBNS
        #add-content $scriptpath "netsh dhcp server $dhcpServerIp scope $subnet set optionvalue 044 IPADDRESS $optionValue044"

        #set scope option 046 - Wins/NBNS - 0x8
        #add-content $scriptpath "netsh dhcp server $dhcpServerIp scope $subnet set optionvalue 046 BYTE $optionvalue046"

        #set scope option 230 OnTimeClock
        add-content $scriptpath "netsh dhcp server $dhcpServerIp scope $subnet set optionvalue 230 STRING `"$optionvalue230`""
    }

    #again, only for data
    if($networkType -eq "Data")
    {
        #this is an example of how to set IPs and a vendor class on a scope option
        #the syntax is optionvalue XXX IPADDRESS Vendor=vendor name XXX.XXX.XXX.XXX YYY.YYY.YYY.YYY
    	#set scope option 241 Option43
    	add-content $scriptpath "netsh dhcp server $dhcpServerIp scope $subnet set optionvalue 241 IPADDRESS Vendor=`"My Vendor Name`" $optionvalue241"
    }

    #activate scope
    add-content $scriptpath "netsh dhcp server $dhcpServerIp scope $subnet set state 1"

    #this creates the deletion script - just in case you bork a parameter and need to delete and start over
    #you could also use the script to create a fix, but this might be easier
    add-content $reverseScriptPath "netsh dhcp server $dhcpServerIp delete scope $subnet"

    add-content $scriptpath "rem Done creating script for $name"
    add-content $scriptpath "rem *********************************************`r`n"
    #you're done! Depending on the number of scopes you create, it can take some time. The 75 or so scopes I created took around 30 minutes.
}

How to kill all but the newest Excel process with PowerShell

Excel was the example I used, but you could use anything…Somebody on HardForums.com was asking about a way to kill all but the most recently started Excel process. I came up with this:

get-process | where-object { $_.name -eq "excel" } | sort-object -property "Starttime" -descending | select-object -skip 1 | foreach { taskkill /pid $_.id }

If there is just one excel process running, it will leave that process alone, thanks to the -skip 1 in Select-Object.

I can’t imagine this is too useful, but who knows? :)

How to give a user account rights to register its own Service Principal Name (SPN)

I recently had a SQL server where the SQL instance had a different name than the hostname. Not having rights to connect to SQL, I wasn’t aware of that. So, I registered the SPNs as they should have been registered, and it was still falling back to NTLM (see: failing).

SQL Server will register its own SPNs at startup – assuming the service account has rights to set its own SPN. To give the service account rights to self-register SPN (assuming you’re using a domain service account and not Network Service/Local System), you need to grant the service account rights to “Write Public Information” on itself in Active Directory.

1) Launch Active Directory Users and Computers
2) Find your service account and hit the Security tab
3) Select “SELF” in the “Groups or user names” listbox
4) Find “Write public information” in the “Permissions for SELF” listbox and check “Allow”
5) Click OK


After, you’ll need to restart SQL Server for the SPN to register. Use setspn -l domain\account to verify that the account has properly registered.

If you do happen to be using Network Service or Local System, shame on you. That said you’ll just need to verify on the computer account in AD that SELF has “Validated write to service principal name” set to Allow. But, seriously, stop using Network Service or Local System (ESPECIALLY THAT!) and start using a domain account…or at the very least a local account.

Reblog this post [with Zemanta]

How to configure AD, SQL, and IIS for two-hop Kerberos authentication

Recently, some of our developers were writing an app that required impersonation from the web service, as the user, to the database. Admittedly, Kerberos isn’t one of my strong points.

There were two hops here. From the user -> IIS server and from IIS Server -> SQL Server, but the application in IIS would impersonate the user when authenticating with the SQL server.

So, the idea here is that from the user to the IIS server, we know Kerberos will work. The user passes its ticket to the web service. Nothing unusual. From there, the web app, running as a custom app pool ID, needs to pretend (delegate) to be the user when it authenticates to the SQL server.

There are a few requirements.
1) Your application in IIS should be running under a custom identity – domain\MyAppService
2) SQL Server needs to be running under a domain service account – domain\MySQLService
3) IIS needs to use Negotiate instead of NTLM for that application. It should do this by default, then fall back to NTLM. For whatever reason, my app was using NTLM. IIS should also have Windows Authentication enabled.
4) Change your connection string to impersonate the site user

Step 1 – Set the SPN on your app pool ID for the site, for the hostname and FQDN.
setspn -a http/mysite domain\MyAppService
setspn -a http/mysite.domain.com domain\MyAppService

Step 2 – Set the SPN for the SQL service on your SQL service account – assuming you use the default SQL port
setspn -a MSSQLSvc/hostname domain\MySQLService
setspn -a MSSQLSvc/hostname.domain.com domain/MySQLService
setspn -a MSSQLSvc/hostname:1433 domain\MySQLService
setspn -a MSSQLSvc/hostname.domain.com:1433 domain/MySQLService

Restart SQL

Step 3 – In Active Directory Users and Computers, find the service account, click the delegation tab, and trust it for delegation. You can set it for delegation to anywhere, or constrained delegation to the SPNs you’ll set for the SQL service account.

Step 4 – Force your site or application to use Negotiate. This won’t work with NTLM, so we’ll remove it. (Note: This is for IIS7/7.5)
- Find and open your applicationHost.config. It’s probably under c:\windows\system32\inetsrv\config. You can also set this in the system.webServer section of the web.config for the application.

- Scroll to the bottom and above /configuration copy this in:

   <location path="SitePath">
        <system.webServer>
            <security>
                <authentication>
                    <windowsAuthentication>
                        <providers>
                            <add value="Negotiate" />
                            <remove value="NTLM" />
                        </providers>
                    </windowsAuthentication>
                </authentication>
            </security>
        </system.webServer>
    </location>

If you get a 500 error after adding the above XML, it’s probably because Negotiate is already added elsewhere. Just remove the line that says add value=”Negotiate” and leave the remove NTLM line.

Reference: This post was extremely helpful in solving my problem – http://blogs.technet.com/askds/archive/2008/06/13/understanding-kerberos-double-hop.aspx – in the end, I did pretty much everything in that post, and still had the IIS server passing anonymous to SQL, which is what tipped me off that it was using NTLM and not Negotiate.

Reblog this post [with Zemanta]

Windows 7 – How To Link Online IDs

I stumbled upon this today in Windows 7, completely by accident.

Linking your online ID will let you log in to Windows and then log in to any service for which you’ve installed a provider, without being prompted to login again. I associated my Windows Live ID with my home desktop computer login. When I went to Hotmail, all I had to do was click my email address to log in. I went over to MSDN to check my available downloads and simply clicked “Sign in” and was there. Pretty cool feature and a great time saver.

Here’s how to do it:

Click the Windows button.
Type “link online” and you should see “Link Online IDs” at the top of the search. Click it.
Select “Add an Online ID Provider” and select one from the list – at the time I’m writing this, only MS Live is available.

Download the installer, and install. You should see this:
LiveID

Click “Link online ID” and enter your credentials. That’s it! Now head over to your Live/Passport enabled sites and login!

Reblog this post [with Zemanta]