<div dir="ltr">Here it is. I'm not a PowerShell expert so go easy on me :-)<div><br></div><div>...Juerg</div><div><br></div><div><br></div><div><div>#</div><div># Copyright (c) 2014 Hewlett-Packard Development Company, L.P.</div>
<div>#</div><div># Author: Juerg Haefliger <<a href="mailto:juerg.haefliger@hp.com">juerg.haefliger@hp.com</a>></div><div>#</div><div># This program is free software; you can redistribute it and/or</div><div># modify it under the terms of the GNU General Public License as</div>
<div># published by the Free Software Foundation; either version 2 of the</div><div># License, or (at your option) any later version.</div><div>#</div><div># This program is distributed in the hope that it will be useful, but</div>
<div># WITHOUT ANY WARRANTY; without even the implied warranty of</div><div># MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU</div><div># General Public License for more details.</div><div>#</div><div># You should have received a copy of the GNU General Public License</div>
<div># along with this program; if not, write to the Free Software</div><div># Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA</div><div># 02110-1301 USA</div><div>#</div><div># Simple powershell script that runs at startup and provides the following</div>
<div># functionality.</div><div># 1) Generate a random password</div><div># 2) Set the Administrator password</div><div># 3) Pull the public SSH key from the Nova metadata server</div><div># 4) Encrypt the random password with the public SSH key</div>
<div># 5) Write the encrypted password to the serial port (console log)</div><div>#</div><div><br></div><div>$metadata_url = "<a href="http://169.254.169.254/2009-04-04/meta-data">http://169.254.169.254/2009-04-04/meta-data</a>"</div>
<div><br></div><div>$id_rsa_dir = "C:\Users\Administrator\.ssh"</div><div>$id_rsa_pub = $id_rsa_dir + "\id_rsa.pub"</div><div>$id_rsa_pub8 = $id_rsa_dir + "\id_rsa.pub8"</div><div><br></div>
<div>Function Log([string] $text)</div><div>{</div><div><span class="" style="white-space:pre"> </span>$now = Get-Date -format "MMM dd HH:mm:ss"</div><div><span class="" style="white-space:pre"> </span>$console.WriteLine($now + " " + $env:Computername + ": [cloud-init] " +</div>
<div><span class="" style="white-space:pre"> </span> $text)</div><div>}</div><div><br></div><div>Function CloudInit()</div><div>{</div><div><span class="" style="white-space:pre"> </span>$script:console = New-Object System.IO.Ports.SerialPort `</div>
<div><span class="" style="white-space:pre"> </span> COM1,115200,None,8,One</div><div><span class="" style="white-space:pre"> </span>$console.Open()</div><div><span class="" style="white-space:pre"> </span>Log("Starting")</div>
<div>}</div><div><br></div><div>Function CloudExit()</div><div>{</div><div><span class="" style="white-space:pre"> </span>Log("Done")</div><div><span class="" style="white-space:pre"> </span>$console.Close()</div>
<div><span class="" style="white-space:pre"> </span>exit</div><div>}</div><div><br></div><div>Function Out([string] $text)</div><div>{</div><div><span class="" style="white-space:pre"> </span>$console.WriteLine($text)</div>
<div>}</div><div><br></div><div>Function WaitForMetadataService()</div><div>{</div><div><span class="" style="white-space:pre"> </span>log("Connect to metadata service")</div><div><span class="" style="white-space:pre"> </span>$client = New-Object Net.WebClient</div>
<div><span class="" style="white-space:pre"> </span>for ($i = 1; $i -le 10; $i++) {</div><div><span class="" style="white-space:pre"> </span>if ($i -gt 1) {</div><div><span class="" style="white-space:pre"> </span>Start-Sleep -s ($i * 2)</div>
<div><span class="" style="white-space:pre"> </span>log("Connect to metadata service (" + $i + ". try)")</div><div><span class="" style="white-space:pre"> </span>}</div><div><span class="" style="white-space:pre"> </span>$tmp = $client.DownloadString($metadata_url)</div>
<div><span class="" style="white-space:pre"> </span>if ($?) {</div><div><span class="" style="white-space:pre"> </span>return</div><div><span class="" style="white-space:pre"> </span>}</div><div><span class="" style="white-space:pre"> </span>}</div>
<div><span class="" style="white-space:pre"> </span>log("Giving up")</div><div><span class="" style="white-space:pre"> </span>CloudExit</div><div>}</div><div><br></div><div>Function GetLocalHostname()</div><div>
{</div><div><span class="" style="white-space:pre"> </span>log("Get local hostname")</div><div><span class="" style="white-space:pre"> </span>$client = New-Object Net.WebClient</div><div><span class="" style="white-space:pre"> </span>$val = $client.DownloadString($metadata_url + "/local-hostname")</div>
<div><span class="" style="white-space:pre"> </span>return $val</div><div>}</div><div><br></div><div>Function GetPublicSshKey()</div><div>{</div><div><span class="" style="white-space:pre"> </span>log("Get public SSH key")</div>
<div><span class="" style="white-space:pre"> </span>if (!(Test-Path $id_rsa_dir)) {</div><div><span class="" style="white-space:pre"> </span>New-Item -type directory -path $id_rsa_dir</div><div><span class="" style="white-space:pre"> </span>}</div>
<div><span class="" style="white-space:pre"> </span>$client = New-Object Net.WebClient</div><div><span class="" style="white-space:pre"> </span>$val = $client.DownloadString($metadata_url + </div><div><span class="" style="white-space:pre"> </span> "/public-keys/0/openssh-key")</div>
<div><span class="" style="white-space:pre"> </span>$val | Out-File -encoding ascii -filePath $id_rsa_pub</div><div><span class="" style="white-space:pre"> </span>ssh-keygen -e -P "dummy" -m pkcs8 -f $id_rsa_pub | Out-File `</div>
<div><span class="" style="white-space:pre"> </span> -encoding ascii -filePath $id_rsa_pub8</div><div>}</div><div><br></div><div>Function GenerateRandomPassword([int] $length)</div><div>{</div><div><span class="" style="white-space:pre"> </span>log("Generate random password")</div>
<div><span class="" style="white-space:pre"> </span>Add-Type -AssemblyName System.Web</div><div><span class="" style="white-space:pre"> </span>$val = [System.Web.Security.Membership]::GeneratePassword($length, 4)</div><div>
<span class="" style="white-space:pre"> </span>return $val</div><div>}</div><div><br></div><div>Function EncryptPassword([string] $secret)</div><div>{</div><div><span class="" style="white-space:pre"> </span>log("Encrypt random password")</div>
<div><span class="" style="white-space:pre"> </span>$temp_file = [System.IO.Path]::GetTempFileName()</div><div><span class="" style="white-space:pre"> </span>$secret | openssl rsautl -encrypt -inkey $id_rsa_pub8 -pubin `</div>
<div><span class="" style="white-space:pre"> </span> -out $temp_file</div><div><span class="" style="white-space:pre"> </span>$val = openssl enc -base64 -in $temp_file</div><div><span class="" style="white-space:pre"> </span>Remove-Item $temp_file</div>
<div><span class="" style="white-space:pre"> </span>return $val</div><div>}</div><div><br></div><div>Function SetAdministratorPassword([string] $secret)</div><div>{</div><div><span class="" style="white-space:pre"> </span>log("Set Administrator password")</div>
<div><span class="" style="white-space:pre"> </span>[adsi] $admin = "WinNT://" + $env:Computername + "/Administrator"</div><div><span class="" style="white-space:pre"> </span>$admin.SetPassword($secret)</div>
<div>}</div><div><br></div><div>Function PrintEncryptedPassword([string[]] $secret)</div><div>{</div><div><span class="" style="white-space:pre"> </span>Out("-----BEGIN BASE64-ENCODED ENCRYPTED PASSWORD-----")</div>
<div><span class="" style="white-space:pre"> </span>foreach ($line in $secret) {</div><div><span class="" style="white-space:pre"> </span>Out($line)</div><div><span class="" style="white-space:pre"> </span>}</div><div><span class="" style="white-space:pre"> </span>Out("-----END BASE64-ENCODED ENCRYPTED PASSWORD-----")</div>
<div>}</div><div><br></div><div>CloudInit</div><div><br></div><div>if (Test-Path $id_rsa_pub) {</div><div><span class="" style="white-space:pre"> </span>Log("Nothing to do")</div><div><span class="" style="white-space:pre"> </span>CloudExit</div>
<div>}</div><div><br></div><div>$plain_pw = GenerateRandomPassword(12)</div><div>SetAdministratorPassword($plain_pw)</div><div><br></div><div>WaitForMetadataService</div><div>GetPublicSshKey</div><div><br></div><div>$encrypted_pw = EncryptPassword($plain_pw)</div>
<div>PrintEncryptedPassword($encrypted_pw)</div><div><br></div><div>CloudExit</div></div><div><br></div><div><br><div><br></div></div></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Thu, Jan 23, 2014 at 9:52 AM, Clark, Robert Graham <span dir="ltr"><<a href="mailto:robert.clark@hp.com" target="_blank">robert.clark@hp.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="im">On Thu Jan 23 07:54:23 2014, Juerg Haefliger wrote:<br>
> On Tue, Jan 21, 2014 at 8:22 AM, Joe Topjian <<a href="mailto:joe@topjian.net">joe@topjian.net</a><br>
</div><div class="im">> <mailto:<a href="mailto:joe@topjian.net">joe@topjian.net</a>>> wrote:<br>
> ><br>
> > Hi Juerg,<br>
> ><br>
> > That's a really creative way of setting the password. Are you able<br>
> to share your powershell script?<br>
><br>
> Sorry, missed this request earlier. Need to check with legal (sigh).<br>
><br>
> ..Juerg<br>
><br>
><br>
> > Thanks,<br>
> > Joe<br>
> ><br>
> ><br>
> > On Tue, Jan 21, 2014 at 8:15 AM, Juerg Haefliger <<a href="mailto:juergh@gmail.com">juergh@gmail.com</a><br>
</div><div class="im">> <mailto:<a href="mailto:juergh@gmail.com">juergh@gmail.com</a>>> wrote:<br>
> >><br>
> >><br>
> >> On Tue, Jan 21, 2014 at 3:15 AM, jeffty <<a href="mailto:wantwatering@gmail.com">wantwatering@gmail.com</a><br>
</div><div><div class="h5">> <mailto:<a href="mailto:wantwatering@gmail.com">wantwatering@gmail.com</a>>> wrote:<br>
> >> ><br>
> >> > Thanks Joe, It really helps.<br>
> >> ><br>
> >> > Will check them to find the proper way.<br>
> >> ><br>
> >> > Thanks.<br>
> >> ><br>
> >> > On 1/19/2014 3:32 PM, Joe Topjian wrote:<br>
> >> > > Hello,<br>
> >> > ><br>
> >> > > We've used this in the past:<br>
> >> > ><br>
> >> > > <a href="https://github.com/jordanrinke/openstack" target="_blank">https://github.com/jordanrinke/openstack</a><br>
> >> > ><br>
> >> > > It allows a user to type in an Administrator password in the<br>
> Post Config<br>
> >> > > text box when launching an instance in Horizon. The password is<br>
> then<br>
> >> > > retrieved when Windows first boots via the metadata service.<br>
> >> > ><br>
> >> > > We stopped using it for two reasons, though:<br>
> >> > ><br>
> >> > > 1. The password was permanently stored in the metadata server<br>
> >> > > 2. There was no (default) way to let the user know that the<br>
> password<br>
> >> > > they chose was not a strong enough password<br>
> >> > ><br>
> >> > > We now just have users connect to the VNC console and set the<br>
> password<br>
> >> > > upon first boot.<br>
> >> > ><br>
> >> > > There have been a few discussions over the past year on the<br>
> >> > > openstack-operators list about the cloudbase Windows cloud-init<br>
> service.<br>
> >> > > I think one or two people have been able to get the password<br>
> injection<br>
> >> > > portion working. It might be worth a shot to search the archives:<br>
> >> > ><br>
> >> > > <a href="http://www.gossamer-threads.com/lists/openstack/operators/" target="_blank">http://www.gossamer-threads.com/lists/openstack/operators/</a><br>
> >> > ><br>
> >> > > Joe<br>
> >> > ><br>
> >> > ><br>
> >> > > On Sun, Jan 19, 2014 at 4:21 AM, jeffty <<a href="mailto:wantwatering@gmail.com">wantwatering@gmail.com</a><br>
> <mailto:<a href="mailto:wantwatering@gmail.com">wantwatering@gmail.com</a>><br>
</div></div><div><div class="h5">> >> > > <mailto:<a href="mailto:wantwatering@gmail.com">wantwatering@gmail.com</a><br>
> <mailto:<a href="mailto:wantwatering@gmail.com">wantwatering@gmail.com</a>>>> wrote:<br>
> >> > ><br>
> >> > > Thanks Jacob.<br>
> >> > ><br>
> >> > > Is there any openstack API guide for send instance password<br>
> while<br>
> >> > > launch it?<br>
> >> > ><br>
> >> > > Thanks.<br>
> >> > ><br>
> >> > > On 1/19/2014 11:08 AM, Jacob Godin wrote:<br>
> >> > > > Yes, they must input a password every time. It's within<br>
> Windows, they<br>
> >> > > > must use the console.<br>
> >> > > ><br>
> >> > > > Sent from my mobile device<br>
> >> > > ><br>
> >> > > > On Jan 18, 2014 10:51 PM, "jeffty"<br>
> <<a href="mailto:wantwatering@gmail.com">wantwatering@gmail.com</a> <mailto:<a href="mailto:wantwatering@gmail.com">wantwatering@gmail.com</a>><br>
> >> > > <mailto:<a href="mailto:wantwatering@gmail.com">wantwatering@gmail.com</a> <mailto:<a href="mailto:wantwatering@gmail.com">wantwatering@gmail.com</a>>><br>
> >> > > > <mailto:<a href="mailto:wantwatering@gmail.com">wantwatering@gmail.com</a><br>
> <mailto:<a href="mailto:wantwatering@gmail.com">wantwatering@gmail.com</a>> <mailto:<a href="mailto:wantwatering@gmail.com">wantwatering@gmail.com</a><br>
> <mailto:<a href="mailto:wantwatering@gmail.com">wantwatering@gmail.com</a>>>>><br>
> >> > > wrote:<br>
> >> > > ><br>
> >> > > > Thanks Jacob.<br>
> >> > > ><br>
> >> > > > Then the user must input a password for every windows<br>
> instance he<br>
> >> > > > launched?<br>
> >> > > ><br>
> >> > > > In other word different instance owns different<br>
> password even<br>
> >> > > they are<br>
> >> > > > launched at the same time? e.g. Input 3 while launching<br>
> >> > > instance in<br>
> >> > > > Horizon portal for this windows image.<br>
> >> > > ><br>
> >> > > > If yes, how to send this password to the instance in<br>
> portal?<br>
> >> > > That should<br>
> >> > > > be implemented by meta service.<br>
> >> > > ><br>
> >> > > > If no, all of the instances have the same default<br>
> password, right?<br>
> >> > > ><br>
> >> > > ><br>
> >> > > > On 1/19/2014 10:02 AM, Jacob Godin wrote:<br>
> >> > > > > We've used sysprep to have the administrator<br>
> provide a password<br>
> >> > > > when the<br>
> >> > > > > instance is first booted.<br>
> >> > > ><br>
> >><br>
> >> We use a simple powershell script that generates a random<br>
> Administrator password on first boot, pulls the SSH key from the<br>
> metadata server, encrypts the password with the key and writes the<br>
> encrypted password to the serial port.<br>
> >><br>
> >> The user retrieves the encrypted password through the nova<br>
> console-log and decrypts it with his private key. The image is setup<br>
> such that the user is prompted to change the (random) password the<br>
> first time he logs into the instance.<br>
> >><br>
> >> ...Juerg<br>
> >><br>
> >><br>
> >><br>
> >> > ><br>
> >> > > _______________________________________________<br>
> >> > > Mailing list:<br>
> >> > > <a href="http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack" target="_blank">http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack</a><br>
> >> > > Post to : <a href="mailto:openstack@lists.openstack.org">openstack@lists.openstack.org</a><br>
> <mailto:<a href="mailto:openstack@lists.openstack.org">openstack@lists.openstack.org</a>><br>
</div></div>> >> > > <mailto:<a href="mailto:openstack@lists.openstack.org">openstack@lists.openstack.org</a><br>
<div class="im">> <mailto:<a href="mailto:openstack@lists.openstack.org">openstack@lists.openstack.org</a>>><br>
> >> > > Unsubscribe :<br>
> >> > > <a href="http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack" target="_blank">http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack</a><br>
> >> > ><br>
> >> > ><br>
> >> ><br>
> >> ><br>
> >> > _______________________________________________<br>
> >> > Mailing list:<br>
> <a href="http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack" target="_blank">http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack</a><br>
> >> > Post to : <a href="mailto:openstack@lists.openstack.org">openstack@lists.openstack.org</a><br>
> <mailto:<a href="mailto:openstack@lists.openstack.org">openstack@lists.openstack.org</a>><br>
> >> > Unsubscribe :<br>
> <a href="http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack" target="_blank">http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack</a><br>
> ><br>
> ><br>
<br>
</div>If it's not possible to release the script it shouldn't be to hard to<br>
re-create. Juerg has already described the tricky bit, which is the<br>
crypto stuff, the only piece missing is putting the password into<br>
Windows :)<br>
</blockquote></div><br></div>