You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by an...@apache.org on 2016/06/06 10:05:19 UTC

[1/2] jclouds-labs-google git commit: support shortened hardware id

Repository: jclouds-labs-google
Updated Branches:
  refs/heads/1.9.x 3053cb5ae -> 276ee07e5


support shortened hardware id


Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/commit/8d648431
Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/tree/8d648431
Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/diff/8d648431

Branch: refs/heads/1.9.x
Commit: 8d648431580d075b24dfacb43876b9b52393c1f7
Parents: 3053cb5
Author: Andrea Turli <an...@gmail.com>
Authored: Fri Apr 15 16:23:23 2016 +0200
Committer: Andrea Turli <an...@gmail.com>
Committed: Mon Jun 6 12:04:29 2016 +0200

----------------------------------------------------------------------
 .../functions/MachineTypeToHardware.java        |  9 +-
 .../src/test/resources/logback.xml              | 99 ++++----------------
 2 files changed, 24 insertions(+), 84 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/8d648431/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/MachineTypeToHardware.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/MachineTypeToHardware.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/MachineTypeToHardware.java
index a1eaf36..b0887e5 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/MachineTypeToHardware.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/MachineTypeToHardware.java
@@ -54,8 +54,9 @@ public final class MachineTypeToHardware implements Function<MachineType, Hardwa
          throw new IllegalStateException(
                String.format("zone %s not present in %s", zoneLink, locationsByUri.get().keySet()));
       }
+
       return new HardwareBuilder()
-              .id(input.selfLink().toString())
+              .id(buildHardwareId(zone, input.selfLink()))
               .providerId(input.id())
               .location(zone)
               .name(input.name())
@@ -69,6 +70,12 @@ public final class MachineTypeToHardware implements Function<MachineType, Hardwa
               .build();
    }
 
+   private String buildHardwareId(Location zone, URI uri) {
+      String hardwareURI = uri.toString();
+      String hardwareName = hardwareURI.substring(hardwareURI.lastIndexOf('/') + 1);
+      return String.format("%s/%s", zone.getId(), hardwareName);
+   }
+
    private Iterable<Volume> collectVolumes(MachineType input) {
       ImmutableSet.Builder<Volume> volumes = ImmutableSet.builder();
       for (MachineType.ScratchDisk disk : input.scratchDisks()) {

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/8d648431/google-compute-engine/src/test/resources/logback.xml
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/resources/logback.xml b/google-compute-engine/src/test/resources/logback.xml
index 864a5a5..367150f 100644
--- a/google-compute-engine/src/test/resources/logback.xml
+++ b/google-compute-engine/src/test/resources/logback.xml
@@ -1,83 +1,16 @@
-<?xml version="1.0"?>
-<!--
-
-    Licensed to the Apache Software Foundation (ASF) under one or more
-    contributor license agreements.  See the NOTICE file distributed with
-    this work for additional information regarding copyright ownership.
-    The ASF licenses this file to You under the Apache License, Version 2.0
-    (the "License"); you may not use this file except in compliance with
-    the License.  You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-
--->
-<configuration scan="false">
-    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
-        <file>target/test-data/jclouds.log</file>
-
-        <encoder>
-            <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
-        </encoder>
-    </appender>
-
-    <appender name="WIREFILE" class="ch.qos.logback.core.FileAppender">
-        <file>target/test-data/jclouds-wire.log</file>
-
-        <encoder>
-            <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
-        </encoder>
-    </appender>
-
-    <appender name="COMPUTEFILE" class="ch.qos.logback.core.FileAppender">
-        <file>target/test-data/jclouds-compute.log</file>
-
-        <encoder>
-            <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
-        </encoder>
-    </appender>
-
-    <appender name="SSHFILE" class="ch.qos.logback.core.FileAppender">
-        <file>target/test-data/jclouds-ssh.log</file>
-
-        <encoder>
-            <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
-        </encoder>
-    </appender>
-
-    <root>
-        <level value="warn" />
-    </root>
-
-    <logger name="org.jclouds">
-        <level value="TRACE" />
-        <appender-ref ref="FILE" />
-    </logger>
-
-    <logger name="jclouds.wire">
-        <level value="TRACE" />
-        <appender-ref ref="WIREFILE" />
-    </logger>
-
-    <logger name="jclouds.headers">
-        <level value="TRACE" />
-        <appender-ref ref="WIREFILE" />
-    </logger>
-
-    <logger name="jclouds.compute">
-        <level value="TRACE" />
-        <appender-ref ref="COMPUTEFILE" />
-    </logger>
-
-    <logger name="jclouds.ssh">
-        <level value="TRACE" />
-        <appender-ref ref="SSHFILE" />
-    </logger>
-
-</configuration>
-
+<?xml version="1.0"?>
+<configuration>
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>-  %msg%n</pattern>
+    </encoder>
+  </appender>
+  <root level="info">
+    <appender-ref ref="STDOUT"/>
+  </root>
+  <logger name="jclouds.compute" level="debug"/>
+  <logger name="net.schmizz" level="warn"/>
+  <logger name="jclouds.wire" level="debug"/>
+  <logger name="jclouds.headers" level="debug"/>
+  <logger name="jclouds.ssh" level="debug"/>
+</configuration>


[2/2] jclouds-labs-google git commit: GCE ResetWindowsPassword

Posted by an...@apache.org.
GCE ResetWindowsPassword

- Function which tells to GCE to reset Windows password

- InstanceApiLiveTest for windows

- Unit test for decrypting windows password


Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/commit/276ee07e
Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/tree/276ee07e
Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/diff/276ee07e

Branch: refs/heads/1.9.x
Commit: 276ee07e57a4851e8e93e1f64c85f1caa3ff848a
Parents: 8d64843
Author: Valentin Aitken <bo...@gmail.com>
Authored: Tue Mar 29 03:42:20 2016 +0300
Committer: Andrea Turli <an...@gmail.com>
Committed: Mon Jun 6 12:05:19 2016 +0200

----------------------------------------------------------------------
 google-compute-engine/pom.xml                   |   6 +
 .../GoogleComputeEngineApiMetadata.java         |   2 +-
 .../GoogleComputeEngineServiceAdapter.java      |  37 ++-
 ...GoogleComputeEngineServiceContextModule.java |   6 +-
 .../compute/functions/ResetWindowsPassword.java | 226 +++++++++++++++++++
 .../GoogleComputeEngineTemplateOptions.java     |  24 +-
 .../features/InstanceApi.java                   |  11 +
 .../functions/ResetWindowsPasswordTest.java     | 105 +++++++++
 .../features/InstanceApiMockTest.java           |  12 +
 .../features/InstanceApiWindowsLiveTest.java    | 132 +++++++++++
 .../BaseGoogleComputeEngineApiLiveTest.java     |   3 +-
 .../parse/ParseInstanceSerialOutputTest.java    |   8 +
 .../instance_serial_port_4_windows.json         |   5 +
 13 files changed, 562 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/276ee07e/google-compute-engine/pom.xml
----------------------------------------------------------------------
diff --git a/google-compute-engine/pom.xml b/google-compute-engine/pom.xml
index a89e805..0ad083b 100644
--- a/google-compute-engine/pom.xml
+++ b/google-compute-engine/pom.xml
@@ -115,6 +115,12 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.jclouds.driver</groupId>
+            <artifactId>jclouds-bouncycastle</artifactId>
+            <version>${jclouds.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
           <groupId>com.squareup.okhttp</groupId>
           <artifactId>mockwebserver</artifactId>
           <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/276ee07e/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java
index c2e632a..ce50e06 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java
@@ -61,7 +61,7 @@ public class GoogleComputeEngineApiMetadata extends BaseHttpApiMetadata<GoogleCo
       properties.put(JWS_ALG, "RS256");
       properties.put(PROPERTY_SESSION_INTERVAL, 3600);
       properties.put(OPERATION_COMPLETE_INTERVAL, 500);
-      properties.put(OPERATION_COMPLETE_TIMEOUT, 600000);
+      properties.put(OPERATION_COMPLETE_TIMEOUT, 1000000);
       properties.put(TEMPLATE, "osFamily=DEBIAN,osVersionMatches=7\\..*,locationId=us-central1-a");
       properties.put(PROJECT_NAME, ""); // Defaulting to empty helps avoid temptation for optional inject!
       properties.put(IMAGE_PROJECTS, "centos-cloud,debian-cloud,rhel-cloud,suse-cloud,opensuse-cloud,gce-nvme,coreos-cloud,ubuntu-os-cloud,windows-cloud");

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/276ee07e/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java
index ee50d92..8dfab09 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java
@@ -24,24 +24,19 @@ import static java.lang.String.format;
 import static org.jclouds.googlecloud.internal.ListPages.concat;
 import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperties.IMAGE_PROJECTS;
 
-import javax.inject.Inject;
-import javax.inject.Named;
 import java.net.URI;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
 
-import com.google.common.base.Predicate;
-import com.google.common.base.Splitter;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.util.concurrent.Atomics;
-import com.google.common.util.concurrent.UncheckedTimeoutException;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import com.google.common.collect.ImmutableMap;
 import org.jclouds.compute.ComputeServiceAdapter;
 import org.jclouds.compute.domain.Hardware;
 import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.OsFamily;
 import org.jclouds.compute.domain.Template;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.domain.Location;
@@ -66,6 +61,16 @@ import org.jclouds.googlecomputeengine.domain.Zone;
 import org.jclouds.googlecomputeengine.features.InstanceApi;
 import org.jclouds.location.suppliers.all.JustProvider;
 
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Atomics;
+import com.google.common.util.concurrent.UncheckedTimeoutException;
+
 /**
  * This implementation maps the following:
  * <ul>
@@ -87,6 +92,7 @@ public final class GoogleComputeEngineServiceAdapter
    private final Map<URI, URI> diskToSourceImage;
    private final Predicate<AtomicReference<Operation>> operationDone;
    private final Predicate<AtomicReference<Instance>> instanceVisible;
+   private final Function<Map<String, ?>, String> windowsPasswordGenerator;
    private final FirewallTagNamingConvention.Factory firewallTagNamingConvention;
    private final List<String> imageProjects;
 
@@ -94,6 +100,7 @@ public final class GoogleComputeEngineServiceAdapter
                                             Predicate<AtomicReference<Operation>> operationDone,
                                             Predicate<AtomicReference<Instance>> instanceVisible,
                                             Map<URI, URI> diskToSourceImage,
+                                            Function<Map<String, ?>, String> windowsPasswordGenerator,
                                             Resources resources,
                                             FirewallTagNamingConvention.Factory firewallTagNamingConvention,
                                             @Named(IMAGE_PROJECTS) String imageProjects) {
@@ -102,6 +109,7 @@ public final class GoogleComputeEngineServiceAdapter
       this.operationDone = operationDone;
       this.instanceVisible = instanceVisible;
       this.diskToSourceImage = diskToSourceImage;
+      this.windowsPasswordGenerator = windowsPasswordGenerator;
       this.resources = resources;
       this.firewallTagNamingConvention = firewallTagNamingConvention;
       this.imageProjects = Splitter.on(',').omitEmptyStrings().splitToList(imageProjects);
@@ -173,6 +181,15 @@ public final class GoogleComputeEngineServiceAdapter
       // Add lookup for InstanceToNodeMetadata
       diskToSourceImage.put(instance.get().disks().get(0).source(), template.getImage().getUri());
 
+      if (options.autoCreateWindowsPassword() != null && options.autoCreateWindowsPassword()
+              || OsFamily.WINDOWS == template.getImage().getOperatingSystem().getFamily()) {
+          Map<String, ?> params = ImmutableMap.of("instance", instance, "zone", zone, "email", create.user(), "userName", credentials.getUser());
+          String password = windowsPasswordGenerator.apply(params);
+          credentials = LoginCredentials.builder(credentials)
+		          .password(password)
+		          .build();
+      }
+
       return new NodeAndInitialCredentials<Instance>(instance.get(), instance.get().selfLink().toString(), credentials);
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/276ee07e/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java
index 1e22b29..0da5ce4 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java
@@ -58,10 +58,11 @@ import org.jclouds.googlecomputeengine.compute.functions.InstanceToNodeMetadata;
 import org.jclouds.googlecomputeengine.compute.functions.MachineTypeToHardware;
 import org.jclouds.googlecomputeengine.compute.functions.OrphanedGroupsFromDeadNodes;
 import org.jclouds.googlecomputeengine.compute.functions.Resources;
+import org.jclouds.googlecomputeengine.compute.functions.ResetWindowsPassword;
 import org.jclouds.googlecomputeengine.compute.options.GoogleComputeEngineTemplateOptions;
-import org.jclouds.googlecomputeengine.compute.predicates.GroupIsEmpty;
 import org.jclouds.googlecomputeengine.compute.predicates.AtomicInstanceVisible;
 import org.jclouds.googlecomputeengine.compute.predicates.AtomicOperationDone;
+import org.jclouds.googlecomputeengine.compute.predicates.GroupIsEmpty;
 import org.jclouds.googlecomputeengine.compute.strategy.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
 import org.jclouds.googlecomputeengine.domain.Image;
 import org.jclouds.googlecomputeengine.domain.Instance;
@@ -134,6 +135,9 @@ public final class GoogleComputeEngineServiceContextModule
       bind(new TypeLiteral<CacheLoader<NetworkAndAddressRange, Network>>() {
       }).to(FindNetworkOrCreate.class);
 
+      bind(new TypeLiteral<Function<Map<String, ?>, String>>() {
+      }).to(ResetWindowsPassword.class);
+
       bind(FirewallTagNamingConvention.Factory.class).in(Scopes.SINGLETON);
       bindHttpApi(binder(), Resources.class);
    }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/276ee07e/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/ResetWindowsPassword.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/ResetWindowsPassword.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/ResetWindowsPassword.java
new file mode 100644
index 0000000..cd25fa1
--- /dev/null
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/ResetWindowsPassword.java
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.googlecomputeengine.compute.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPublicKeySpec;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.annotation.Resource;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import com.google.gson.GsonBuilder;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.crypto.Crypto;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
+import org.jclouds.googlecomputeengine.domain.Instance;
+import org.jclouds.googlecomputeengine.domain.Instance.SerialPortOutput;
+import org.jclouds.googlecomputeengine.domain.Metadata;
+import org.jclouds.googlecomputeengine.domain.Operation;
+import org.jclouds.googlecomputeengine.features.InstanceApi;
+import org.jclouds.logging.Logger;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.BaseEncoding;
+import com.google.common.util.concurrent.Atomics;
+import com.google.gson.Gson;
+import org.jclouds.util.Predicates2;
+
+/**
+ * References:
+ * <ul>
+ *   <li><a href="https://cloud.google.com/compute/docs/instances/automate-pw-generation">automate-pw-generation</a>
+ *   <li><a href="https://github.com/GoogleCloudPlatform/compute-image-windows/blob/master/examples/windows_auth_java_sample.java">windows_auth_java_sample.java</a>
+ * </ul>
+ * 
+ * In brief, the sequence is:
+ * <ol>
+ *   <li>Generate a temporary key for encrypting and decrypting the password
+ *   <li>Send the RSA public key to the instance, by settings its metadata
+ *   <li>Retrieve the result from the {@link SerialPortOutput}
+ *   <li>Decode and decrypt the result.
+ * </ol>
+ */
+public class ResetWindowsPassword implements Function<Map<String, ?>, String> {
+
+   /**
+    * Indicates when the key should expire. Keys are one-time use, so the metadata doesn't need to stay around for long.
+    * 10 minutes chosen to allow for differences between time on the client
+    * and time on the server.
+    */
+   private static final long EXPIRE_DURATION = 10 * 60 * 1000;
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private final GoogleComputeEngineApi api;
+   private final Crypto crypto;
+   private final Predicate<AtomicReference<Operation>> operationDone;
+
+   @Inject
+   protected ResetWindowsPassword(GoogleComputeEngineApi api, Crypto crypto, Predicate<AtomicReference<Operation>> operationDone) {
+      this.api = api;
+      this.crypto = crypto;
+      this.operationDone = operationDone;
+   }
+
+   @Override
+   public String apply(Map<String, ?> params) {
+      String zone = (String)params.get("zone");
+      final AtomicReference<Instance> instance = (AtomicReference<Instance>)params.get("instance");
+      String userName = (String)params.get("userName");
+      String email = (String)params.get("email");
+
+       // Generate the public/private key pair for encryption and decryption.
+       // TODO do we need to explicitly set 2048 bits? Presumably "RSA" is implicit
+       KeyPair keys = crypto.rsaKeyPairGenerator().genKeyPair();
+
+       // Update instance's metadata with new "windows-keys" entry, and wait for operation to
+       // complete.
+       logger.debug("Generating windows key for instance %s, by updating metadata", instance.get().name());
+       final InstanceApi instanceApi = api.instancesInZone(zone);
+       Metadata metadata = instance.get().metadata();
+
+      try {
+         // If disableHtmlEscaping is not there, == will be escaped from modulus value
+         metadata.put("windows-keys", new GsonBuilder().disableHtmlEscaping().create().toJson(extractKeyMetadata(keys, userName, email)));
+      } catch (NoSuchAlgorithmException e) {
+         Throwables.propagate(e);
+      } catch (InvalidKeySpecException e) {
+         Throwables.propagate(e);
+      }
+
+       AtomicReference<Operation> operation = Atomics.newReference(instanceApi.setMetadata(instance.get().name(), metadata));
+       operationDone.apply(operation);
+
+       if (operation.get().httpErrorStatusCode() != null) {
+          logger.warn("Generating windows key for %s failed. Http Error Code: %d HttpError: %s",
+                operation.get().targetId(), operation.get().httpErrorStatusCode(),
+                operation.get().httpErrorMessage());
+       }
+
+       try {
+          final Map<String, String> passwordDict = new HashMap<String, String>();
+          boolean passwordRetrieved = Predicates2.retry(new Predicate<Instance>() {
+             public boolean apply(Instance instance) {
+                String serialPortContents = instanceApi.getSerialPortOutput(instance.name(), 4).contents();
+                if (!serialPortContents.startsWith("{\"ready\":true")) {
+                   return false;
+                }
+                String[] contentEntries = serialPortContents.split("\n");
+                passwordDict.clear();
+                passwordDict.putAll(new Gson().fromJson(contentEntries[contentEntries.length - 1], Map.class));
+                passwordDict.put("passwordDictContentEntries", contentEntries[contentEntries.length - 1]);
+                return passwordDict.get("encryptedPassword") != null;
+             }
+          }, 10 * 60, 30, TimeUnit.SECONDS).apply(instance.get()); // Notice that timeoutDuration should be less than EXPIRE_DURATION
+          if (passwordRetrieved) {
+             return decryptPassword(checkNotNull(passwordDict.get("encryptedPassword"), "encryptedPassword shouldn't be null"), keys);
+          } else {
+             throw new IllegalStateException("encryptedPassword shouldn't be null: " + passwordDict.get("passwordDictContentEntries"));
+          }
+       } catch (Exception e) {
+          throw Throwables.propagate(e);
+       }
+   }
+
+   /**
+    * Decrypts the given password - the encrypted text is base64-encoded.
+    * As per the GCE docs, assumes it was encrypted with algorithm "RSA/NONE/OAEPPadding", and UTF-8.
+    */
+   protected String decryptPassword(String message, KeyPair keys) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
+      Cipher cipher;
+      try {
+         // Assumes user has configured appropriate crypto guice module.
+         cipher = crypto.cipher("RSA/NONE/OAEPPadding");
+      } catch (NoSuchAlgorithmException e) {
+         throw new RuntimeException("Problem finding cypher. Try adding bouncycastle dependency.", e);
+      } catch (NoSuchPaddingException e) {
+         throw new RuntimeException("Problem finding cypher. Try adding bouncycastle dependency.", e);
+      }
+
+      // Add the private key for decryption.
+      cipher.init(Cipher.DECRYPT_MODE, keys.getPrivate());
+
+      // Decrypt the text.
+      byte[] rawMessage = BaseEncoding.base64().decode(message);
+      byte[] decryptedText = cipher.doFinal(rawMessage);
+
+      // The password was encoded using UTF8. Transform into string.
+      return new String(decryptedText, Charset.forName("UTF-8"));
+   }
+
+   /**
+    * Generates the metadata value for this keypair.
+    * Extracts the public key's the RSA spec's modulus and exponent, encoded as Base-64, and
+    * an expires date.
+    *
+    * @param pair
+    * @return
+    * @throws NoSuchAlgorithmException
+    * @throws InvalidKeySpecException
+    */
+   protected Map<String, String> extractKeyMetadata(KeyPair pair, String userName, String email) throws NoSuchAlgorithmException, InvalidKeySpecException {
+      KeyFactory factory = crypto.rsaKeyFactory();
+      RSAPublicKeySpec pubSpec = factory.getKeySpec(pair.getPublic(), RSAPublicKeySpec.class);
+      BigInteger modulus = pubSpec.getModulus();
+      BigInteger exponent = pubSpec.getPublicExponent();
+
+      // Strip out the leading 0 byte in the modulus.
+      byte[] modulusArr = Arrays.copyOfRange(modulus.toByteArray(), 1, modulus.toByteArray().length);
+      String modulusString = BaseEncoding.base64().encode(modulusArr).replaceAll("\n", "");
+      String exponentString = BaseEncoding.base64().encode(exponent.toByteArray()).replaceAll("\n", "");
+
+      // Create the expire date, formatted as rfc3339
+      Date expireDate = new Date(System.currentTimeMillis() + EXPIRE_DURATION);
+      SimpleDateFormat rfc3339Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+      rfc3339Format.setTimeZone(TimeZone.getTimeZone("UTC"));
+      String expireString = rfc3339Format.format(expireDate);
+
+      return ImmutableMap.<String, String>builder()
+              .put("modulus", modulusString)
+              .put("exponent", exponentString)
+              .put("expireOn", expireString)
+              .put("userName", userName)
+              .put("email", email) // email of the user should be here. Now it is the username.
+              .build();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/276ee07e/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/options/GoogleComputeEngineTemplateOptions.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/options/GoogleComputeEngineTemplateOptions.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/options/GoogleComputeEngineTemplateOptions.java
index 7b4350e..6afb45f 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/options/GoogleComputeEngineTemplateOptions.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/options/GoogleComputeEngineTemplateOptions.java
@@ -29,6 +29,7 @@ public final class GoogleComputeEngineTemplateOptions extends TemplateOptions {
 
    private URI network = null;
    private boolean autoCreateKeyPair = true;
+   private Boolean autoCreateWindowsPassword = null;
    private String bootDiskType;
    private boolean preemptible = false;
 
@@ -46,6 +47,7 @@ public final class GoogleComputeEngineTemplateOptions extends TemplateOptions {
          GoogleComputeEngineTemplateOptions eTo = GoogleComputeEngineTemplateOptions.class.cast(to);
          eTo.network(network());
          eTo.autoCreateKeyPair(autoCreateKeyPair());
+         eTo.autoCreateWindowsPassword(autoCreateWindowsPassword());
          eTo.bootDiskType(bootDiskType());
          eTo.preemptible(preemptible());
       }
@@ -101,12 +103,30 @@ public final class GoogleComputeEngineTemplateOptions extends TemplateOptions {
    }
 
    /**
-    * Sets whether an SSH key pair should be created automatically.
+    * Whether an SSH key pair should be created automatically.
     */
    public boolean autoCreateKeyPair() {
       return autoCreateKeyPair;
    }
-
+   
+   /**
+    * Whether a Windows password should be created automatically; {@link null} means to generate
+    * the password if and only if the image is for a Windows VM.
+    */
+   public Boolean autoCreateWindowsPassword() {
+	  return autoCreateWindowsPassword;
+   }
+   
+   /**
+    * Sets whether to auto-create a windows password. The default ({@code null}) is to always
+    * do so for Windows VMs, inferring this from the image. An explicit value of true or false
+    * overrides this.
+    */
+   public GoogleComputeEngineTemplateOptions autoCreateWindowsPassword(Boolean autoCreateWindowsPassword) {
+      this.autoCreateWindowsPassword = autoCreateWindowsPassword;
+      return this;
+   }
+   
    /**
     * {@inheritDoc}
     */

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/276ee07e/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/InstanceApi.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/InstanceApi.java b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/InstanceApi.java
index 43d708a..99fc284 100644
--- a/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/InstanceApi.java
+++ b/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/InstanceApi.java
@@ -135,6 +135,17 @@ public interface InstanceApi {
    @GET
    @Path("/{instance}/serialPort")
    SerialPortOutput getSerialPortOutput(@PathParam("instance") String instance);
+   
+   /**
+    * Returns the specified instance's serial port output.
+    *
+    * @param instance the instance name.
+    * @return if successful, this method returns a SerialPortOutput containing the instance's serial output.
+    */
+   @Named("Instances:getSerialPortOutput")
+   @GET
+   @Path("/{instance}/serialPort")
+   SerialPortOutput getSerialPortOutput(@PathParam("instance") String instance, @QueryParam("port") int port);
 
    /**
     * Hard-resets the instance.

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/276ee07e/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/functions/ResetWindowsPasswordTest.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/functions/ResetWindowsPasswordTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/functions/ResetWindowsPasswordTest.java
new file mode 100644
index 0000000..0b3495f
--- /dev/null
+++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/compute/functions/ResetWindowsPasswordTest.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.googlecomputeengine.compute.functions;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.isA;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.testng.Assert.assertEquals;
+
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.Security;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.crypto.Cipher;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.BaseEncoding;
+import org.jclouds.crypto.Crypto;
+import org.jclouds.encryption.bouncycastle.BouncyCastleCrypto;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
+import org.jclouds.googlecomputeengine.domain.Instance;
+import org.jclouds.googlecomputeengine.domain.Instance.SerialPortOutput;
+import org.jclouds.googlecomputeengine.domain.Metadata;
+import org.jclouds.googlecomputeengine.domain.Operation;
+import org.jclouds.googlecomputeengine.features.InstanceApi;
+import org.jclouds.googlecomputeengine.parse.ParseInstanceTest;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+
+@Test(groups = "unit")
+public class ResetWindowsPasswordTest {
+   public void testGeneratePassword() throws Exception {
+      Crypto bcCrypto = new BouncyCastleCrypto();
+      Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+
+      String password = "|opj213'33423'*";
+
+      KeyPair keyPair = bcCrypto.rsaKeyPairGenerator().genKeyPair();
+
+      KeyFactory factory = bcCrypto.rsaKeyFactory();
+      RSAPublicKeySpec pubSpec = factory.getKeySpec(keyPair.getPublic(), RSAPublicKeySpec.class);
+      BigInteger exponent = pubSpec.getPublicExponent();
+      String exponentString = BaseEncoding.base64().encode(exponent.toByteArray()).replaceAll("\n", "");
+
+      Cipher cipher = bcCrypto.cipher("RSA/NONE/OAEPPadding");
+      cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
+      String encryptedPass = BaseEncoding.base64().encode(cipher.doFinal(password.getBytes(Charset.forName("UTF-8")), 0, password.length()));
+
+      Predicate<AtomicReference<Operation>> operationDone = Predicates.alwaysTrue();
+      Instance instance = new ParseInstanceTest().expected();
+      String zone = "us-central1-a";
+
+      GoogleComputeEngineApi api = createMock(GoogleComputeEngineApi.class);
+      InstanceApi instanceApi = createMock(InstanceApi.class);
+      Operation operation = createMock(Operation.class);
+      SerialPortOutput serialPortOutput = createMock(SerialPortOutput.class);
+      Crypto crypto = createMock(Crypto.class);
+      KeyPairGenerator keyPairGenerator = createMock(KeyPairGenerator.class);
+      
+      expect(api.instancesInZone(zone)).andReturn(instanceApi).atLeastOnce();
+      expect(crypto.rsaKeyPairGenerator()).andReturn(keyPairGenerator);
+      expect(crypto.rsaKeyFactory()).andReturn(factory);
+      expect(keyPairGenerator.genKeyPair()).andReturn(keyPair);
+      // FIXME assert that metadata contained what we expected
+      expect(instanceApi.setMetadata(eq(instance.name()), isA(Metadata.class))).andReturn(operation).atLeastOnce();
+      expect(operation.httpErrorStatusCode()).andReturn(null);
+      expect(instanceApi.getSerialPortOutput(instance.name(), 4)).andReturn(serialPortOutput).atLeastOnce();
+      expect(serialPortOutput.contents()).andReturn("{\"ready\":true,\"version\":\"Microsoft Windows NT 6.2.9200.0\"}\n" +
+              "{\"encryptedPassword\":\"" + encryptedPass + "\",\"exponent\":\"" + exponentString + "\",\"passwordFound\":true,\"userName\":\"Administrator\"}");
+      expect(crypto.cipher("RSA/NONE/OAEPPadding")).andReturn(bcCrypto.cipher("RSA/NONE/OAEPPadding"));
+
+      replay(api, instanceApi, operation, serialPortOutput, crypto, keyPairGenerator);
+
+      ResetWindowsPassword generator = new ResetWindowsPassword(api, crypto, operationDone);
+      String result = generator.apply(ImmutableMap.of("instance", new AtomicReference<Instance>(instance), "zone", zone,  "email", "test@google.com", "userName", "test"));
+
+      verify(api, instanceApi, operation, serialPortOutput);
+
+      assertEquals(result, password);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/276ee07e/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/InstanceApiMockTest.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/InstanceApiMockTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/InstanceApiMockTest.java
index 1bc3821..e59c7f5 100644
--- a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/InstanceApiMockTest.java
+++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/InstanceApiMockTest.java
@@ -65,6 +65,18 @@ public class InstanceApiMockTest extends BaseGoogleComputeEngineApiMockTest {
       assertSent(server, "GET", "/projects/party/zones/us-central1-a/instances/test-1/serialPort");
    }
 
+   public void getInstanceSerialPortOutputWindowsPassword() throws Exception {
+      server.enqueue(jsonResponse("/instance_serial_port_4_windows.json"));
+
+      assertEquals(instanceApi().getSerialPortOutput("test-1", 4),
+              new ParseInstanceSerialOutputTest().expected(
+                      url("/projects"),
+                      "{\"ready\":true,\"version\":\"Microsoft Windows NT 6.1.7601 Service Pack 1\"}\n{\"encryptedPassword\":\"uiHDEhxyvj6lF5GalHh9TsMZb4bG6Y9qGmFb9S3XI29yvVsDCLdp4IbUg21MncHcaxP0rFu0kyjxlEXDs8y4L1KOhy6iyB42Lh+vZ4XIMjmvU4rZrjsBZ5TxQo9hL0lBW7o3FRM\\/UIXCeRk39ObUl2AjDmQ0mcw1byJI5v9KVJnNMaHdRCy\\/kvN6bx3qqjIhIMu0JExp4UVkAX2Mxb9b+c4o2DiZF5pY6ZfbuEmjSbvGRJXyswkOJ4jTZl+7e6+SZfEal8HJyRfZKiqTjrz+DLjYSlXrfIRqlvKeAFGOJq6IRojNWiTOOh8Zorc0iHDTIkf+MY0scfbBUo5m30Bf4w==\",\"exponent\":\"AQAB\",\"modulus\":\"0tiKdO2JmBHss26jnrSAwb583KG\\/ZIw5JwwMPXrCVsFAPwY1OV3RlT1Hp4Xvpibr7rvJbOC+f\\/Gd0cBrK5pccQfccB+OHKpbBof473zEfRbdtFwPn10RfAFj\\/xikW0r\\/XxgG\\/c8tz9bmALBStGqmwOVOLRHxjwgtGu4poeuwmFfG6TuwgCadxpllW74mviFd4LZVSuCSni5YJnBM2HSJ8NP6g1fqI17KDXt2XO\\/7kSItubmMk+HGEXdH4qiugHYewaIf1o4XSQROC8xlRl7t\\/RaD4U58hKYkVwg0Ir7WzYzAVpG2UR4Co\\/GDG9Hct7HOYekDqVQ+sSZbwzajnVunkw==\",\"passwordFound\":true,\"userName\":\"example-user\"}\n"
+              ));
+
+      assertSent(server, "GET", "/projects/party/zones/us-central1-a/instances/test-1/serialPort?port=4");
+   }
+
    public void insert_noOptions() throws Exception {
       server.enqueue(jsonResponse("/zone_operation.json"));
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/276ee07e/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/InstanceApiWindowsLiveTest.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/InstanceApiWindowsLiveTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/InstanceApiWindowsLiveTest.java
new file mode 100644
index 0000000..5062186
--- /dev/null
+++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/InstanceApiWindowsLiveTest.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.googlecomputeengine.features;
+
+import static org.jclouds.googlecomputeengine.options.ListOptions.Builder.filter;
+import static org.testng.Assert.assertFalse;
+
+import java.net.URI;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.TypeLiteral;
+import org.jclouds.encryption.bouncycastle.config.BouncyCastleCryptoModule;
+import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
+import org.jclouds.googlecomputeengine.domain.Image;
+import org.jclouds.googlecomputeengine.domain.Instance;
+import org.jclouds.googlecomputeengine.domain.NewInstance;
+import org.jclouds.googlecomputeengine.internal.BaseGoogleComputeEngineApiLiveTest;
+import org.jclouds.googlecomputeengine.options.DiskCreationOptions;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+
+@Test(groups = "live", testName = "InstanceApiLiveTest")
+public class InstanceApiWindowsLiveTest extends BaseGoogleComputeEngineApiLiveTest {
+
+   private static final String INSTANCE_NETWORK_NAME = "instance-api-live-test-network";
+   private static final String INSTANCE_NAME = "instance-api-test-instance-1";
+   private static final String DISK_NAME = "instance-live-test-disk";
+   private static final String IPV4_RANGE = "10.0.0.0/8";
+   private static final int DEFAULT_DISK_SIZE_GB = 25;
+
+   private Function<Map<String, ?>, String> reset_windows_password;
+   private NewInstance instance;
+
+   @Override
+   protected GoogleComputeEngineApi create(Properties props, Iterable<Module> modules) {
+      GoogleComputeEngineApi api = super.create(props, modules);
+      reset_windows_password = injector.getInstance(Key.get(new TypeLiteral<Function<Map<String, ?>, String>>() {}));
+
+      List<Image> list = api.images().listInProject("windows-cloud", filter("name eq windows-server-2012.*")).next();
+      URI imageUri = FluentIterable.from(list)
+              .filter(new Predicate<Image>() {
+                 @Override
+                 public boolean apply(Image input) {
+                    // filter out all deprecated images
+                    return !(input.deprecated() != null && input.deprecated().state() != null);
+                 }
+              })
+              .first()
+              .get()
+              .selfLink();
+      instance = NewInstance.create(
+              INSTANCE_NAME,
+              getDefaultMachineTypeUrl(),
+              getNetworkUrl(INSTANCE_NETWORK_NAME),
+              imageUri
+      );
+
+      return api;
+   }
+
+   @Override
+   protected Iterable<Module> setupModules() {
+      return ImmutableSet.<Module>builder().addAll(super.setupModules()).add(new BouncyCastleCryptoModule()).build();
+   }
+
+   private InstanceApi api() {
+      return api.instancesInZone(DEFAULT_ZONE_NAME);
+   }
+
+   private DiskApi diskApi() {
+      return api.disksInZone(DEFAULT_ZONE_NAME);
+   }
+
+   @Test(groups = "live")
+   public void testInsertInstanceWindows() {
+      // need to insert the network first
+      assertOperationDoneSuccessfully(api.networks().createInIPv4Range
+              (INSTANCE_NETWORK_NAME, IPV4_RANGE));
+
+      assertOperationDoneSuccessfully(diskApi().create(DISK_NAME,
+            new DiskCreationOptions.Builder().sizeGb(DEFAULT_DISK_SIZE_GB).build()));
+      assertOperationDoneSuccessfully(api().create(instance));
+   }
+
+   @Test(groups = "live", dependsOnMethods = "testInsertInstanceWindows")
+   public void testGetSerialPortOutput4() throws NoSuchAlgorithmException, CertificateException {
+      Instance instance = api().get(INSTANCE_NAME);
+      Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); // Needed when initializing the cipher
+      String result = reset_windows_password.apply(ImmutableMap.of("instance", new AtomicReference<Instance>(instance), "zone", DEFAULT_ZONE_NAME, "email", identity, "userName", prefix));
+      assertFalse(Strings.isNullOrEmpty(result), "Password shouldn't be empty");
+   }
+
+   @AfterClass(groups = { "integration", "live" })
+   protected void tearDownContext() {
+      try {
+         waitOperationDone(api().delete(INSTANCE_NAME));
+         waitOperationDone(diskApi().delete(DISK_NAME));
+         waitOperationDone(api.networks().delete(INSTANCE_NETWORK_NAME));
+      } catch (Exception e) {
+         // we don't really care about any exception here, so just delete away.
+       }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/276ee07e/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/BaseGoogleComputeEngineApiLiveTest.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/BaseGoogleComputeEngineApiLiveTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/BaseGoogleComputeEngineApiLiveTest.java
index 41e1e74..086c3ee 100644
--- a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/BaseGoogleComputeEngineApiLiveTest.java
+++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/internal/BaseGoogleComputeEngineApiLiveTest.java
@@ -60,6 +60,7 @@ public class BaseGoogleComputeEngineApiLiveTest extends BaseApiLiveTest<GoogleCo
    protected static final String TARGET_HTTP_PROXY_API_URL_SUFFIX = "/global/targetHttpProxies/";
    protected static final String GOOGLE_PROJECT = "google";
 
+   protected Injector injector;
    protected Predicate<AtomicReference<Operation>> operationDone;
    protected URI projectUrl;
 
@@ -73,7 +74,7 @@ public class BaseGoogleComputeEngineApiLiveTest extends BaseApiLiveTest<GoogleCo
    }
 
    @Override protected GoogleComputeEngineApi create(Properties props, Iterable<Module> modules) {
-      Injector injector = newBuilder().modules(modules).overrides(props).buildInjector();
+      injector = newBuilder().modules(modules).overrides(props).buildInjector();
       operationDone = injector.getInstance(Key.get(new TypeLiteral<Predicate<AtomicReference<Operation>>>() {
       }));
       projectUrl = injector.getInstance(Key.get(new TypeLiteral<Supplier<URI>>() {

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/276ee07e/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseInstanceSerialOutputTest.java
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseInstanceSerialOutputTest.java b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseInstanceSerialOutputTest.java
index 5b8eca5..eaf9708 100644
--- a/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseInstanceSerialOutputTest.java
+++ b/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseInstanceSerialOutputTest.java
@@ -45,4 +45,12 @@ public class ParseInstanceSerialOutputTest extends BaseGoogleComputeEngineParseT
             URI.create(baseUrl + "/party/zones/us-central1-a/instances/test-instance/serialPort"),
             "console output");
    }
+
+   @Consumes(APPLICATION_JSON)
+   public SerialPortOutput expected(String baseUrl, String contents) {
+      return SerialPortOutput.create(
+              URI.create(baseUrl + "/party/zones/us-central1-a/instances/test-instance/serialPort"),
+              contents
+      );
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/276ee07e/google-compute-engine/src/test/resources/instance_serial_port_4_windows.json
----------------------------------------------------------------------
diff --git a/google-compute-engine/src/test/resources/instance_serial_port_4_windows.json b/google-compute-engine/src/test/resources/instance_serial_port_4_windows.json
new file mode 100644
index 0000000..f78df2c
--- /dev/null
+++ b/google-compute-engine/src/test/resources/instance_serial_port_4_windows.json
@@ -0,0 +1,5 @@
+{
+  "kind": "compute#serialPortOutput",
+  "selfLink": "https://www.googleapis.com/compute/v1/projects/party/zones/us-central1-a/instances/test-instance/serialPort",
+  "contents": "{\"ready\":true,\"version\":\"Microsoft Windows NT 6.1.7601 Service Pack 1\"}\n{\"encryptedPassword\":\"uiHDEhxyvj6lF5GalHh9TsMZb4bG6Y9qGmFb9S3XI29yvVsDCLdp4IbUg21MncHcaxP0rFu0kyjxlEXDs8y4L1KOhy6iyB42Lh+vZ4XIMjmvU4rZrjsBZ5TxQo9hL0lBW7o3FRM\\/UIXCeRk39ObUl2AjDmQ0mcw1byJI5v9KVJnNMaHdRCy\\/kvN6bx3qqjIhIMu0JExp4UVkAX2Mxb9b+c4o2DiZF5pY6ZfbuEmjSbvGRJXyswkOJ4jTZl+7e6+SZfEal8HJyRfZKiqTjrz+DLjYSlXrfIRqlvKeAFGOJq6IRojNWiTOOh8Zorc0iHDTIkf+MY0scfbBUo5m30Bf4w==\",\"exponent\":\"AQAB\",\"modulus\":\"0tiKdO2JmBHss26jnrSAwb583KG\\/ZIw5JwwMPXrCVsFAPwY1OV3RlT1Hp4Xvpibr7rvJbOC+f\\/Gd0cBrK5pccQfccB+OHKpbBof473zEfRbdtFwPn10RfAFj\\/xikW0r\\/XxgG\\/c8tz9bmALBStGqmwOVOLRHxjwgtGu4poeuwmFfG6TuwgCadxpllW74mviFd4LZVSuCSni5YJnBM2HSJ8NP6g1fqI17KDXt2XO\\/7kSItubmMk+HGEXdH4qiugHYewaIf1o4XSQROC8xlRl7t\\/RaD4U58hKYkVwg0Ir7WzYzAVpG2UR4Co\\/GDG9Hct7HOYekDqVQ+sSZbwzajnVunkw==\",\"passwordFound\":true,\"userName\":\"example-user\"}\n"
+}
\ No newline at end of file