Showing posts with label script. Show all posts
Showing posts with label script. Show all posts

Monday, February 04, 2013

Building A Hyper-V Test Lab on Windows 8 – Part 5 Configuring DC1

Introduction

This the fifth part in a multi-part set of articles on building a test lab with Hyper-V and PowerShell. See the following prior articles in this series.

Configuring the First DC

In the last article, I showed you how to create a domain controller in a new forest by taking a newly installed workgroup server and promoting it to be the first domain controller in a new domain/forest.  Since the DC provides the AD environment for the rest of the VMs in my test lab(s), it has to become the DC before any other VMs are created. Once the server becomes the domain controller, a second script is used to configure the DC. Note that most of the other servers can be configured in just one step (although the jury’s still out that with respect to Lync and Exchange).

In this article I present a second configuration script for the domain controller, snappily named Configure-DC1-2.ps1. This second script just finishes off the configuration and setup of the DC. Once the DC1 VM has been created and promoted to being a domain controller, you use this second script to finish configuration.

From a work flow perspective, once the DC has been created, creating and configuring other new VMs can be done in parallel with configuring DC1 (i.e. running Configure-DC1-2). Also, if you are creating VMs that rely on DHCP, you would need to complete the DCHP configuration before those VMs are created.

In my lab’s case, configuration the first domain controller, DC1, is pretty simple:

  • Set the VM to automatically logon as the domain admin – in lab environments, life really is too short to have to type credentials any more than is absolutely needed. So I set the registry settings to enable auto admin logon
  • Install Key Windows features - I include some simple features, including IIS (which is needed to enable the DC to be a CA).
  • Install and configure basic DHCP - Most of my lab machines have fixed IP addresses, but having a small DHCP block seems a good thing! I allocate 20 IP addresses, but you could change that as needed. I also configure this scope with some basic options (IP, subnet mask, and DNS Server). You could add a default gateway if you want to enable routing via the host.
  • Create a second administrator – I create one extra user, me (TFL), and add this user to domain and enterprise admin groups. I have a further script Configure-ReskitAD.ps1, as part of this series, that adds a richer AD environment in terms of a coupe of OUs, and more users.

Once that has been done, I do two more things in the script (outside the configuration block):

  • Force a reboot of the VM – The very last thing the script block does before returning is to call restart-computer. The reboot is, in effect, asynchronous. Thus after the script exits and control is passed to the main script running on the Hyper-V box which continues while the DC reboots. After the exit, the script restarts DC1.
  • Take a snapshot of the DC. This is useful if I want to do some AD configuration but then back out of that. To cater for the async nature of the reboot (it happens in another process/VM), I use the the parameters ‘–Wait –For PowerShell’ which waits till the system has rebooted, the user has logged on before proceeding to take the actual snapshot. .

Using Remoting to Configure DC1

In the first two scripts that create and start up the VM, the scripts contain a function definition that is then run against the local system, i.e. the host you are using to run Hyper-V. In my case, this was done on Windows 8 on my laptop and one of my Server 2012 boxes. They both run Hyper-V well so testing is easy both at home and on the road. As I noted previously, the remainder of the scripts I use that to setup and configure the domain and servers use remoting. The following pattern if these scripts is as follows:

  • Create a script block, $CONF or similar, containing PowerShell code to perform some configuration on a server. The PowerShell code is intended to run in the target VM.
  • Use Invoke-Command, and the appropriate credentials, to run that script block on a remote server (i.e. one of the Hyper-V VMs you are building/configuring.

This process is flexible and allows me to do things before invoking the script block. For example, to install SQL, Exchange or Lync, I need to have the product CD inserted in the D: drive. For Exchange, I have to load some pre-requisites onto the server, for example bits of IIS, etc. that are not needed for other labs. SO in that case, the configuration script file can create a couple of script blocks to divide up the work. In the longer term I consolidate the multiple script blocks, but that’s work to be done!

Snapshotting VMs

These scripts were designed to support me in writing and developing courseware. Since the development work can be error-prone plus with a need to test, test, and re-test lab instructions, I need to take snapshots before and after key configuration events. So what this script does, at the very end, is to take a VM snapshot and label it as being created by this script. You can, of course, comment out this if you don’t need to have a snapshot!

Using the Scripts

When I am building out a new set of VMs, I open ALL these scripts in the ISE on the Hyper-V host. Whilst I am at home, that means running these via a terminal services window against one of my Hyper-V servers or my laptop. Once I have all the scripts open, I just work through them, tweaking the unattend.xml, building the base disk, building then promoting DC1, finishing off DC1 configuration, configuring a CA, configuring the IIS servers, etc.

Getting the Scripts

I have published the full set of deployment scripts to my web site, at http://www.reskit.net/powershell/vmbuild.zip. Note that some of the scripts in this zip file are very much works in progress that are changing, and hopefully improving, as I publish these articles. I reserve the right to change any of all of them from time to time. I will try to blog any important changes.

I am also publishing the individual scripts over on my PowerShell Scripts Blog:

Recent Script Changes

Since starting this series, I’ve been tidying up the scripts. In some cases, I’ve moved parameters into hash tables to increase readability. The changes to the scripts are now added to the script itself. One key change is that I set Auto-Admin logon for all servers, and force a reboot of the server after configuring it. I’ve also added some judicious Hyper-V check pointing into some of the scripts to simplify both further testing and to suit my courses.

[Later]

In the original post, the reboot and snapshot were not handled in a tidy fashion. I re-coded this logic so that the configuration script block does NOT do the restart. Instead, I let the script block continue, and exit back to the main Configure-DC1-2 script block where I then forcibly reboot the DC, wait till the reboot has completed then take the snapshot. Just a little more elegant and it ensures that the snapshot after the reboot has completed and the auto-admin logon has occurred. This makes reverting back to the snapshot that bit easier.

Future Scripts

The next couple of scripts, which I hope to get documented this week, including building and configuring general purpose servers and creating a Certificate Authority. I also have some utility scripts that I have added and will also be documenting.

Comments

Any comments? I’d love to hear from you – either as comments to this blog post, or via email.

 

Friday, June 03, 2011

Spell Checking Strings in PowerShell scripts

Let’s face it – spelling misteaks suck. Whether in a blog post or in a PowerShell script. Doug Finke has written a nice article on this subject that shows  how to spell your strings in your scripts. He even includes a script to do it! You can Download the PowerShell script  HERE.

Technorati Tags: ,,

Thursday, June 17, 2010

Signing PowerShell Scripts – A Gotcha with ISE!

In some enterprise environments, signing PowerShell scripts and setting an execution policy to only run signed scripts is a useful control mechanism. It can avoid less skilled admins ‘fixing’ a script almost correctly and can avoid untested scripts from running. Of course, the malign admin can still cut/paste the scripts into the command line and do damage – but that same admin can nuke the registry, reformat a volume, etc. Script signing is just another layer of defence.

The Scripting Guys Team (well actually superstar MVP Ragnar Harper) has written a two part blog post on the subject of how to do script signing. Part 1 is a useful tutorial on how to setup your own PKI using Windows Server’s built in Certificate Service feature (AD CS as MSFT call it). With Part 1, you learn how to get your code signing digital certificate.  Part 2 then talks you through how to use that certificate to generate a signed script.

The demo is good and the instructions work well, however there is one small gotcha. If you use PowerShell ISE to edit and save your scripts, the technique shown in Part 2 will fail. Here’s what you will see (from the ISE).

image

As you can see, this results in a rather less than helpful “UnknownError”. Turns out the reason is simple: By default, ISE saves scripts in Unicode BigEndian format – which Set-Authenticode does not cater for. And worse, the ISE Save-As dialog does not give you any option to save in a more friendly encoding (ie ASCII!).

There are three solutions to this:

1. Use Notepad to re-save the file as ASCII, then sign it. This is suboptimal but it works.

2.  You can get ISE to save as Unicode (not BigEndian) using a small script, unfortunately not from the menus. Just run the following bit of code:

$psise.CurrentFile.Save([system.Text.Encoding]::Unicode)

3: You can get ISE to save as always as ANSI. As t add the following bit of code to your PowerShell ISE:

register-objectevent $psise.CurrentPowerShellTab.Files collectionchanged -action {
    # iterate ISEFile objects
    $event.sender | % {
        # set private field which holds default encoding to ASCII
        $_.gettype().getfield("encoding","nonpublic,instance").setvalue($_, [text.encoding]::ascii)
     }
}

Once this fragment is executed, probably by adding it to your Profile, scripts get saved as ANSI and can be signed just fine.

Note in the above, you can both save the current file as ASCII or auto-save as Unicode. Set-AuthenticodeSignature works with both encodings, just not the ISE normal default of Unicode BigEndian (just change ::ASCII to ::UNICODE or vice versa!). Personally, I now save as ASCII. But if you have non-ANSI characters in your scripts, set the default as Unicode!

And a tip of the hat to Oisin Grehan who posted about this on the MVP list and who has posted some of the above code on his blog here.

 

Tuesday, May 19, 2009

2009 Summer Scripting Games

As in years past, Microsoft is hosting the 2009 Summer Scripting Games. This year, the games are co-promoted by The Microsoft Script Guys and PosCode.Org. The games will run from June 15-26 and should be a lot of fun. As happened last year, I’ve been asked to submit a sample script and am hard at work on it already!

If you are a novice, an expert, or anywhere inbetween with scripting (PowerShell or even VB Script) then visit the site, sign-up and take part.

 

Sunday, January 18, 2009

Managing a Music archive with PowerShell

I have a lot of digital music, as many of my friends, and readers of this blog, know. I’ve been slowly getting around to writing some PowerShell scripts to manage this archive. This afternoon, someone asked me how many Jerry Garcia shows I had on my system. I could not recall off the top of my head, so I wrote a short script to do the counting, and published this over on my PowerShell Scripts blog. The Get-CountOfJerryShows.ps1 scritp may not, perhaps, be the most elegant of PowerShell scripts. And I know I could do it in fewer lines. But it works and it’s something I am likely to be able to read and understand in the future, if/when I want to update it.

The answer, thanks to PowerShell: I currently have 734 live Jerry Shows! This is in addition to my 1144 Grateful Dead shows.

The latter is a lot simpler, as I just store all the Grateful Dead Shows in a single folder. To get a count of Dead shows, I just type:

   1: cd m:\gd;(ls | where {$_.Name -match "^gd"}).count
Technorati Tags: ,

Friday, January 02, 2009

Posting PowerShell Scripts – one solution

On my PowerShell Scripts blog, I publish daily, or near daily, PowerShell scripts. I am slowly decorating the MSDN on-line library with PowerShell samples. I am also playing with the latest version of PowerShell V2 (aka CTP3) and the new auto-help feature.

Now that CTP3 is out, I’ve decided to change the way I posts scripts – I’ll use the auto-help approach, and include examples in the posted script. You may have noticed based on recently posted scripts. Well – those of you who look at my script blog have noticed. May have noticed. Or not…

In the past, when I’ve created a script, I use Live Writer to create the blog post. In that post, I copy in the script AND the results (each formatted separately). With V2 CTP3’s auto-help, I’ve decided to decorate each script post with documentation that includes expected output. All of this is neatly embedded into the script as posted, with expected output specified in the .EXAMPLE section in the opening comment block.

With the new auto-help format, I can also document the posted scripts better – a comment made on the blog some whiles back.

I’d be interested in thoughts on this idea. Suggestions naturally welcome.

Friday, December 26, 2008

Enums, Enum values and PowerShell

 

I’ve been reading Jeffrey Snover's discussions on ENUMs over on the PowerShell Team blog (here, and here). I knew most of this stuff and have  been playing with Enums and PowerShell for a while. I’ve created a bunch of sample scripts I’ve uploaded both to the MDSN Wiki and to my PowerShell Scripts blog. This morning, I stumbled upon an interesting use of Enums – parsing strings into Integer values, as shown on the MSDN Library at http://msdn.microsoft.com/en-us/library/system.globalization.numberstyles.aspx.

Effectively want I wanted to do was to convert the following C# code to PowerShell:

  1. // Parse the string, allowing a leading sign, and ignoring leading and trailing white spaces. 
  2. num = "    -45   "; 
  3. val = int.Parse(num, NumberStyles.AllowLeadingSign |  
  4.      NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite); 
  5. Console.WriteLine("'{0}' parsed to an int is '{1}'.", num, val); 

Initially, I could not work out how to use the enums  (In System.Globalization.NumberStyles). Thanks to Shay Levy, it turns out to be remarkably easy (although not quite as succinct as in C'#). Basically, in true PowerShell style, every enum value is just an object. Objects have properties, and one property of an enum value is “value__” as demonstrated here:

PSH [D:\foo]: [System.Globalization.NumberStyles]::AllowLeadingSign
AllowLeadingSign
PSH [D:\foo]: [System.Globalization.NumberStyles]::AllowLeadingSign | gm

   TypeName: System.Globalization.NumberStyles

Name        MemberType   Definition
----        ----------   ----------
CompareTo   Method       System.Int32 CompareTo(Object target)
Equals      Method       System.Boolean Equals(Object obj)
GetHashCode Method       System.Int32 GetHashCode()
GetType     Method       System.Type GetType()
GetTypeCode Method       System.TypeCode GetTypeCode()
ToString    Method       System.String ToString(), System.String ToString(String format, IFormatPr...
value__     Property     System.Int32 value__ {get;set;}
MSDN        ScriptMethod System.Object MSDN();

PSH [D:\foo]: [System.Globalization.NumberStyles]::AllowLeadingSign.value__
4
PSH [D:\foo]:

As you can see from this output, the enum [System.Globalization.NumberStyles]::AllowLeadingSign displays normally the value “AllowLeadingSign” , but if you use the .value__ property on this enum value, you get back a number – in this case a decimal 4 (0x0004). You can then combine these as follows to convert the above C# Code into PowerShell:

  1. $num = " -45 "; 
  2.   $val = [system.int32]::Parse($num, 
  3.                [System.Globalization.NumberStyles]::AllowLeadingSign.value__ + 
  4.                [System.Globalization.NumberStyles]::AllowLeadingWhite.value__ + 
  5.                [System.Globalization.NumberStyles]::AllowTrailingWhite.value__) 
  6. "'{0}' parsed to an int is '{1}'." -f $num, $val 

Thanks to Shay Levy for his post in the PowerShell newsgroup for helping me to work this out. FWIW: you can the results of this up on the MSDN Library and on my PowerShell Scripts blog.

 

Tuesday, July 15, 2008

Finding Your External IP address (with PowerShell)

For many of us, the IP address of our PC is not a publicly routable one.  If I run IPConfig on my workstation, I get something that looks like this:

Ethernet adapter Lan1BuiltIn:

   Connection-specific DNS Suffix  . : foobar.net
   Link-local IPv6 Address . . . . . : fe80::3955:f42b:2d1e:1423%10
   IPv4 Address. . . . . . . . . . . : 10.234.12.123
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 10.234.12.100

But if you were to try to ping me, you’d never get to my system, since I’m behind a firewall. My IP address on the Internet, the publicly routable IP address, is quite different.

There are a lot of ways you can find out what your IP address is – there are a bunch of sites you can use:

No doubt there are a bunch more!

Recently, I’ve seen two other sites used, via PowerShell, to get your IP address. The first is http://checkip.dyndns.org. The nice thing about this site is that it returns very little data, so you can do some ‘screen scraping’ to extract your external IP address. With this site, as shown on Per Ostergaard’s blog, you could do this as follows:

$wc=New-Object net.webclient $wc.downloadstring("http://checkip.dyndns.com") -replace "[^\d\.]"

This is pretty cool – and in discovering this, I discovered the –replace operator that I did not know about before.

The second is also interesting. The source is here.  The script looks like this:

 

## Function to retrieve external IP address. ## the external address is retrieved from the ## title header of the webpage "www.myip.dk" function Get-ExternalIP { $source = "http://www.myip.dk" $client = new-object System.Net.WebClient $webpage = $client.downloadString($source) $lines = $webpage.split("`n") foreach ($line in $lines) { if ($line.contains("</title>")) { $ip = $line.replace(" <title>Your IP address is: ", "").replace("</title>","") } } $obj = New-Object Object $obj | Add-Member Noteproperty externalIP -value $ip $obj }

What this script does is to first call www.myip.dk and return the page. This site is different form the earlier ones in that it puts the IP address into the title bar. The script then parses the returned page, gets the title directive, and pulls the IP address out.

Technorati Tags: ,,