Wednesday, February 13, 2013

SCVMM Service Template for the first DC in a Forest – the bits

So here goes nothing.  Why not hand it over just to see how this Service Template Export and Import process really works.

In theory, you simply download my Service Template and then Import it.  It will give you the custom application resource folder and the scripts.

You need to bring the Server 2012 evaluation VHD image and place it in your Library.

Be sure to hook your logical network settings, connect to the VHD image.

When you configure your deployment you should have to fill in the domain FQDN, NetBIOS name, and recovery password.  Then click go. 

That is the theory.

Why not give it a go?

I ask one thing though, if you download and try – can you please leave comments and feedback about the experience.

Thanks!

 

BATCH Script Domain Controller Service Template

PowerShell Script Domain Controller Service Template

Tuesday, February 12, 2013

SCVMM Service Template for the first DC in a Forest – part 2

Okay, so I posted the traditional way of handling this, with a BATCH file.

But, in reality all I did was sue a BATCH file to in turn process a PowerShell script.  I considered this silly.  There must be a way to process the PowerShell script without having to use the BATCH script.

I mean, come on.  This is Server 2012 I am using and PowerShell v3.  Yes, I know there are some advanced things that ca be done with BATCH scripting (I have done some in my history), but think out of the box here.

So, I spent bunches of time playing around with this (so you wouldn’t have to (if you stumbled on my post)).

In the end, it wasn’t that difficult, just had to think about things a bit differently.

Oh, and one important thing I left out of my previous post.  Use a local administrator Run As account for adding the local administrator admin credentials to the OS and the same Run As account a second time to process the scripts.

So, here is the script the PowerShell way:

param (
[string]$domainName,
[string]$netbiosName,
[string]$safeModePass
)

# Build a domain controller and the test domain.

# Add the RSAT tools
Add-WindowsFeature RSAT-AD-Tools

# Add the features
Add-WindowsFeature AD-Domain-Services -IncludeAllSubFeature -IncludeManagementTools
Add-WindowsFeature DNS -IncludeAllSubFeature -IncludeManagementTools
Add-WindowsFeature GPMC -IncludeAllSubFeature -IncludeManagementTools

# convert the password to a secure string as required
$secPass = ConvertTo-SecureString -String $safeModePass -AsPlainText -Force

# Create the Forest and Domain
Install-ADDSForest -CreateDnsDelegation:$false -DomainMode Win2012 -DomainName $domainName -DomainNetbiosName $netbiosName -ForestMode Win2012 -InstallDns -Force -SafeModeAdministratorPassword $secPass

I know what you are thinking, that can be shortened.  And my reply; yes, it can.  And you advanced folks, go right ahead.

Now, in the Application Configuration of the Tier in the Service.  Two pre-install scripts.

The first pre-install script is to set script execution to RemoteSigned:

The executable program is: %WINDIR%\System32\WindowsPowerShell\v1.0\PowerShell.exe

And the Parameters are: -command set-executionpolicy remotesigned –force 

(I don’t have a Run As account defined BTW).

image

The second pre-install script is everything above.  But those are included in the Custom Resource Package as a .ps1 file.

The executable program is the same.  The Parameters are different: -file .\DomainController.ps1 @DomainName@ @DomainNetbiosName@ @SafeModeAdministratorPassword@

And the Run As account is my local admin run as account profile.  And the timeout needs to be turned up to about 600 seconds.

image

That is it.  I tried it a few times.  It works. 

Monday, February 11, 2013

SCVMM Service Template for the first DC in a Forest – part 1

I am teaching myself about Service Templates in SCVMM and I always like to take something that I am doing and repurpose it.

Today, I finally had success with taking the PowerShell scripts behind creating a new domain controller in a new forest and automating that using the SCVMM Service Template model.

Oh my (quoting George Takei).  What an interesting adventure this has been.

So, if you are looking for a real world examples of building your own Template, here is it.  No references to Pet Shop here. 

Also, this is not a walkthrough or a point and click post.  I am expecting you to have some familiarity with setting up SCVMM and read all the other blogs about building an SCVMM Service Template.  I am going straight to the peculiar things that you need to be aware of or do to make this happen.

 

First:

I begin with the Server 2012 evaluation VHD (nice and small, prepared with sysprep, no baggage.) and I place that in my SCVMM Library.

Second:

And this is (what I think is) the key – I build some scripts.  The SCVMM Library comes with a few “Application Frameworks” and these are Custom Resources.  Server App-V and WebDeploy are delivered this way.

How do you do this?  Go to your SCVMM Library share, and model the built in ones.

Create a Folder called MyFoo.cr.  Add a file to that called “SCVMMCRTag.cr”  - Don’t copy an existing one, create one.

Add your scripts to this folder.

The Scripts:

Welcome back to the world of BATCH.  If you have been here before, you will recall it fondly.  If you have never been here, you must be relatively young.  There are some excellent web sites with some excellent BATCH command references.  Just search a bit (there aren’t lots, so you find them rather quickly).

Why batch – look at the MSFT included stuff.  Fun with BATCH.  And it ‘just works’.

So here is my trick:  Running PowerShell commands from a BATCH script:

echo Set the Execution Policy to RemotedSigned
%WINDIR%\System32\WindowsPowerShell\v1.0\PowerShell.exe -command set-executionpolicy remotesigned –force

And for this exercise I can install all the required features of Active Directory and DNS:

REM echo Add the RSAT tools
%WINDIR%\System32\WindowsPowerShell\v1.0\PowerShell.exe -command Add-WindowsFeature RSAT-AD-Tools

REM echo Add the Windows Features
%WINDIR%\System32\WindowsPowerShell\v1.0\PowerShell.exe -command Add-WindowsFeature AD-Domain-Services -IncludeAllSubFeature –IncludeManagementTools

%WINDIR%\System32\WindowsPowerShell\v1.0\PowerShell.exe -command Add-WindowsFeature DNS -IncludeAllSubFeature –IncludeManagementTools

%WINDIR%\System32\WindowsPowerShell\v1.0\PowerShell.exe -command Add-WindowsFeature GPMC -IncludeAllSubFeature -IncludeManagementTools

And this is where BATCH fails.  This needs to be totally unattended.  And the cmdlet to create the AD Forest requires a secure string.  I can’t do that in BATCH.  I need to actually use a script (or call the command and add my script as a script block).

This PowerShell script will be in the same folder as the BATCH script (MyFoo.cr).

param (
[string]$domainName,
[string]$netbiosName,
[string]$safeModePass
)

# convert the password to a secure string as required
$secPass = ConvertTo-SecureString -String $safeModePass -AsPlainText -Force

# Create the Forest and Domain
Install-ADDSForest -CreateDnsDelegation:$false -DomainMode Win2012 -DomainName $domainName -DomainNetbiosName $netbiosName -ForestMode Win2012 -InstallDns -Force -SafeModeAdministratorPassword $secPass

Okay, back to the batch script.  You need to properly call the PowerShell script from the BATCH script.

REM Running the PowerShell Domain Controller script
%WINDIR%\System32\WindowsPowerShell\v1.0\PowerShell.exe -file %~dp0\DomainController.ps1 %domName% %domNBios% %recoPass%

But what are those “%” thingies?  Parameters.  I pass parameters to the BATCH script, which are in turn passed to the PowerShell script.  All because of the Secure String.

Now, setting this in the Service Template:

The executable program is cmd.exe (because we are using the command prompt)

And the parameters are: /q /c DCInstall.cmd @DomainName@ @DomainNetbiosName@ @SafeModeAdministratorPassword@

The /q is for ‘quiet’ and the /c defines the ‘command’ – the /c needs to be the last entry of the line.  The @thing@ are the parameters that SCVMM will prompt you for when you deploy.

And the Run As account is my local admin run as account profile.  And the timeout needs to be turned up to about 600 seconds.

And the Script resource package is that folder you created that you put the scripts into.  This gets copied to your VM via BITS (the VM OS does a pull from the Library share BTW), executed, and then deleted on success.

It should look something like this:

image

And the Custom Resource:

image

 

Next post, a twist.  Who wants to call the command prompt and relive the glory days of BATCH all the time?  Why not use only PowerShell??

Friday, February 1, 2013

Reincarnating - Exporting - Importing an Azure Virtual Machine and Service

There are times when I really like the fact that I have to figure stuff out.  And then I have to do it again, and then for a co-worker.  Then before you know it I have to do it in a really serious way.

You could also describe this as Exporting and Importing an entire multi-machine Service.

Why might you want to do this?

  • Moving a Service from one Subscription to another.
  • Moving a Service from one Storage Account to another.
  • Rebuilding a Service in place to force the machines to new hardware.
  • Recover a ‘misbehaving’ machine.
  • Fixing / changing configurations of the Virtual Network or subnet (okay, you would have to modify the XML files, but still).
  • Bringing your machine / service configuration down to earth (without some other tool).

In this case there are the traditional IaaS / Datacenter reasons, of which there a few.  But from the PaaS side there might be lots of reasons.  And coming for a datacenter background, the PaaS thinking around this simply seems foreign and strange, but it really is the ‘cloud’ way of thinking about this – when you are working at an abstracted layer.

In my case; this script began with having to destroy and reincarnate Virtual Machines in Azure (over and over again, for various reasons).  Then I had to move my entire Service to an entirely different Account and thus Subscription.  So it evolved into that.

To complicate things I use Virtual Networks (the Azure software defined networking abstraction) and its Subnets.

Why would you need to reincarnate a Virtual Machine?  (and why are you using this reincarnate word?).

I love the term ‘reincarnate’ when describing changing the configuration of an Azure machine.  Due to the PaaS beginnings it is a fitting description of what actually happens.  The virtual disk stays intact, only the configuration and the deployed machine go away.

For example, you want to move a VM to a different Virtual Network, or Subnet.  You need to destroy it and make it again.  If you want to change the Affinity Group, you need to destroy and make again.  If something on Azure is going all pear-shaped – what do they recommend?  Delete and recreate.

What happens when you reincarnate?  Well, for starters you end up on a different stamp.  Your machine will literally be some place else in the infrastructure.  If you are like me and constantly play with Beta releases, this can be a great thing (or a not so great thing).

Any way, lets get to preserving my script where I can find it in the future when I need it again.  The other thing this will demonstrate is walking through various things in Azure and dependency chains.

 

#Azure cmdlets v0.6.9

Import-Module -Name Azure

# Download the settings file.  This only needs to be done once, until the certificates expire.
# Get-AzurePublishSettingsFile # (this opens an IE window.  Logon.  A certificate is generated in all accounts that your Microsoft Account has access to and embedded in the settings file.  Download the settings file.)

Import-AzurePublishSettingsFile 'C:\Users\Public\Documents\MyServices.publishsettings'

###### start Azure Subscription Selector  ######
Get-AzureSubscription | ft
$sourceSub = [string](Read-Host -Prompt "Type the name of the SOURCE Subscription")
Select-AzureSubscription -SubscriptionName $sourceSub

Get-AzureStorageAccount | ft
$sourceStor = [string](Read-Host -Prompt "Type the name of the SOURCE Storage Account")
$sourceStorKey = Get-AzureStorageKey -StorageAccountName $sourceStor
Set-AzureSubscription -SubscriptionName $sourceSub -CurrentStorageAccount $sourceStor

Get-AzureStorageAccount | ft
$targetStor = [string](Read-Host -Prompt "Type the name of the TARGET Storage Account")
$targetStorKey = Get-AzureStorageKey -StorageAccountName $targetStor

Get-AzureSubscription | ft
$targetSub = [string](Read-Host -Prompt "Type the name of the TARGET Subscription")
Select-AzureSubscription -SubscriptionName $targetSub
###### end Azure Subscription Selector ######

###### start Azure Service VM export ######
Get-AzureService | ft ServiceName, Description, AffinityGroup, URL
$sourceSer = [string](Read-Host -Prompt "Type the name of the SOURCE Service")
Get-AzureVM -ServiceName $sourceSer | ft
$myVm = Read-Host -Prompt "what is the VM name you want to export?  (type '*' for all)"

If ($myVm -eq "*") {
    $path = 'C:\users\public\Downloads\azure\' + $sourceSer
    if ((Test-Path -Path $path -PathType Container) -eq $false) {New-Item -ItemType directory -Path $path}
    Get-AzureVNetConfig -ExportToFile ($path + "\vnetconfig.xml")
     Get-AzureVM -ServiceName $sourceSer | foreach { 
         $vmpath = $path + '\' + $_.Name + '.xml'
         Export-AzureVM -ServiceName $sourceSer -Name $_.Name -Path $vmpath
    }
}
else{
    $path = 'C:\users\public\Downloads\azure\' + $sourceSer 
    if ((Test-Path -Path $path -PathType Container) -eq $false) {New-Item -ItemType directory -Path $path}
    Get-AzureVNetConfig -ExportToFile ($path + "\vnetconfig.xml")
    $vmpath = $path + '\' + $myVm + '.xml'
    Export-AzureVM -ServiceName $sourceSer -Name $myVm -Path $vmpath
}
###### end Azure Service VM export ######

##### Start Service and Deployment deletion #####
$vms = Get-AzureVM -ServiceName $sourceSer

###### start Azure Service Removal ######
If ($myVm -eq "*") {
    foreach($vm in $vms){
        Remove-AzureVM -ServiceName $sourceSer -Name $vm.name
    }
    Start-Sleep 10
    Remove-AzureDeployment -ServiceName $sourceSer -Slot Production -Force
    Start-Sleep 10
    Remove-AzureService -ServiceName $sourceSer -Force
}
###### end Azure Service Removal ######

# Remove-AzureDisk to de-register from the VHD Library and remove the lease before the blobs can be copied to another Storage account
If ($myVm -eq "*") {
    foreach ($vm in $vms) {
        $osDisk = Get-AzureOSDisk -VM $vm
        $dataDisk = Get-AzureDataDisk -VM $vm
        If ($osDisk -ne $null){ Remove-AzureDisk -DiskName $osDisk.DiskName }
        Foreach ($dat in $dataDisk){
            If ($dat -ne $null){ Remove-AzureDisk -DiskName $dat.DiskName }
        }
    }
}

##### end Service and Deployment deletion ######

###### start Azure VM VHD copy ######


At this writing this is all hand wavy because the PowerShell cmdlets don’t support it yet.  I used a third party GUI that did the copying using the Azure Storage API.

 

## Start VHD registration with the VHD Library ##
Add-AzureDisk -OS Windows -MediaLocation
http://mydemo.blob.core.windows.net/vhds/myDemo-Foo-2013-1-8-762B.vhd -DiskName 'myDemo-Foo-0-2013010819464B' -Label Foo
Add-AzureDisk -OS Windows -MediaLocation
http://mydemo.blob.core.windows.net/vhds/MyDemo-Bar-2013-1-8-761B.vhd -DiskName 'myDemo-Bar-0-2013010819450B' -Label Bar
## End VHD registration with the VHD Library ##

###### end Azure VM VHD copy ######

 

##### start create new Service / VMs #####
# Query New Service name, and Subscription name
# $targetSer = $sourceSer  # for reincarnation

$vms = @()
$path = 'C:\users\public\Downloads\azure\' + $targetSer
$vmsToImport = Get-ChildItem $path

foreach ($vm in $vmsToImport) { 
    if ($vm.Name -match 'vnetconfig'){
        [xml]$vNetConfig = Get-Content -Path $vm.FullName  # if modification, it needs to happen, but this can be used later
        Set-AzureVNetConfig -ConfigurationPath $vm.FullName  -ErrorAction Continue  #if adding a VNet to a Subscription with an existing VNet, this will falsely error.  Check after this step to be sure the VNet created before adding the VMs.
    }
    else {
        $vms += Import-AzureVM -Path $vm.FullName
    }
}

# Choose the VNet to properly fill the following.  This is at the Subscription level
Get-AzureVNetSite | ft Name, AddressSpacePrefixes, AffinityGroup, Subnets, DnsServers, InUse
$myVNet = Read-Host "What is the Name of your target Virtual Network for the Domain Controller / DNS Server?"
$myVNet = Get-AzureVNetSite -VNetName $myVNet
# no need to query the subnet because the VM configurations contain that.

If ((Test-AzureName -Service $targetSer) -eq $false){
    # Importing to Existing Service is the assumption
    New-AzureVM -ServiceName $targetSer -VMs $vms -VNetName $myVNet.Name
}
else {
    # Creating new Service is the Assumption
    New-AzureVM -ServiceName $targetSer -VMs $vms -Location "You Should Know" -VNetName $myVNet.Name -AffinityGroup "SorryIDidn’tQuery"
}

##### end create new Service / VMs #####