Windows Run Once Script for VSphere Deployment

I wrote this script as a framework that I could expand upon in the future. I prefer to have everything I do be built upon a solid implementation that I can grow as I need new features. I think we can agree that doing things this way allows for more flexibility in the future and more consistency as well.

This process consists of 3 parts, an XML configuration file, the PoSH script that executes the process and a BAT file for triggering the PoSH script to bypass local execution policies.

I will start with the BAT file, it is really self explanatory, it triggers the RunOnceDeployment script:

	PowerShell.exe -ExecutionPolicy UnRestricted -File ./RunOnceDeployment.ps1

Next is the Settings XML file file, it needs to be placed in the same folder as the rest of the scripts.

	<?xml version="1.0"?>
	<Settings>
		<Notification>
			<Email
				SMTP='email.server.com'
				From='#HostName#@Something.com'
				To='jeremy.tirrell@Something.com'
				Subject='Deployment Notification for #HostName#'
			/>
		</Notification>
		<Registry>
			<Entry 
				Location='HKLM:SOFTWARE\Some\Key\'
				Key='FromAddress'
				Type='String'
				Value='#HostName#@Something.com'
			/>
			<Entry 
				Location='HKLM:SOFTWARE\Some\Other\Key'
				Key='ServiceBaseAddress'
				Type='String'
				Value='https://#HostName#:80/api/'
			/>	
		</Registry>
		<Services>
			<Service
				ServiceName='SomeService'
				State='Automatic'
				Status='Start'
			/>
			<Service
				ServiceName='SomeOtherService'
				State='Automatic'
				Status='Start'
			/>
		</Services>
	</Settings>

The registry section of the XML is used to write and or replace registry keys. The Services section is used to start up services on the guest after the registry keys have been written. Finally the Notification section defines a email host and an email address to deliver notifications to.

Now for the RunOnceDeployment.ps1 script:

    $myDir = Split-Path -Parent $MyInvocation.MyCommand.Path    # Get execution directory
    $myDir
    $InLineVar = @{}
    $InLineVar.set_item("#HostName#",$env:computername)         # Get computer hostname
    #InLineVar.HostName['var'] = '#HostName#'                   # Set var to replace

    [string]$StrConfig = Get-Content "$myDir\Settings.xml"      # Get deployment settings

    foreach ($Var in $InLineVar.Keys){                          # Perform variable replacement
        $StrConfig = $StrConfig -replace $Var, $InLineVar.Item($Var)
    }

    #$StrConfig = $StrConfig -replace '#HostName#', $hostname   # Replace Variables
    [xml]$ConfigFile = $StrConfig                               # Populate XML Object        

    $Body = "Deployment log for $env:computername :`r`n"
    $Body += "Apply Registry Keys:`r`n"                            
    $nodes = $ConfigFile.SelectNodes("//*[@Key]")               # Get all registry keys and apply them to the registry
    foreach ($node in $nodes){
        try {
            $Body += "Set-ItemProperty -Path " + $node.attributes['Location'].value + " -Name " + $node.attributes['Key'].value +" -Value " +$node.attributes['Value'].value +":`r`n"
            Set-ItemProperty -Path $node.attributes['Location'].value -Name $node.attributes['Key'].value -Value $node.attributes['Value'].value
        } Catch {
            $Body += $error[0].ToString() + $error[0].InvocationInfo.PositionMessage +"`r`n"
        }
    }

    $Body += "Starting and enabling services:`r`n"
    $nodes = $ConfigFile.SelectNodes("//*[@ServiceName]")               # Get all Services
    foreach ($node in $nodes){
        try {
            $Body += "Set-Service " + $node.attributes['ServiceName'].value + " -StartupType" + $node.attributes['State'].value + ":`r`n"
            Set-Service $node.attributes['ServiceName'].value -StartupType $node.attributes['State'].value
        } Catch {
            $Body += $error[0].ToString() + $error[0].InvocationInfo.PositionMessage +"`r`n"
        }
        try {
            $Body += "Start-Service " + $node.attributes['ServiceName'].value + ":`r`n"
            Start-Service $node.attributes['ServiceName'].value
        } Catch {
            $Body += $error[0].ToString() + $error[0].InvocationInfo.PositionMessage +"`r`n"
        }    
    }

    $smtpsettings = @{                                          # Setup email notification
        To = $ConfigFile.Settings.Notification.Email.To
        From = $ConfigFile.Settings.Notification.Email.From
        Subject = $ConfigFile.Settings.Notification.Email.Subject
        SmtpServer = $ConfigFile.Settings.Notification.Email.SMTP
    }
    send-mailmessage @smtpsettings -Body $Body                  # send email
    shutdown.exe /L

Jeremy Tirrell

Read more posts by this author.