In my Advanced IaaS Talk at Build I showed a demo where you can configure a PowerShell script that can download a zip file with multiple actions (unzip or execute) that gives you similar functionality to a Windows Azure startup task for web and worker roles.
This is a very simple example but it does show some of the capabilities you can do.
The bootstrap.ps1 file below is the main script that is responsible for downloading the bootstrap.zip file from your storage account. You should create a directory called c:\BootStrap and place the script inside and reference it from the startup task on your virtual machine.
In this demonstration I am pulling from a public storage container. If you want to contain anything secure in your bootstrap.zip you should modify the url below to use a shared access signature..
bootstrap.ps1
cls
$rootpath = 'C:\BootStrap\'
$manifest = 'C:\BootStrap\Working\manifest.xml'
$workingdir = 'C:\BootStrap\Working\'
$downloaddir = 'C:\BootStrap\Working\config1.zip'
$packagesource = 'http://YOURSTORAGEACCOUNT.blob.core.windows.net/bootstrap/config1.zip'
function GetPayload()
{
$retries = 5
while($retries -gt 0)
{
CheckFolder $workingdir
try {
$wc = New-Object System.Net.WebClient
$wc.DownloadFile($packagesource, $downloaddir)
break
}
catch [System.Net.WebException] {
# $_ is set to the ErrorRecord of the exception
if ($_.Exception.InnerException) {
$_.Exception.InnerException.Message | Out-File c:\BootStrap\error.txt -Append
} else {
$_.Exception.Message | Out-File c:\BootStrap\error.txt -Append
}
Start-Sleep -Seconds 15
$retries = $retries - 1
}
}
UnzipFileTo $downloaddir $workingdir
}
function BootStrapVM()
{
if((Test-Path HKLM:\Software\VMBootStrap) -eq $true)
{
Write-Host "Already Ran"
return
}
[xml] $manifest = Get-Content $manifest
$counter = 0
$manifest.StartupManifest.Items.Item | foreach {
$action = $_.action
$path = $_."#text"
$target = $_.target
$sourcefullpath = $workingdir + $path
switch($action)
{
"execute" {
Write-Host "Executing command: " $sourcefullpath
ExecuteCommand $sourcefullpath
}
"unzip" {
$sourcefullpath = $workingdir + $path
Write-Host "Unzipping " $sourcefullpath " to " $target
UnzipFileTo $sourcefullpath $target
}
}
}
New-Item -Path HKLM:\Software -Name VMBootStrap –Force | Out-Null
Set-Item -Path HKLM:\Software\VMBootStrap -Value "ran" | Out-Null
}
function ExecuteCommand($commandpath)
{
& $commandpath
}
function UnzipFileTo($sourcepath, $destinationpath)
{
CheckFolder $destinationpath
$shell_app = new-object -com shell.application
$zip_file = $shell_app.namespace($sourcepath)
$destination = $shell_app.namespace($destinationpath)
$destination.Copyhere($zip_file.items(), 16)
}
function CheckFolder($path)
{
if((Test-Path $path) -eq $false)
{
New-Item -ItemType directory -Path $path -Force | Out-Null
}
}
GetPayload
BootStrapVM
Here are two sample configs I put together:
config1.zip Enables Remote PowerShell
http://mwweststorage.blob.core.windows.net/bootstrapblog/config1.zip
config2.zip Installs IIS, WebPI, MySQL and WordPress
http://mwweststorage.blob.core.windows.net/bootstrapblog/config2.zip
The config1.zip file contains a manifest.xml file and all of the entities you need to upload.
Example of the manifest.xml section of the config2.zip
<StartupManifest>
<Items>
<Item action="execute">InstallRoles\ConfigureRoles.ps1</Item>
<Item action="unzip" target="c:\webpi\">WebPI\WebpiCmd.zip</Item>
<Item action="execute">InstallPackages\EnableWebPI.cmd</Item>
</Items>
</StartupManifest>
All that is needed to configure the script to run when your VM boots is to configure a startup task using group policy.
Run: mmc
File -> Add/Remove Snapin
Select -> Group Policy Object Editor (Local Computer)
Expand -> Computer Configuration -> Windows Settings -> Scripts Startup/Shutdown
Once the script is configured you will need to change the execution policy on the virtual machine before you run sysprep and capture on your image otherwise the script will not execute on the next boot.
Set-ExecutionPolicy RemoteSigned
Finally, if you do enable to the remote PowerShell task here is the command line you can use to connect.
Enter-PSSession -ComputerName {hostname}.cloudapp.net -Authentication Basic -Credential Administrator -UseSSL -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck)
