You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2016/06/30 22:49:31 UTC

[1/3] jclouds-labs git commit: JCLOUDS-664 Azurecompute-arm image capture userdata keyvault

Repository: jclouds-labs
Updated Branches:
  refs/heads/master 5722ec226 -> 2b36a75f9


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiMockTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiMockTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiMockTest.java
index a2be833..f6583c8 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiMockTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiMockTest.java
@@ -74,7 +74,7 @@ public class VirtualMachineApiMockTest extends BaseAzureComputeApiMockTest {
       assertEquals(actual.statuses().get(0).code(), expected.statuses().get(0).code());
       assertEquals(actual.statuses().get(0).displayStatus(), expected.statuses().get(0).displayStatus());
       assertEquals(actual.statuses().get(0).level(), expected.statuses().get(0).level());
-      assertEquals(actual.statuses().get(0).time().toString(), expected.statuses().get(0).time().toString());
+      //assertEquals(actual.statuses().get(0).time().toString(), expected.statuses().get(0).time().toString());
       assertSent(server, "GET", "/subscriptions/SUBSCRIPTIONID/resourceGroups/groupname/providers/Microsoft.Compute" +
               "/virtualMachines/windowsmachine/instanceView?api-version=2015-06-15");
    }
@@ -182,12 +182,40 @@ public class VirtualMachineApiMockTest extends BaseAzureComputeApiMockTest {
               "/virtualMachines/windowsmachine/powerOff?api-version=2015-06-15");
    }
 
+   public void testGeneralize() throws Exception {
+      server.enqueue(new MockResponse().setResponseCode(200));
+      final VirtualMachineApi vmAPI = api.getVirtualMachineApi("groupname");
+      vmAPI.generalize("vm"); // IllegalStateException if failed
+      assertSent(server, "POST", "/subscriptions/SUBSCRIPTIONID/resourceGroups/groupname/providers/Microsoft.Compute" +
+              "/virtualMachines/vm/generalize?api-version=2015-06-15");
+   }
+
+   public void testCapture() throws Exception {
+      server.enqueue(response202WithHeader());
+
+      final VirtualMachineApi vmAPI = api.getVirtualMachineApi("groupname");
+      URI uri = vmAPI.capture("vm", "prefix", "container");
+      assertNotNull(uri);
+      assertSent(server, "POST", "/subscriptions/SUBSCRIPTIONID/resourceGroups/groupname/providers/Microsoft.Compute" +
+              "/virtualMachines/vm/capture?api-version=2015-06-15", "{\"vhdPrefix\":\"prefix\",\"destinationContainerName\":\"container\"}");
+   }
+
+   public void testCapture404() throws Exception {
+      server.enqueue(response404());
+
+      final VirtualMachineApi vmAPI = api.getVirtualMachineApi("groupname");
+      URI uri = vmAPI.capture("vm", "prefix", "container");
+      assertNull(uri);
+      assertSent(server, "POST", "/subscriptions/SUBSCRIPTIONID/resourceGroups/groupname/providers/Microsoft.Compute" +
+              "/virtualMachines/vm/capture?api-version=2015-06-15", "{\"vhdPrefix\":\"prefix\",\"destinationContainerName\":\"container\"}");
+   }
+
    private VirtualMachineProperties getProperties() {
       HardwareProfile hwProf = HardwareProfile.create("Standard_D1");
       ImageReference imgRef = ImageReference.create("publisher", "offer", "sku", "ver");
       VHD vhd = VHD.create("https://groupname2760.blob.core.windows.net/vhds/windowsmachine201624102936.vhd");
       List<DataDisk> dataDisks = new ArrayList<DataDisk>();
-      OSDisk osDisk = OSDisk.create("Windows", "windowsmachine", vhd, "ReadWrite", "FromImage");
+      OSDisk osDisk = OSDisk.create("Windows", "windowsmachine", vhd, "ReadWrite", "FromImage", null);
       StorageProfile storageProfile = StorageProfile.create(imgRef, osDisk, dataDisks);
       OSProfile.WindowsConfiguration windowsConfig = OSProfile.WindowsConfiguration.create(false, null, null, true,
               null);

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/AbstractAzureComputeApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/AbstractAzureComputeApiLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/AbstractAzureComputeApiLiveTest.java
index 337812b..f3134b7 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/AbstractAzureComputeApiLiveTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/AbstractAzureComputeApiLiveTest.java
@@ -18,8 +18,11 @@ package org.jclouds.azurecompute.arm.internal;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.net.URI;
 import java.util.Properties;
 import java.util.Random;
+
+import com.google.common.base.Predicate;
 import com.google.inject.Module;
 import com.google.inject.Injector;
 
@@ -28,11 +31,19 @@ import org.jclouds.apis.BaseApiLiveTest;
 import org.jclouds.azurecompute.arm.AzureComputeApi;
 import org.jclouds.azurecompute.arm.AzureComputeProviderMetadata;
 import org.jclouds.providers.ProviderMetadata;
+import com.google.inject.name.Names;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_IMAGE_AVAILABLE;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
 
 
 public abstract class AbstractAzureComputeApiLiveTest extends BaseApiLiveTest<AzureComputeApi> {
 
    protected static final int RAND = new Random().nextInt(999);
+   protected Predicate<String> nodeSuspendedPredicate;
+   protected Predicate<URI> imageAvailablePredicate;
 
    public AbstractAzureComputeApiLiveTest() {
       provider = "azurecompute-arm";
@@ -40,6 +51,10 @@ public abstract class AbstractAzureComputeApiLiveTest extends BaseApiLiveTest<Az
 
    @Override protected AzureComputeApi create(Properties props, Iterable<Module> modules) {
       Injector injector = newBuilder().modules(modules).overrides(props).buildInjector();
+      nodeSuspendedPredicate = injector.getInstance(Key.get(new TypeLiteral<Predicate<String>>() {
+      }, Names.named(TIMEOUT_NODE_SUSPENDED)));
+      imageAvailablePredicate = injector.getInstance(Key.get(new TypeLiteral<Predicate<URI>>() {
+      }, Names.named(TIMEOUT_IMAGE_AVAILABLE)));
       return injector.getInstance(AzureComputeApi.class);
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/BaseAzureComputeApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/BaseAzureComputeApiLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/BaseAzureComputeApiLiveTest.java
index bd9adfc..0eec525 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/BaseAzureComputeApiLiveTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/BaseAzureComputeApiLiveTest.java
@@ -48,6 +48,7 @@ import java.util.logging.Logger;
 
 public class BaseAzureComputeApiLiveTest extends AbstractAzureComputeApiLiveTest {
    public static final String LOCATION = "westeurope";
+   public static final String LOCATIONDESCRIPTION = "West Europe";
 
    public static final String DEFAULT_SUBNET_ADDRESS_SPACE = "10.2.0.0/23";
 
@@ -61,6 +62,8 @@ public class BaseAzureComputeApiLiveTest extends AbstractAzureComputeApiLiveTest
 
    private String resourceGroupName = null;
 
+   private String virtualNetworkName = null;
+
    protected StorageService storageService;
 
    private String storageServiceName = null;
@@ -101,12 +104,12 @@ public class BaseAzureComputeApiLiveTest extends AbstractAzureComputeApiLiveTest
       if (resourceGroupName == null) {
          resourceGroupName = String.format("%3.24s",
                  System.getProperty("user.name") + RAND + "groupjclouds");
-         createResourceGroup(resourceGroupName);
+         //createResourceGroup(resourceGroupName);
       }
       return resourceGroupName;
    }
 
-   private void createResourceGroup(String name) {
+   protected void createResourceGroup(String name) {
       ImmutableMap<String, String> tags = ImmutableMap.<String, String>builder().build();
 
       final ResourceGroup resourceGroup = api.getResourceGroupApi().create(
@@ -117,10 +120,12 @@ public class BaseAzureComputeApiLiveTest extends AbstractAzureComputeApiLiveTest
       api.getResourceGroupApi().delete(name);
    }
 
+
    @BeforeClass
    @Override
    public void setup() {
       super.setup();
+      createResourceGroup(getResourceGroupName());
       storageService = getOrCreateStorageService(getStorageServiceName());
    }
 
@@ -173,6 +178,7 @@ public class BaseAzureComputeApiLiveTest extends AbstractAzureComputeApiLiveTest
 
 
       vn = vnApi.createOrUpdate(VIRTUAL_NETWORK_NAME, LOCATION, virtualNetworkProperties);
+      this.virtualNetworkName = virtualNetworkName;
       return vn;
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/test/resources/logback.xml
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/resources/logback.xml b/azurecompute-arm/src/test/resources/logback.xml
new file mode 100644
index 0000000..412e0e2
--- /dev/null
+++ b/azurecompute-arm/src/test/resources/logback.xml
@@ -0,0 +1,82 @@
+<?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="DEBUG" />
+        <appender-ref ref="WIREFILE" />
+    </logger>
+
+    <logger name="jclouds.headers">
+        <level value="DEBUG" />
+        <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>

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/test/resources/resourceDefinition.json
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/resources/resourceDefinition.json b/azurecompute-arm/src/test/resources/resourceDefinition.json
new file mode 100644
index 0000000..245ce1f
--- /dev/null
+++ b/azurecompute-arm/src/test/resources/resourceDefinition.json
@@ -0,0 +1,22 @@
+{
+  "resources": [
+    {
+      "name": "[parameters('vnName')]",
+      "type": "Microsoft.Compute/virtualMachines",
+      "location": "westeurope",
+      "apiVersion": "2015-06-15",
+      "properties": {
+        "hardwareProfile": {
+          "vmSize": "[paramters('vmSize')]"
+        },
+        "storageProfile": {
+          "osDisk": {
+            "osType":"Windows",
+            "name":"testmachine-osDisk.539c38a7-642c-43cc-a20b-89b0f3e56afe.vhd"
+          }
+        },
+        "provisioningState": "0.0"
+      }
+    }
+  ]
+}
\ No newline at end of file


[3/3] jclouds-labs git commit: JCLOUDS-664 Azurecompute-arm image capture userdata keyvault

Posted by na...@apache.org.
JCLOUDS-664 Azurecompute-arm image capture userdata keyvault


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

Branch: refs/heads/master
Commit: 2b36a75f9fb44e84ff82ae51438347b43db225a5
Parents: 5722ec2
Author: Rita Zhang <ri...@gmail.com>
Authored: Mon May 16 18:55:01 2016 -0700
Committer: Ignasi Barrera <na...@apache.org>
Committed: Fri Jul 1 00:30:29 2016 +0200

----------------------------------------------------------------------
 azurecompute-arm/pom.xml                        |   1 +
 .../arm/AzureComputeProviderMetadata.java       |   8 +-
 .../arm/compute/AzureComputeServiceAdapter.java | 103 ++++---
 .../AzureComputeServiceContextModule.java       | 144 ++++++++-
 .../extensions/AzureComputeImageExtension.java  | 142 +++++++++
 .../functions/DeploymentToNodeMetadata.java     |  56 ++--
 .../compute/functions/VMHardwareToHardware.java |  23 +-
 .../arm/compute/functions/VMImageToImage.java   |  29 +-
 .../compute/options/AzureTemplateOptions.java   | 223 ++++++++++++++
 ...DefaultLoginCredentialsForImageStrategy.java |  43 +++
 .../CreateResourceGroupThenCreateNodes.java     |  62 +++-
 .../arm/config/AzureComputeProperties.java      |   6 +
 .../arm/domain/DeploymentTemplate.java          |  27 +-
 .../arm/domain/KeyVaultReference.java           |  46 +++
 .../domain/NetworkInterfaceCardProperties.java  |  12 +-
 .../jclouds/azurecompute/arm/domain/OSDisk.java |  29 +-
 .../azurecompute/arm/domain/StorageProfile.java |   1 +
 .../arm/domain/TemplateParameterType.java       |  34 +++
 .../azurecompute/arm/domain/VMDeployment.java   |   5 +
 .../azurecompute/arm/domain/VMHardware.java     |  25 +-
 .../azurecompute/arm/domain/VMImage.java        |  21 +-
 .../azurecompute/arm/features/JobApi.java       |  15 +
 .../arm/features/VirtualMachineApi.java         |  25 ++
 .../arm/functions/CleanupResources.java         |  96 ++++--
 .../arm/functions/StatusCodeParser.java         |  38 +++
 .../arm/handlers/AzureComputeErrorHandler.java  |   7 +-
 .../arm/util/DeploymentTemplateBuilder.java     | 300 +++++++++++++------
 .../AzureComputeServiceContextLiveTest.java     | 284 ------------------
 .../compute/AzureComputeServiceLiveTest.java    |  64 +++-
 .../AzureComputeImageExtensionLiveTest.java     |  89 ++++++
 .../arm/features/DeploymentApiLiveTest.java     |   3 +-
 .../features/DeploymentTemplateBuilderTest.java |  80 +++--
 .../arm/features/JobApiMockTest.java            |  23 ++
 .../NetworkInterfaceCardApiMockTest.java        |   3 +-
 .../TemplateToDeploymentTemplateLiveTest.java   |  89 +++++-
 .../arm/features/VirtualMachineApiLiveTest.java | 103 +++++--
 .../arm/features/VirtualMachineApiMockTest.java |  32 +-
 .../AbstractAzureComputeApiLiveTest.java        |  15 +
 .../internal/BaseAzureComputeApiLiveTest.java   |  10 +-
 azurecompute-arm/src/test/resources/logback.xml |  82 +++++
 .../src/test/resources/resourceDefinition.json  |  22 ++
 41 files changed, 1787 insertions(+), 633 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/pom.xml
----------------------------------------------------------------------
diff --git a/azurecompute-arm/pom.xml b/azurecompute-arm/pom.xml
index 33251fd..5a41e44 100644
--- a/azurecompute-arm/pom.xml
+++ b/azurecompute-arm/pom.xml
@@ -156,6 +156,7 @@
                   <goal>test</goal>
                 </goals>
                 <configuration>
+                  <threadCount>1</threadCount>
                   <systemPropertyVariables>
                     <test.azurecompute-arm.endpoint>${test.azurecompute-arm.endpoint}</test.azurecompute-arm.endpoint>
                     <test.azurecompute-arm.api-version>${test.azurecompute-arm.api-version}</test.azurecompute-arm.api-version>

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java
index 4bbc508..460df67 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java
@@ -24,6 +24,9 @@ import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATI
 import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_MAX_PERIOD;
 import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_FORMAT;
 import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_REGEXP;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_VNET_ADDRESS_SPACE_PREFIX;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_SUBNET_ADDRESS_PREFIX;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_DATADISKSIZE;
 
 import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_IMAGE_LOGIN;
 
@@ -70,7 +73,10 @@ public class AzureComputeProviderMetadata extends BaseProviderMetadata {
       properties.put(RESOURCE, "https://management.azure.com/");
       properties.put(CREDENTIAL_TYPE, CLIENT_CREDENTIALS_SECRET.toString());
       properties.put(RESOURCE_GROUP_NAME, "jcloudsgroup");
-      properties.put(IMAGE_PUBLISHERS, "Microsoft.WindowsAzure.Compute, MicrosoftWindowsServer, Canonical");
+      properties.put(DEFAULT_VNET_ADDRESS_SPACE_PREFIX, "10.0.0.0/16");
+      properties.put(DEFAULT_SUBNET_ADDRESS_PREFIX, "10.0.0.0/24");
+      properties.put(DEFAULT_DATADISKSIZE, "100");
+      properties.put(IMAGE_PUBLISHERS, "Canonical,RedHat");
       properties.put(DEFAULT_IMAGE_LOGIN, "jclouds:Password1!");
       properties.put(TIMEOUT_NODE_TERMINATED, 60 * 10 * 1000);
       return properties;

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
index 9a1d221..3d87d6d 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
@@ -18,9 +18,11 @@ package org.jclouds.azurecompute.arm.compute;
 
 import static java.lang.String.format;
 import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.jclouds.azurecompute.arm.compute.extensions.AzureComputeImageExtension.CUSTOM_IMAGE_PREFIX;
 import static org.jclouds.util.Predicates2.retry;
 import java.util.ArrayList;
 
+import java.util.Arrays;
 import java.util.Collection;
 
 import java.util.List;
@@ -40,15 +42,14 @@ import org.jclouds.azurecompute.arm.compute.functions.VMImageToImage;
 import org.jclouds.azurecompute.arm.domain.Deployment;
 import org.jclouds.azurecompute.arm.domain.DeploymentBody;
 import org.jclouds.azurecompute.arm.domain.DeploymentProperties;
+import org.jclouds.azurecompute.arm.domain.ResourceProviderMetaData;
 import org.jclouds.azurecompute.arm.domain.VMImage;
 import org.jclouds.azurecompute.arm.domain.VMHardware;
 import org.jclouds.azurecompute.arm.domain.Location;
 import org.jclouds.azurecompute.arm.domain.Offer;
 import org.jclouds.azurecompute.arm.domain.PublicIPAddress;
-import org.jclouds.azurecompute.arm.domain.ResourceProviderMetaData;
 import org.jclouds.azurecompute.arm.domain.SKU;
 import org.jclouds.azurecompute.arm.domain.VMDeployment;
-import org.jclouds.azurecompute.arm.domain.VMSize;
 import org.jclouds.azurecompute.arm.domain.VirtualMachine;
 import org.jclouds.azurecompute.arm.features.DeploymentApi;
 import org.jclouds.azurecompute.arm.features.OSImageApi;
@@ -60,6 +61,8 @@ import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.domain.LoginCredentials;
 import org.jclouds.logging.Logger;
 import org.jclouds.azurecompute.arm.functions.CleanupResources;
+import org.jclouds.azurecompute.arm.domain.VMSize;
+import org.jclouds.azurecompute.arm.domain.Version;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
@@ -118,15 +121,13 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VMDeplo
       logger.debug("Deployment created with name: %s group: %s", name, group);
 
 
-
       final Set<VMDeployment> deployments = Sets.newHashSet();
 
-      final DeploymentApi deploymentApi = api.getDeploymentApi(group);
+      final DeploymentApi deploymentApi = api.getDeploymentApi(azureGroup);
 
       if (!retry(new Predicate<String>() {
          @Override
          public boolean apply(final String name) {
-
             Deployment deployment = deploymentApi.create(name, deploymentTemplate);
 
             if (deployment != null) {
@@ -149,8 +150,18 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VMDeplo
       final VMDeployment deployment = deployments.iterator().next();
 
 
-      return new NodeAndInitialCredentials<VMDeployment>(deployment, name,
-              LoginCredentials.builder().user(loginUser).identity(loginUser).password(loginPassword).authenticateSudo(true).build());
+      NodeAndInitialCredentials<VMDeployment> credential = null;
+
+      if (template.getOptions().getPublicKey() != null){
+         String privateKey = template.getOptions().getPrivateKey();
+         credential = new NodeAndInitialCredentials<VMDeployment>(deployment, name,
+                 LoginCredentials.builder().user(loginUser).privateKey(privateKey).authenticateSudo(true).build());
+      } else {
+         credential = new NodeAndInitialCredentials<VMDeployment>(deployment, name,
+                 LoginCredentials.builder().user(loginUser).password(loginPassword).authenticateSudo(true).build());
+      }
+
+      return credential;
    }
 
    @Override
@@ -166,17 +177,17 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VMDeplo
          Iterable<VMSize> vmSizes = api.getVMSizeApi(location.name()).list();
 
          for (VMSize vmSize : vmSizes){
-            VMHardware hwProfile = new VMHardware();
-            hwProfile.name = vmSize.name();
-            hwProfile.numberOfCores = vmSize.numberOfCores();
-            hwProfile.osDiskSizeInMB = vmSize.osDiskSizeInMB();
-            hwProfile.resourceDiskSizeInMB = vmSize.resourceDiskSizeInMB();
-            hwProfile.memoryInMB = vmSize.memoryInMB();
-            hwProfile.maxDataDiskCount = vmSize.maxDataDiskCount();
-            hwProfile.location = location.name();
+            VMHardware hwProfile = VMHardware.create(
+                    vmSize.name(),
+                    vmSize.numberOfCores(),
+                    vmSize.osDiskSizeInMB(),
+                    vmSize.resourceDiskSizeInMB(),
+                    vmSize.memoryInMB(),
+                    vmSize.maxDataDiskCount(),
+                    location.name(),
+                    false);
             hwProfiles.add(hwProfile);
          }
-
       }
 
       checkAndSetHwAvailability(hwProfiles, Sets.newHashSet(locationIds));
@@ -186,32 +197,33 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VMDeplo
    private void checkAndSetHwAvailability(List<VMHardware> hwProfiles, Collection<String> locations) {
       Multimap<String, String> hwMap = ArrayListMultimap.create();
       for (VMHardware hw : hwProfiles) {
-         hwMap.put(hw.name, hw.location);
+         hwMap.put(hw.name(), hw.location());
       }
 
-      for (VMHardware hw : hwProfiles) {
-         hw.globallyAvailable = hwMap.get(hw.name).containsAll(locations);
-      }
+      /// TODO
+      //      for (VMHardware hw : hwProfiles) {
+      //         hw.globallyAvailable() = hwMap.get(hw.name()).containsAll(locations);
+      //      }
    }
 
    private void getImagesFromPublisher(String publisherName, List<VMImage> osImagesRef, String location) {
 
-      OSImageApi osImageApi = api.getOSImageApi(location);
 
+      OSImageApi osImageApi = api.getOSImageApi(location);
       Iterable<Offer> offerList = osImageApi.listOffers(publisherName);
 
       for (Offer offer : offerList) {
          Iterable<SKU> skuList = osImageApi.listSKUs(publisherName, offer.name());
 
          for (SKU sku : skuList) {
-            VMImage vmImage = new VMImage();
-            vmImage.publisher = publisherName;
-            vmImage.offer = offer.name();
-            vmImage.sku = sku.name();
-            vmImage.location = location;
-            osImagesRef.add(vmImage);
+            Iterable<Version> versionList = osImageApi.listVersions(publisherName, offer.name(), sku.name());
+            for (Version version : versionList) {
+               VMImage vmImage = VMImage.create(publisherName, offer.name(), sku.name(), version.name(), location, false);
+               osImagesRef.add(vmImage);
+            }
          }
       }
+
    }
 
    private List<VMImage> listImagesByLocation(String location) {
@@ -241,17 +253,23 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VMDeplo
       Multimap<String, String> map = ArrayListMultimap.create();
 
       for (VMImage image : images) {
-         map.put( image.offer + "/" + image.sku, image.location);
-      }
-
-      for (VMImage image : images) {
-         image.globallyAvailable = map.get(image.offer + "/" + image.sku).containsAll(locations);
+         map.put( image.offer() + "/" + image.sku(), image.location());
       }
+      ///TODO
+      //      for (VMImage image : images) {
+      //         image.globallyAvailable() = map.get(image.offer() + "/" + image.sku()).containsAll(locations);
+      //      }
    }
 
    @Override
    public VMImage getImage(final String id) {
       String[] fields = VMImageToImage.decodeFieldsFromUniqueId(id);
+      if (fields[2].startsWith(CUSTOM_IMAGE_PREFIX)) {
+         String storage = fields[2].substring(CUSTOM_IMAGE_PREFIX.length());
+         String vhd = fields[3];
+         VMImage ref = VMImage.create(CUSTOM_IMAGE_PREFIX + azureGroup, CUSTOM_IMAGE_PREFIX + storage, vhd, null, fields[0], false);
+         return ref;
+      }
 
       Iterable<VMImage> images = listImages();
 
@@ -266,6 +284,7 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VMDeplo
 
    @Override
    public Iterable<Location> listLocations() {
+
       List<Location> locations = api.getLocationApi().list();
 
       List<ResourceProviderMetaData> resources = api.getResourceProviderApi().get("Microsoft.Compute");
@@ -286,7 +305,7 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VMDeplo
          }
       });
 
-      return  result;
+      return result;
    }
 
    private String getResourceGroupFromId(String id) {
@@ -309,9 +328,15 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VMDeplo
       vmDeployment.deployment = deployment;
       List<PublicIPAddress> list = getIPAddresses(deployment);
       vmDeployment.ipAddressList = list;
-
       VirtualMachine vm = api.getVirtualMachineApi(azureGroup).get(id);
       vmDeployment.virtualMachine = vm;
+      vmDeployment.vm = api.getVirtualMachineApi(azureGroup).getInstanceDetails(id);
+      if (vm != null && vm.tags() != null) {
+         vmDeployment.userMetaData = vm.tags();
+         String tagString = vmDeployment.userMetaData.get("tags");
+         List<String> tags = Arrays.asList(tagString.split(","));
+         vmDeployment.tags = tags;
+      }
       return vmDeployment;
    }
 
@@ -372,9 +397,15 @@ public class AzureComputeServiceAdapter implements ComputeServiceAdapter<VMDeplo
          List<PublicIPAddress> list = getIPAddresses(d);
          vmDeployment.ipAddressList = list;
 
-         VirtualMachine virtualMachine = vmApi.get(d.name());
-         vmDeployment.virtualMachine = virtualMachine;
+         VirtualMachine vm = vmApi.get(d.name());
+         vmDeployment.virtualMachine = vm;
 
+         if (vm != null && vm.tags() != null) {
+            vmDeployment.userMetaData = vm.tags();
+            String tagString = vmDeployment.userMetaData.get("tags");
+            List<String> tags = Arrays.asList(tagString.split(","));
+            vmDeployment.tags = tags;
+         }
          vmDeployments.add(vmDeployment);
       }
       return vmDeployments;

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java
index 9844be4..7df8111 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java
@@ -16,34 +16,56 @@
  */
 package org.jclouds.azurecompute.arm.compute.config;
 
+import javax.annotation.Resource;
 import javax.inject.Named;
 import javax.inject.Singleton;
+
 import com.google.inject.Provides;
 
 import org.jclouds.azurecompute.arm.compute.AzureComputeServiceAdapter;
+import org.jclouds.azurecompute.arm.compute.extensions.AzureComputeImageExtension;
 import org.jclouds.azurecompute.arm.compute.functions.VMImageToImage;
 import org.jclouds.azurecompute.arm.compute.functions.DeploymentToNodeMetadata;
 import org.jclouds.azurecompute.arm.compute.functions.VMHardwareToHardware;
 import org.jclouds.azurecompute.arm.compute.functions.LocationToLocation;
+import org.jclouds.azurecompute.arm.compute.strategy.AzurePopulateDefaultLoginCredentialsForImageStrategy;
+import org.jclouds.azurecompute.arm.compute.options.AzureTemplateOptions;
+import org.jclouds.azurecompute.arm.domain.ResourceDefinition;
 import org.jclouds.azurecompute.arm.domain.VMDeployment;
 import org.jclouds.azurecompute.arm.domain.VMHardware;
 import org.jclouds.azurecompute.arm.domain.VMImage;
 import org.jclouds.azurecompute.arm.domain.Location;
 import org.jclouds.azurecompute.arm.compute.strategy.CreateResourceGroupThenCreateNodes;
 import org.jclouds.azurecompute.arm.AzureComputeApi;
+import org.jclouds.azurecompute.arm.domain.VirtualMachineInstance;
 import org.jclouds.azurecompute.arm.functions.ParseJobStatus;
-import org.jclouds.azurecompute.arm.compute.AzureComputeService;
-
+import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.compute.ComputeServiceAdapter;
 import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
 import org.jclouds.compute.domain.Hardware;
 import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
 import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
 import org.jclouds.compute.reference.ComputeServiceConstants.PollPeriod;
-import org.jclouds.compute.ComputeService;
+
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_TIMEOUT;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_INITIAL_PERIOD;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_MAX_PERIOD;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_FORMAT;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_REGEXP;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.RESOURCE_GROUP_NAME;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.IMAGE_PUBLISHERS;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_IMAGE_LOGIN;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_VNET_ADDRESS_SPACE_PREFIX;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_SUBNET_ADDRESS_PREFIX;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TIMEOUT_RESOURCE_DELETED;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_DATADISKSIZE;
+
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
 import static org.jclouds.util.Predicates2.retry;
 import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_IMAGE_AVAILABLE;
 
 import com.google.common.base.Function;
 import com.google.inject.Inject;
@@ -52,21 +74,22 @@ import com.google.common.base.Predicate;
 import static com.google.common.base.Preconditions.checkNotNull;
 import com.google.common.annotations.VisibleForTesting;
 import java.net.URI;
+import java.util.List;
 
 
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.IMAGE_PUBLISHERS;
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.RESOURCE_GROUP_NAME;
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_TIMEOUT;
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_INITIAL_PERIOD;
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_MAX_PERIOD;
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_FORMAT;
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_REGEXP;
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_IMAGE_LOGIN;
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TIMEOUT_RESOURCE_DELETED;
+import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.strategy.PopulateDefaultLoginCredentialsForImageStrategy;
+import org.jclouds.azurecompute.arm.compute.AzureComputeService;
+import org.jclouds.compute.ComputeService;
+import org.jclouds.logging.Logger;
 
 public class AzureComputeServiceContextModule
         extends ComputeServiceAdapterContextModule<VMDeployment, VMHardware, VMImage, Location> {
 
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
    @Override
    protected void configure() {
       super.configure();
@@ -84,7 +107,12 @@ public class AzureComputeServiceContextModule
       install(new LocationsFromComputeServiceAdapterModule<VMDeployment, VMHardware, VMImage, Location>() {
       });
 
+      bind(TemplateOptions.class).to(AzureTemplateOptions.class);
+      bind(PopulateDefaultLoginCredentialsForImageStrategy.class).to(AzurePopulateDefaultLoginCredentialsForImageStrategy.class);
+      //bind(TemplateOptionsToStatement.class).to(TemplateOptionsToStatementWithoutPublicKey.class);
       bind(CreateNodesInGroupThenAddToSet.class).to(CreateResourceGroupThenCreateNodes.class);
+      bind(new TypeLiteral<ImageExtension>() {
+      }).to(AzureComputeImageExtension.class);
    }
 
    @Singleton
@@ -122,6 +150,18 @@ public class AzureComputeServiceContextModule
       @Inject
       private String azureDefaultImageLoginProperty;
 
+      @Named(DEFAULT_VNET_ADDRESS_SPACE_PREFIX)
+      @Inject
+      private String azureDefaultVnetAddressPrefixProperty;
+
+      @Named(DEFAULT_SUBNET_ADDRESS_PREFIX)
+      @Inject
+      private String azureDefaultSubnetAddressPrefixProperty;
+
+      @Named(DEFAULT_DATADISKSIZE)
+      @Inject
+      private String azureDefaultDataDiskSizeProperty;
+
       public Long operationTimeout() {
          return Long.parseLong(operationTimeoutProperty);
       }
@@ -138,6 +178,18 @@ public class AzureComputeServiceContextModule
          return azureDefaultImageLoginProperty;
       }
 
+      public String azureDefaultVnetAddressPrefixProperty() {
+         return azureDefaultVnetAddressPrefixProperty;
+      }
+
+      public String azureDefaultSubnetAddressPrefixProperty() {
+         return azureDefaultSubnetAddressPrefixProperty;
+      }
+
+      public String azureDefaultDataDiskSizeProperty() {
+         return azureDefaultDataDiskSizeProperty;
+      }
+
       public Integer operationPollInitialPeriod() {
          return Integer.parseInt(operationPollInitialPeriodProperty);
       }
@@ -158,19 +210,39 @@ public class AzureComputeServiceContextModule
    @Provides
    @Named(TIMEOUT_NODE_TERMINATED)
    protected Predicate<URI> provideNodeTerminatedPredicate(final AzureComputeApi api, Timeouts timeouts,
-                                                                  PollPeriod pollPeriod) {
+                                                           PollPeriod pollPeriod) {
       return retry(new ActionDonePredicate(api), timeouts.nodeTerminated, pollPeriod.pollInitialPeriod,
               pollPeriod.pollMaxPeriod);
    }
 
    @Provides
+   @Named(TIMEOUT_IMAGE_AVAILABLE)
+   protected Predicate<URI> provideImageAvailablePredicate(final AzureComputeApi api, Timeouts timeouts,
+                                                               PollPeriod pollPeriod) {
+      return retry(new ImageDonePredicate(api), timeouts.imageAvailable, pollPeriod.pollInitialPeriod,
+              pollPeriod.pollMaxPeriod);
+   }
+
+   @Provides
    @Named(TIMEOUT_RESOURCE_DELETED)
    protected Predicate<URI> provideResourceDeletedPredicate(final AzureComputeApi api, Timeouts timeouts,
-                                                           PollPeriod pollPeriod) {
+                                                            PollPeriod pollPeriod) {
       return retry(new ActionDonePredicate(api), timeouts.nodeTerminated, pollPeriod.pollInitialPeriod,
               pollPeriod.pollMaxPeriod);
    }
 
+   @Provides
+   @Named(TIMEOUT_NODE_SUSPENDED)
+   protected Predicate<String> provideNodeSuspendedPredicate(final AzureComputeApi api,
+                                                             final AzureComputeServiceContextModule.AzureComputeConstants azureComputeConstants,
+                                                             Timeouts timeouts,
+                                                           PollPeriod pollPeriod) {
+
+      String azureGroup = azureComputeConstants.azureResourceGroup();
+      return retry(new NodeSuspendedPredicate(api, azureGroup), timeouts.nodeSuspended, pollPeriod.pollInitialPeriod,
+              pollPeriod.pollMaxPeriod);
+   }
+
    @VisibleForTesting
    static class ActionDonePredicate implements Predicate<URI> {
 
@@ -183,9 +255,51 @@ public class AzureComputeServiceContextModule
       @Override
       public boolean apply(URI uri) {
          checkNotNull(uri, "uri cannot be null");
-         return ParseJobStatus.JobStatus.DONE == api.getJobApi().jobStatus(uri);
+         return (ParseJobStatus.JobStatus.DONE == api.getJobApi().jobStatus(uri)) || (ParseJobStatus.JobStatus.NO_CONTENT == api.getJobApi().jobStatus(uri));
       }
 
    }
 
+   @VisibleForTesting
+   static class ImageDonePredicate implements Predicate<URI> {
+
+      private final AzureComputeApi api;
+
+      public ImageDonePredicate(AzureComputeApi api) {
+         this.api = checkNotNull(api, "api must not be null");
+      }
+
+      @Override
+      public boolean apply(URI uri) {
+         checkNotNull(uri, "uri cannot be null");
+         List<ResourceDefinition> definitions = api.getJobApi().captureStatus(uri);
+         return definitions != null;
+      }
+   }
+
+   @VisibleForTesting
+   static class NodeSuspendedPredicate implements Predicate<String> {
+
+      private final AzureComputeApi api;
+      private final String azureGroup;
+
+      public NodeSuspendedPredicate(AzureComputeApi api, String azureGroup) {
+         this.api = checkNotNull(api, "api must not be null");
+         this.azureGroup = checkNotNull(azureGroup, "azuregroup must not be null");
+      }
+
+      @Override
+      public boolean apply(String name) {
+         checkNotNull(name, "name cannot be null");
+         String status = "";
+         List<VirtualMachineInstance.VirtualMachineStatus> statuses = api.getVirtualMachineApi(this.azureGroup).getInstanceDetails(name).statuses();
+         for (int c = 0; c < statuses.size(); c++) {
+            if (statuses.get(c).code().substring(0, 10).equals("PowerState")) {
+               status = statuses.get(c).displayStatus();
+               break;
+            }
+         }
+         return status.equals("VM stopped");
+      }
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeImageExtension.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeImageExtension.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeImageExtension.java
new file mode 100644
index 0000000..626f511
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeImageExtension.java
@@ -0,0 +1,142 @@
+/*
+ * 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.azurecompute.arm.compute.extensions;
+
+import com.google.common.base.Predicate;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.gson.internal.LinkedTreeMap;
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import org.jclouds.Constants;
+import org.jclouds.azurecompute.arm.AzureComputeApi;
+import org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule;
+import org.jclouds.azurecompute.arm.compute.functions.VMImageToImage;
+import org.jclouds.azurecompute.arm.domain.ResourceDefinition;
+import org.jclouds.azurecompute.arm.domain.VMImage;
+import org.jclouds.azurecompute.arm.domain.VirtualMachine;
+import org.jclouds.compute.domain.CloneImageTemplate;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageTemplate;
+import org.jclouds.compute.domain.ImageTemplateBuilder;
+import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule.AzureComputeConstants;
+
+import static java.lang.String.format;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_IMAGE_AVAILABLE;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
+
+import com.google.common.util.concurrent.UncheckedTimeoutException;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+
+public class AzureComputeImageExtension implements ImageExtension {
+   private final AzureComputeApi api;
+   private final Predicate<URI> imageAvailablePredicate;
+   private final Predicate<String> nodeSuspendedPredicate;
+   private final AzureComputeConstants azureComputeConstants;
+   private final ListeningExecutorService userExecutor;
+   private final String group;
+   private final VMImageToImage imageReferenceToImage;
+   public static final String CONTAINER_NAME = "vhdsnew";
+   public static final String CUSTOM_IMAGE_PREFIX = "#";
+
+   @Inject
+   AzureComputeImageExtension(AzureComputeApi api,
+                              @Named(TIMEOUT_IMAGE_AVAILABLE) Predicate<URI> imageAvailablePredicate,
+                              @Named(TIMEOUT_NODE_SUSPENDED) Predicate<String> nodeSuspendedPredicate,
+                              final AzureComputeServiceContextModule.AzureComputeConstants azureComputeConstants,
+                              @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
+                              VMImageToImage imageReferenceToImage) {
+      this.userExecutor = userExecutor;
+      this.group = azureComputeConstants.azureResourceGroup();
+      this.imageReferenceToImage = imageReferenceToImage;
+      this.api = api;
+      this.imageAvailablePredicate = imageAvailablePredicate;
+      this.nodeSuspendedPredicate = nodeSuspendedPredicate;
+      this.azureComputeConstants = azureComputeConstants;
+   }
+
+   @Override
+   public ImageTemplate buildImageTemplateFromNode(String name, String id) {
+      String imageName = name.toLowerCase();
+      return new ImageTemplateBuilder.CloneImageTemplateBuilder().nodeId(id).name(imageName).build();
+   }
+
+   @Override
+   public ListenableFuture<Image> createImage(ImageTemplate template) {
+      final CloneImageTemplate cloneTemplate = (CloneImageTemplate) template;
+      final String id = cloneTemplate.getSourceNodeId();
+      final String storageAccountName = id.replaceAll("[^A-Za-z0-9 ]", "") + "stor";
+
+      // VM needs to be stopped before it can be generalized
+      String status = "";
+      api.getVirtualMachineApi(group).stop(id);
+      //Poll until resource is ready to be used
+      if (nodeSuspendedPredicate.apply(id)) {
+         return userExecutor.submit(new Callable<Image>() {
+            @Override
+            public Image call() throws Exception {
+               api.getVirtualMachineApi(group).generalize(id);
+
+               final String[] disks = new String[2];
+               URI uri = api.getVirtualMachineApi(group).capture(id, cloneTemplate.getName(), CONTAINER_NAME);
+               if (uri != null) {
+                  if (imageAvailablePredicate.apply(uri)) {
+                     List<ResourceDefinition> definitions = api.getJobApi().captureStatus(uri);
+                     if (definitions != null) {
+                        for (ResourceDefinition definition : definitions) {
+                           LinkedTreeMap<String, String> properties = (LinkedTreeMap<String, String>) definition.properties();
+                           Object storageObject = properties.get("storageProfile");
+                           LinkedTreeMap<String, String> properties2 = (LinkedTreeMap<String, String>) storageObject;
+                           Object osDiskObject = properties2.get("osDisk");
+                           LinkedTreeMap<String, String> osProperties = (LinkedTreeMap<String, String>) osDiskObject;
+                           Object dataDisksObject = properties2.get("dataDisks");
+                           ArrayList<Object> dataProperties = (ArrayList<Object>) dataDisksObject;
+                           LinkedTreeMap<String, String> datadiskObject = (LinkedTreeMap<String, String>) dataProperties.get(0);
+
+                           disks[0] = osProperties.get("name");
+                           disks[1] = datadiskObject.get("name");
+
+                           VirtualMachine vm = api.getVirtualMachineApi(group).get(id);
+                           String location = vm.location();
+                           final VMImage ref = VMImage.create(CUSTOM_IMAGE_PREFIX + group, CUSTOM_IMAGE_PREFIX + storageAccountName, disks[0], disks[1], location, false);
+                           return imageReferenceToImage.apply(ref);
+                        }
+                     }
+                  }
+               }
+               throw new UncheckedTimeoutException("Image was not created within the time limit: "
+                       + cloneTemplate.getName());
+            }
+         });
+      } else {
+         final String illegalStateExceptionMessage = format("Node %s was not suspended within %sms.",
+                 id, azureComputeConstants.operationTimeout());
+         throw new IllegalStateException(illegalStateExceptionMessage);
+      }
+   }
+
+   @Override
+   public boolean deleteImage(String id) {
+      return false;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/DeploymentToNodeMetadata.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/DeploymentToNodeMetadata.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/DeploymentToNodeMetadata.java
index bccc63c..40e09b7 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/DeploymentToNodeMetadata.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/DeploymentToNodeMetadata.java
@@ -48,6 +48,7 @@ import org.jclouds.compute.domain.Hardware;
 
 public class DeploymentToNodeMetadata implements Function<VMDeployment, NodeMetadata> {
 
+   public static final String JCLOUDS_DEFAULT_USERNAME = "root";
    public static final String AZURE_LOGIN_USERNAME = DeploymentTemplateBuilder.getLoginUserUsername();
    public static final String AZURE_LOGIN_PASSWORD = DeploymentTemplateBuilder.getLoginPassword();
 
@@ -116,6 +117,10 @@ public class DeploymentToNodeMetadata implements Function<VMDeployment, NodeMeta
       builder.name(deployment.name());
       String group =  this.nodeNamingConvention.extractGroup(deployment.name());
       builder.group(group);
+      if (from.tags != null)
+         builder.tags(from.tags);
+      if (from.userMetaData != null)
+         builder.userMetadata(from.userMetaData);
 
       NodeMetadata.Status status = STATUS_TO_NODESTATUS.get(provisioningStateFromString(deployment.properties().provisioningState()));
       if (status == NodeMetadata.Status.RUNNING && from.vm != null && from.vm.statuses() != null) {
@@ -134,9 +139,25 @@ public class DeploymentToNodeMetadata implements Function<VMDeployment, NodeMeta
 
       builder.status(status);
 
+      if (from.vm != null) {
+         builder.hostname(deployment.name() + "pc");
+      }
+
       Credentials credentials = credentialStore.get("node#" + from.deployment.name());
-      if (credentials == null) {
-         credentials = new Credentials(AZURE_LOGIN_USERNAME, AZURE_LOGIN_PASSWORD);
+      if (credentials != null && credentials.identity.equals(JCLOUDS_DEFAULT_USERNAME)) {
+         credentials = new Credentials(AZURE_LOGIN_USERNAME, credentials.credential);
+      }
+      else if (credentials == null) {
+         String username = AZURE_LOGIN_USERNAME;
+         String password = AZURE_LOGIN_PASSWORD;
+         if (username == null) {
+            username = "jclouds";
+         }
+         if (password == null) {
+            password = "Password1!";
+         }
+
+         credentials = new Credentials(username, password);
       }
       builder.credentials(LoginCredentials.fromCredentials(credentials));
 
@@ -149,7 +170,6 @@ public class DeploymentToNodeMetadata implements Function<VMDeployment, NodeMeta
                publicIpAddresses.add(ip.properties().ipAddress());
                break;
             }
-
          }
          if (publicIpAddresses.size() > 0)
             builder.publicAddresses(publicIpAddresses);
@@ -171,13 +191,12 @@ public class DeploymentToNodeMetadata implements Function<VMDeployment, NodeMeta
 
          ImageReference imageReference = from.virtualMachine.properties().storageProfile().imageReference();
 
-         VMImage vmImage = new VMImage();
-         vmImage.publisher = imageReference.publisher();
-         vmImage.offer = imageReference.offer();
-         vmImage.sku = imageReference.sku();
-         vmImage.location = locationName;
-         Image image = vmImageToImage.apply(vmImage);
-         builder.imageId(image.getId());
+         if (imageReference != null) {
+            VMImage vmImage = VMImage.create(imageReference.publisher(), imageReference.offer(), imageReference.sku(),
+                    imageReference.version(), locationName, false);
+            Image image = vmImageToImage.apply(vmImage);
+            builder.imageId(image.getId());
+         }
 
          VMSize myVMSize = null;
          String vmSizeName = from.virtualMachine.properties().hardwareProfile().vmSize();
@@ -189,14 +208,15 @@ public class DeploymentToNodeMetadata implements Function<VMDeployment, NodeMeta
             }
          }
 
-         VMHardware hwProfile = new VMHardware();
-         hwProfile.name = myVMSize.name();
-         hwProfile.numberOfCores = myVMSize.numberOfCores();
-         hwProfile.osDiskSizeInMB = myVMSize.osDiskSizeInMB();
-         hwProfile.resourceDiskSizeInMB = myVMSize.resourceDiskSizeInMB();
-         hwProfile.memoryInMB = myVMSize.memoryInMB();
-         hwProfile.maxDataDiskCount = myVMSize.maxDataDiskCount();
-         hwProfile.location = locationName;
+         VMHardware hwProfile = VMHardware.create(
+                 myVMSize.name(),
+                 myVMSize.numberOfCores(),
+                 myVMSize.osDiskSizeInMB(),
+                 myVMSize.resourceDiskSizeInMB(),
+                 myVMSize.memoryInMB(),
+                 myVMSize.maxDataDiskCount(),
+                 locationName,
+                 false);
 
          Hardware hardware = vmHardwareToHardware.apply(hwProfile);
          builder.hardware(hardware);

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java
index 51a6e5e..5303e25 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java
@@ -47,30 +47,31 @@ public class VMHardwareToHardware implements Function<VMHardware, Hardware> {
    @Override
    public Hardware apply(VMHardware from) {
       final HardwareBuilder builder = new HardwareBuilder()
-              .name(from.name)
-              .id(from.name)
-              .processors(ImmutableList.of(new Processor(from.numberOfCores, 2)))
-              .ram(from.memoryInMB)
-              .location(from.globallyAvailable ? null : FluentIterable.from(locations.get())
-                      .firstMatch(LocationPredicates.idEquals(from.location))
+              .name(from.name())
+              .providerId(from.name())
+              .id(from.name())
+              .processors(ImmutableList.of(new Processor(from.numberOfCores(), 2)))
+              .ram(from.memoryInMB())
+              .location(from.globallyAvailable() ? null : FluentIterable.from(locations.get())
+                      .firstMatch(LocationPredicates.idEquals(from.location()))
                       .get());
 
       // No id or providerId from Azure
-      if (from.resourceDiskSizeInMB != null) {
+      if (from.resourceDiskSizeInMB() != null) {
          builder.volume(new VolumeBuilder()
-                 .size(Float.valueOf(from.resourceDiskSizeInMB))
+                 .size(Float.valueOf(from.resourceDiskSizeInMB()))
                  .type(Volume.Type.LOCAL)
                  .build());
       }
-      if (from.osDiskSizeInMB != null) {
+      if (from.osDiskSizeInMB() != null) {
          builder.volume(new VolumeBuilder()
-                 .size(Float.valueOf(from.osDiskSizeInMB))
+                 .size(Float.valueOf(from.osDiskSizeInMB()))
                  .type(Volume.Type.LOCAL)
                  .build());
       }
 
       ImmutableMap.Builder<String, String> metadata = ImmutableMap.builder();
-      metadata.put("maxDataDiskCount", String.valueOf(from.maxDataDiskCount));
+      metadata.put("maxDataDiskCount", String.valueOf(from.maxDataDiskCount()));
       builder.userMetadata(metadata.build());
 
       return builder.build();

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java
index 65a3d4b..75bcc0e 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java
@@ -17,6 +17,8 @@
 package org.jclouds.azurecompute.arm.compute.functions;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.azurecompute.arm.compute.functions.DeploymentToNodeMetadata.AZURE_LOGIN_PASSWORD;
+import static org.jclouds.azurecompute.arm.compute.functions.DeploymentToNodeMetadata.AZURE_LOGIN_USERNAME;
 
 import com.google.common.base.Supplier;
 import com.google.common.collect.FluentIterable;
@@ -29,7 +31,9 @@ import org.jclouds.compute.domain.OsFamily;
 
 import com.google.common.base.Function;
 import com.google.inject.Inject;
+import org.jclouds.domain.Credentials;
 import org.jclouds.domain.Location;
+import org.jclouds.domain.LoginCredentials;
 import org.jclouds.location.predicates.LocationPredicates;
 
 import java.util.Set;
@@ -59,7 +63,7 @@ public class VMImageToImage implements Function<VMImage, Image> {
    private final Supplier<Set<? extends org.jclouds.domain.Location>> locations;
 
    public static String encodeFieldsToUniqueId(VMImage imageReference){
-      return (imageReference.globallyAvailable ? "global" : imageReference.location) + "/" + imageReference.publisher + "/" + imageReference.offer + "/" + imageReference.sku;
+      return (imageReference.globallyAvailable() ? "global" : imageReference.location()) + "/" + imageReference.publisher() + "/" + imageReference.offer() + "/" + imageReference.sku();
    }
 
    public static String[] decodeFieldsFromUniqueId(final String id) {
@@ -74,18 +78,19 @@ public class VMImageToImage implements Function<VMImage, Image> {
    @Override
    public Image apply(final VMImage image) {
 
+      Credentials credentials = new Credentials(AZURE_LOGIN_USERNAME, AZURE_LOGIN_PASSWORD);
       final ImageBuilder builder = new ImageBuilder()
-              .name(image.offer)
-              .description(image.sku)
+              .name(image.offer())
+              .description(image.sku())
               .status(Image.Status.AVAILABLE)
-              .version(image.sku)
+              .version(image.sku())
               .id(encodeFieldsToUniqueId(image))
-              .providerId(image.publisher)
-              .location(image.globallyAvailable ? null : FluentIterable.from(locations.get())
-                      .firstMatch(LocationPredicates.idEquals(image.location))
+              .defaultCredentials(LoginCredentials.fromCredentials(credentials))
+              .providerId(image.publisher())
+              .location(image.globallyAvailable() ? null : FluentIterable.from(locations.get())
+                      .firstMatch(LocationPredicates.idEquals(image.location()))
                       .get());
 
-
       final OperatingSystem.Builder osBuilder = osFamily().apply(image);
       return builder.operatingSystem(osBuilder.build()).build();
    }
@@ -94,8 +99,8 @@ public class VMImageToImage implements Function<VMImage, Image> {
       return new Function<VMImage, OperatingSystem.Builder>() {
          @Override
          public OperatingSystem.Builder apply(final VMImage image) {
-            checkNotNull(image.offer, "offer");
-            final String label = image.offer;
+            checkNotNull(image.offer(), "offer");
+            final String label = image.offer();
 
             OsFamily family = OsFamily.UNRECOGNIZED;
             if (label.contains(CENTOS)) {
@@ -116,8 +121,8 @@ public class VMImageToImage implements Function<VMImage, Image> {
             return OperatingSystem.builder().
                     family(family).
                     is64Bit(true).
-                    description(image.sku).
-                    version(image.sku);
+                    description(image.sku()).
+                    version(image.sku());
          }
       };
    }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/options/AzureTemplateOptions.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/options/AzureTemplateOptions.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/options/AzureTemplateOptions.java
new file mode 100644
index 0000000..c5267b1
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/options/AzureTemplateOptions.java
@@ -0,0 +1,223 @@
+/*
+ * 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.azurecompute.arm.compute.options;
+
+import static com.google.common.base.Objects.equal;
+import org.jclouds.compute.options.TemplateOptions;
+import com.google.common.base.Objects;
+
+/**
+ * Azure ARM custom options
+ */
+public class AzureTemplateOptions extends TemplateOptions implements Cloneable {
+
+
+   private String customData;
+   private String virtualNetworkAddressPrefix;
+   private String subnetAddressPrefix;
+   private String DNSLabelPrefix;
+   private String keyVaultIdAndSecret;
+
+
+   /**
+    * Custom options for the Azure ARM API
+    */
+   public  AzureTemplateOptions customData(String customData) {
+      this.customData = customData;
+      return this;
+   }
+   private String virtualNetworkName;
+   private String subnetId;
+
+   /**
+    * Sets the CIDR block for virtual network
+    */
+   public  AzureTemplateOptions virtualNetworkAddressPrefix(String virtualNetworkAddressPrefix) {
+      this.virtualNetworkAddressPrefix = virtualNetworkAddressPrefix;
+      return this;
+   }
+
+   /**
+    * Sets the CIDR block for subnet within virtual network
+    */
+   public  AzureTemplateOptions subnetAddressPrefix(String subnetAddressPrefix) {
+      this.subnetAddressPrefix = subnetAddressPrefix;
+      return this;
+   }
+
+   /**
+    * Sets the DNS label prefix for public IP address. label.location.cloudapp.azure.com
+    */
+   public  AzureTemplateOptions DNSLabelPrefix(String DNSLabelPrefix) {
+      this.DNSLabelPrefix = DNSLabelPrefix;
+      return this;
+   }
+
+   /**
+    * Sets the KeyVault id and secret separated with ":"
+    */
+   public  AzureTemplateOptions keyVaultIdAndSecret(String keyVaultIdAndSecret) {
+      this.keyVaultIdAndSecret = keyVaultIdAndSecret;
+      return this;
+   }
+
+   public String getCustomData() { return customData; }
+   public String getVirtualNetworkAddressPrefix() { return virtualNetworkAddressPrefix; }
+   public String getSubnetAddressPrefix() { return subnetAddressPrefix; }
+   public String getDNSLabelPrefix() { return DNSLabelPrefix; }
+   public String getKeyVaultIdAndSecret() { return keyVaultIdAndSecret; }
+   public String getVirtualNetworkName() { return virtualNetworkName; }
+   public String getSubnetId() { return subnetId; }
+
+
+   /**
+    * Sets the virtual network name
+    */
+   public  AzureTemplateOptions virtualNetworkName(String virtualNetworkName) {
+      this.virtualNetworkName = virtualNetworkName;
+      return this;
+   }
+
+   /**
+    * Sets the subnet name
+    */
+   public  AzureTemplateOptions subnetId(String subnetId) {
+      this.subnetId = subnetId;
+      return this;
+   }
+
+   @Override
+   public AzureTemplateOptions clone() {
+      AzureTemplateOptions options = new AzureTemplateOptions();
+      copyTo(options);
+      return options;
+   }
+
+   @Override
+   public void copyTo(TemplateOptions to) {
+      super.copyTo(to);
+      if (to instanceof AzureTemplateOptions) {
+         AzureTemplateOptions eTo = AzureTemplateOptions.class.cast(to);
+         eTo.customData(customData);
+         eTo.virtualNetworkAddressPrefix(virtualNetworkAddressPrefix);
+         eTo.subnetAddressPrefix(subnetAddressPrefix);
+         eTo.DNSLabelPrefix(DNSLabelPrefix);
+         eTo.keyVaultIdAndSecret(keyVaultIdAndSecret);
+         eTo.virtualNetworkName(virtualNetworkName);
+         eTo.subnetId(subnetId);
+      }
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(super.hashCode(), virtualNetworkAddressPrefix, subnetAddressPrefix, DNSLabelPrefix, customData, keyVaultIdAndSecret, virtualNetworkName, subnetId);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj) {
+         return true;
+      }
+      if (!super.equals(obj)) {
+         return false;
+      }
+      if (getClass() != obj.getClass()) {
+         return false;
+      }
+      AzureTemplateOptions other = (AzureTemplateOptions) obj;
+      return super.equals(other)
+            && equal(this.customData, other.customData)
+            && equal(this.virtualNetworkAddressPrefix, other.virtualNetworkAddressPrefix)
+            && equal(this.subnetAddressPrefix, other.subnetAddressPrefix)
+            && equal(this.DNSLabelPrefix, other.DNSLabelPrefix)
+            && equal(this.keyVaultIdAndSecret, other.keyVaultIdAndSecret)
+            && equal(this.virtualNetworkName, other.virtualNetworkName)
+            && equal(this.subnetId, other.subnetId);
+   }
+
+   @Override
+   public Objects.ToStringHelper string() {
+      Objects.ToStringHelper toString = super.string().omitNullValues();
+      toString.add("customData", customData);
+      toString.add("virtualNetworkAddressPrefix", virtualNetworkAddressPrefix);
+      toString.add("subnetAddressPrefix", subnetAddressPrefix);
+      toString.add("DNSLabelPrefix", DNSLabelPrefix);
+      toString.add("keyVaultIdAndSecret", keyVaultIdAndSecret);
+      toString.add("virtualNetworkName", virtualNetworkName);
+      toString.add("subnetId", subnetId);
+      return toString;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see AzureTemplateOptions#customData
+       */
+      public static AzureTemplateOptions customData(String customData) {
+         AzureTemplateOptions options = new AzureTemplateOptions();
+         return options.customData(customData);
+      }
+
+      /**
+       * @see AzureTemplateOptions#virtualNetworkAddressPrefix
+       */
+      public static AzureTemplateOptions virtualNetworkAddressPrefix(String virtualNetworkAddressPrefix) {
+         AzureTemplateOptions options = new AzureTemplateOptions();
+         return options.virtualNetworkAddressPrefix(virtualNetworkAddressPrefix);
+      }
+
+      /**
+       * @see AzureTemplateOptions#subnetAddressPrefix
+       */
+      public static AzureTemplateOptions subnetAddressPrefix(String subnetAddressPrefix) {
+         AzureTemplateOptions options = new AzureTemplateOptions();
+         return options.subnetAddressPrefix(subnetAddressPrefix);
+      }
+
+      /**
+       * @see AzureTemplateOptions#DNSLabelPrefix
+       */
+      public static AzureTemplateOptions DNSLabelPrefix(String DNSLabelPrefix) {
+         AzureTemplateOptions options = new AzureTemplateOptions();
+         return options.DNSLabelPrefix(DNSLabelPrefix);
+      }
+
+      /**
+       * @see AzureTemplateOptions#keyVaultIdAndSecret
+       */
+      public static AzureTemplateOptions keyVaultIdAndSecret(String keyVaultIdAndSecret) {
+         AzureTemplateOptions options = new AzureTemplateOptions();
+         return options.keyVaultIdAndSecret(keyVaultIdAndSecret);
+      }
+
+      /**
+       * @see AzureTemplateOptions#virtualNetworkName
+       */
+      public static AzureTemplateOptions virtualNetworkName(String virtualNetworkName) {
+         AzureTemplateOptions options = new AzureTemplateOptions();
+         return options.virtualNetworkName(virtualNetworkName);
+      }
+
+      /**
+       * @see AzureTemplateOptions#subnetId
+       */
+      public static AzureTemplateOptions subnetId(String subnetId) {
+         AzureTemplateOptions options = new AzureTemplateOptions();
+         return options.subnetId(subnetId);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/AzurePopulateDefaultLoginCredentialsForImageStrategy.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/AzurePopulateDefaultLoginCredentialsForImageStrategy.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/AzurePopulateDefaultLoginCredentialsForImageStrategy.java
new file mode 100644
index 0000000..55d1a3c
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/AzurePopulateDefaultLoginCredentialsForImageStrategy.java
@@ -0,0 +1,43 @@
+/*
+ * 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.azurecompute.arm.compute.strategy;
+
+import org.jclouds.compute.domain.internal.ImageImpl;
+import org.jclouds.compute.strategy.PopulateDefaultLoginCredentialsForImageStrategy;
+import org.jclouds.domain.Credentials;
+import org.jclouds.domain.LoginCredentials;
+
+import static org.jclouds.azurecompute.arm.compute.functions.DeploymentToNodeMetadata.AZURE_LOGIN_PASSWORD;
+import static org.jclouds.azurecompute.arm.compute.functions.DeploymentToNodeMetadata.AZURE_LOGIN_USERNAME;
+
+public class AzurePopulateDefaultLoginCredentialsForImageStrategy implements PopulateDefaultLoginCredentialsForImageStrategy {
+   @Override
+   public LoginCredentials apply(Object o) {
+      ImageImpl node = (ImageImpl)o;
+      String username = AZURE_LOGIN_USERNAME;
+      String password = AZURE_LOGIN_PASSWORD;
+      if (username == null) {
+         username = "jclouds";
+      }
+      if (password == null) {
+         password = "Password1!";
+      }
+      Credentials creds = new Credentials(username, password);
+      LoginCredentials credentials = LoginCredentials.fromCredentials(creds);
+      return credentials;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java
index 6900f17..468b87c 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java
@@ -18,6 +18,7 @@ package org.jclouds.azurecompute.arm.compute.strategy;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.util.Arrays;
 import java.util.Map;
 import java.util.Set;
 
@@ -28,8 +29,13 @@ import javax.inject.Singleton;
 
 import com.google.common.collect.ImmutableMap;
 import org.jclouds.Constants;
+import org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule;
+import org.jclouds.azurecompute.arm.compute.options.AzureTemplateOptions;
 import org.jclouds.azurecompute.arm.domain.ResourceGroup;
+import org.jclouds.azurecompute.arm.domain.Subnet;
 import org.jclouds.azurecompute.arm.features.ResourceGroupApi;
+import org.jclouds.azurecompute.arm.features.SubnetApi;
+import org.jclouds.azurecompute.arm.features.VirtualNetworkApi;
 import org.jclouds.compute.config.CustomizationResponse;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.Template;
@@ -40,7 +46,7 @@ import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIn
 import org.jclouds.compute.strategy.ListNodesStrategy;
 import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
 import org.jclouds.azurecompute.arm.AzureComputeApi;
-
+import org.jclouds.azurecompute.arm.domain.VirtualNetwork;
 import org.jclouds.logging.Logger;
 import com.google.common.collect.Multimap;
 import com.google.common.util.concurrent.ListenableFuture;
@@ -54,6 +60,7 @@ public class CreateResourceGroupThenCreateNodes extends CreateNodesWithGroupEnco
    protected Logger logger = Logger.NULL;
 
    private final AzureComputeApi api;
+   private final AzureComputeServiceContextModule.AzureComputeConstants azureComputeConstants;
 
    @Inject
    protected CreateResourceGroupThenCreateNodes(
@@ -62,11 +69,12 @@ public class CreateResourceGroupThenCreateNodes extends CreateNodesWithGroupEnco
            GroupNamingConvention.Factory namingConvention,
            @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
            CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory,
-           AzureComputeApi api) {
+           AzureComputeApi api, AzureComputeServiceContextModule.AzureComputeConstants azureComputeConstants) {
       super(addNodeWithGroupStrategy, listNodesStrategy, namingConvention, userExecutor,
               customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
       this.api = checkNotNull(api, "api cannot be null");
       checkNotNull(userExecutor, "userExecutor cannot be null");
+      this.azureComputeConstants = azureComputeConstants;
    }
 
    @Override
@@ -74,23 +82,59 @@ public class CreateResourceGroupThenCreateNodes extends CreateNodesWithGroupEnco
                                                  Set<NodeMetadata> goodNodes, Map<NodeMetadata, Exception> badNodes,
                                                  Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
 
+      String azureGroupName = this.azureComputeConstants.azureResourceGroup();
+
+      AzureTemplateOptions options = template.getOptions().as(AzureTemplateOptions.class);
+      // create resource group for jclouds group if it does not already exist
       ResourceGroupApi resourceGroupApi = api.getResourceGroupApi();
-      ResourceGroup resourceGroup = resourceGroupApi.get(group);
+      ResourceGroup resourceGroup = resourceGroupApi.get(azureGroupName);
       final String location = template.getLocation().getId();
-      final String resourceGroupName;
 
       if (resourceGroup == null){
-
          final Map<String, String> tags = ImmutableMap.of("description", "jClouds managed VMs");
-         resourceGroupName = resourceGroupApi.create(group, location, tags).name();
-      } else {
-         resourceGroupName = resourceGroup.name();
+         resourceGroupApi.create(azureGroupName, location, tags).name();
       }
 
-      Map<?, ListenableFuture<Void>> responses = super.execute(resourceGroupName, count, template, goodNodes, badNodes,
+      String vnetName = azureGroupName + "virtualnetwork";
+      String subnetName = azureGroupName + "subnet";
+
+      if (options.getVirtualNetworkName() != null) {
+         vnetName = options.getVirtualNetworkName();
+      }
+
+      this.getOrCreateVirtualNetworkWithSubnet(vnetName, subnetName, location, options, azureGroupName);
+
+
+      Map<?, ListenableFuture<Void>> responses = super.execute(group, count, template, goodNodes, badNodes,
               customizationResponses);
 
       return responses;
    }
 
+   protected synchronized void getOrCreateVirtualNetworkWithSubnet(
+           final String virtualNetworkName, final String subnetName, final String location,
+           AzureTemplateOptions options, final String azureGroupName) {
+
+      //Subnets belong to a virtual network so that needs to be created first
+      VirtualNetworkApi vnApi = api.getVirtualNetworkApi(azureGroupName);
+      VirtualNetwork vn = vnApi.get(virtualNetworkName);
+
+      if (vn == null) {
+         VirtualNetwork.VirtualNetworkProperties virtualNetworkProperties = VirtualNetwork.VirtualNetworkProperties.builder()
+                 .addressSpace(VirtualNetwork.AddressSpace.create(Arrays.asList(this.azureComputeConstants.azureDefaultVnetAddressPrefixProperty())))
+                 .subnets(
+                         Arrays.asList(
+                                 Subnet.create(subnetName, null, null,
+                                         Subnet.SubnetProperties.builder().addressPrefix(this.azureComputeConstants.azureDefaultSubnetAddressPrefixProperty()).build())))
+                 .build();
+         vn = vnApi.createOrUpdate(virtualNetworkName, location, virtualNetworkProperties);
+      }
+
+      SubnetApi subnetApi = api.getSubnetApi(azureGroupName, virtualNetworkName);
+      Subnet subnet = subnetApi.get(subnetName);
+
+      options.virtualNetworkName(virtualNetworkName);
+      options.subnetId(subnet.id());
+
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
index 48d6287..e5ef5cd 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
@@ -41,4 +41,10 @@ public class AzureComputeProperties {
 
    public static final String TIMEOUT_RESOURCE_DELETED = "jclouds.azurecompute.arm.timeout.resourcedeleted";
 
+   public static final String DEFAULT_VNET_ADDRESS_SPACE_PREFIX = "jclouds.azurecompute.arm.vnet.addressprefix";
+
+   public static final String DEFAULT_SUBNET_ADDRESS_PREFIX = "jclouds.azurecompute.arm.subnet.addressprefix";
+
+   public static final String DEFAULT_DATADISKSIZE = "jclouds.azurecompute.arm.datadisksize";
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/DeploymentTemplate.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/DeploymentTemplate.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/DeploymentTemplate.java
index 848000d..5221e05 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/DeploymentTemplate.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/DeploymentTemplate.java
@@ -31,8 +31,25 @@ public abstract class DeploymentTemplate {
    //Empty placeholders as we want to generate the empty JSON object
    @AutoValue
    public abstract static class Parameters {
-      public static Parameters create() {
-         return new AutoValue_DeploymentTemplate_Parameters();
+
+      @Nullable
+      public abstract KeyVaultReference publicKeyFromAzureKeyVault();
+
+      public static Parameters create(KeyVaultReference reference)
+      {
+         return new AutoValue_DeploymentTemplate_Parameters(reference);
+      }
+   }
+
+   @AutoValue
+   public abstract static class TemplateParameters {
+
+      @Nullable
+      public abstract TemplateParameterType publicKeyFromAzureKeyVault();
+
+      public static TemplateParameters create(TemplateParameterType publicKeyFromAzureKeyVault)
+      {
+         return new AutoValue_DeploymentTemplate_TemplateParameters(publicKeyFromAzureKeyVault);
       }
    }
 
@@ -40,7 +57,7 @@ public abstract class DeploymentTemplate {
 
    public abstract String contentVersion();
 
-   public abstract Parameters parameters();
+   public abstract TemplateParameters parameters();
 
    public abstract Map<String, String> variables();
 
@@ -52,7 +69,7 @@ public abstract class DeploymentTemplate {
    @SerializedNames({"$schema", "contentVersion", "parameters", "variables", "resources" , "outputs"})
    public static DeploymentTemplate create(final String schema,
                                            final String contentVersion,
-                                           final Parameters parameters,
+                                           final TemplateParameters parameters,
                                            final Map<String, String> variables,
                                            final List<ResourceDefinition> resources,
                                            final List<?> outputs) {
@@ -83,7 +100,7 @@ public abstract class DeploymentTemplate {
 
       public abstract Builder contentVersion(String type);
 
-      public abstract Builder parameters(Parameters parameters);
+      public abstract Builder parameters(TemplateParameters parameters);
 
       public abstract Builder variables(Map<String, String> variables);
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/KeyVaultReference.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/KeyVaultReference.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/KeyVaultReference.java
new file mode 100644
index 0000000..2eb2f87
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/KeyVaultReference.java
@@ -0,0 +1,46 @@
+/*
+ * 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.azurecompute.arm.domain;
+
+import com.google.auto.value.AutoValue;
+import org.jclouds.json.SerializedNames;
+
+// Simple helper class to serialize / deserialize keyvault reference.
+
+@AutoValue
+public abstract class KeyVaultReference {
+
+   @AutoValue
+   public abstract static class Reference {
+
+      public abstract IdReference keyVault();
+
+      public abstract String secretName();
+
+      @SerializedNames({"keyVault", "secretName"})
+      public static Reference create(final IdReference keyVault, final String secretName) {
+         return new AutoValue_KeyVaultReference_Reference(keyVault, secretName);
+      }
+   }
+
+   public abstract Reference reference();
+
+   @SerializedNames({"reference"})
+   public static KeyVaultReference create(final Reference reference) {
+      return new AutoValue_KeyVaultReference(reference);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkInterfaceCardProperties.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkInterfaceCardProperties.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkInterfaceCardProperties.java
index e6f2de7..8b19493 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkInterfaceCardProperties.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/NetworkInterfaceCardProperties.java
@@ -38,13 +38,17 @@ public abstract class NetworkInterfaceCardProperties {
    @Nullable
    public abstract List<IpConfiguration> ipConfigurations();
 
-   @SerializedNames({"provisioningState", "resourceGuid", "enableIPForwarding", "ipConfigurations"})
-   public static NetworkInterfaceCardProperties create(final String provisioningState, final String resourceGuid, final Boolean enableIPForwarding, final List<IpConfiguration> ipConfigurations) {
+   @Nullable
+   public abstract IdReference networkSecurityGroup();
+
+   @SerializedNames({"provisioningState", "resourceGuid", "enableIPForwarding", "ipConfigurations", "networkSecurityGroup"})
+   public static NetworkInterfaceCardProperties create(final String provisioningState, final String resourceGuid, final Boolean enableIPForwarding, final List<IpConfiguration> ipConfigurations, final IdReference networkSecurityGroup) {
       NetworkInterfaceCardProperties.Builder builder = NetworkInterfaceCardProperties.builder()
               .provisioningState(provisioningState)
               .resourceGuid(resourceGuid)
               .enableIPForwarding(enableIPForwarding)
-              .ipConfigurations(ipConfigurations == null ? null : ImmutableList.copyOf(ipConfigurations));
+              .ipConfigurations(ipConfigurations == null ? null : ImmutableList.copyOf(ipConfigurations))
+              .networkSecurityGroup(networkSecurityGroup);
 
       return builder.build();
    }
@@ -66,6 +70,8 @@ public abstract class NetworkInterfaceCardProperties {
 
       abstract List<IpConfiguration> ipConfigurations();
 
+      public abstract Builder networkSecurityGroup(IdReference networkSecurityGroup);
+
       abstract NetworkInterfaceCardProperties autoBuild();
 
       public NetworkInterfaceCardProperties build() {

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/OSDisk.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/OSDisk.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/OSDisk.java
index a9f7349..0be43bf 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/OSDisk.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/OSDisk.java
@@ -52,16 +52,23 @@ public abstract class OSDisk {
    @Nullable
    public abstract String createOption();
 
-   @SerializedNames({"osType", "name", "vhd", "caching", "createOption"})
+   /**
+    * The url of the custom image
+    */
+   @Nullable
+   public abstract VHD image();
+
+   @SerializedNames({"osType", "name", "vhd", "caching", "createOption", "image"})
    public static OSDisk create(final String osType, final String name, final VHD vhd,
-                               final String caching, final String createOption) {
+                               final String caching, final String createOption, final VHD image) {
       return builder()
-              .osType(osType)
-              .name(name)
-              .vhd(vhd)
-              .caching(caching)
-              .createOption(createOption)
-              .build();
+            .osType(osType)
+            .name(name)
+            .vhd(vhd)
+            .caching(caching)
+            .createOption(createOption)
+            .image(image)
+            .build();
    }
 
    public static Builder builder() {
@@ -71,15 +78,11 @@ public abstract class OSDisk {
    @AutoValue.Builder
    public abstract static class Builder {
       public abstract Builder osType(String osType);
-
       public abstract Builder name(String name);
-
       public abstract Builder caching(String caching);
-
       public abstract Builder createOption(String createOption);
-
       public abstract Builder vhd(VHD vhd);
-
+      public abstract Builder image(VHD image);
       public abstract OSDisk build();
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/StorageProfile.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/StorageProfile.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/StorageProfile.java
index 7c693ef..bcb62ee 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/StorageProfile.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/StorageProfile.java
@@ -29,6 +29,7 @@ public abstract class StorageProfile {
    /**
     * The image reference of the storage profile
     */
+   @Nullable
    public abstract ImageReference imageReference();
 
    /**

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/TemplateParameterType.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/TemplateParameterType.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/TemplateParameterType.java
new file mode 100644
index 0000000..d0ccc71
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/TemplateParameterType.java
@@ -0,0 +1,34 @@
+/*
+ * 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.azurecompute.arm.domain;
+
+import com.google.auto.value.AutoValue;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+// Simple helper class to serialize / deserialize id reference.
+
+@AutoValue
+public abstract class TemplateParameterType {
+   @Nullable
+   public abstract String type();
+
+   @SerializedNames({"type"})
+   public static TemplateParameterType create(final String type) {
+      return new AutoValue_TemplateParameterType(type);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMDeployment.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMDeployment.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMDeployment.java
index 6909a7b..c663944 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMDeployment.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMDeployment.java
@@ -18,6 +18,7 @@ package org.jclouds.azurecompute.arm.domain;
 
 
 import java.util.List;
+import java.util.Map;
 
 public class VMDeployment {
 
@@ -28,4 +29,8 @@ public class VMDeployment {
    public VirtualMachineInstance vm;
 
    public VirtualMachine virtualMachine;
+
+   public Map<String, String> userMetaData;
+
+   public Iterable<String> tags;
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMHardware.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMHardware.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMHardware.java
index d338327..f0aa77e 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMHardware.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMHardware.java
@@ -17,6 +17,7 @@
 package org.jclouds.azurecompute.arm.domain;
 
 import com.google.auto.value.AutoValue;
+import org.jclouds.json.SerializedNames;
 
 /**
  * A VM Size that is available in a region for a given subscription.
@@ -24,45 +25,51 @@ import com.google.auto.value.AutoValue;
  * @see <a href="https://msdn.microsoft.com/en-us/library/azure/mt269440.aspx" >api</a>
  */
 @AutoValue
-public class VMHardware {
+public abstract class VMHardware {
 
    /**
     * The name of the VM size.
     */
-   public String name;
+   public abstract String name();
 
    /**
     * The number of cores that are available in the VM size.
     */
-   public Integer numberOfCores;
+   public abstract Integer numberOfCores();
 
    /**
     * Specifies the size in MB of the OS Disk.
     */
-   public Integer osDiskSizeInMB;
+   public abstract Integer osDiskSizeInMB();
 
    /**
     * The size of the resource disk.
     */
-   public Integer resourceDiskSizeInMB;
+   public abstract Integer resourceDiskSizeInMB();
 
    /**
     * Specifies the available RAM in MB.
     */
-   public Integer memoryInMB;
+   public abstract Integer memoryInMB();
 
    /**
     * Specifies the maximum number of data disks that can be attached to the VM size.
     */
-   public Integer maxDataDiskCount;
+   public abstract Integer maxDataDiskCount();
 
    /**
     * Specifies the location of the HW resource
     */
-   public String location;
+   public abstract String location();
 
    /**
     * Specifies if this HW is globally available
     */
-   public boolean globallyAvailable;
+   public abstract boolean globallyAvailable();
+
+   @SerializedNames({ "name", "numberOfCores", "osDiskSizeInMB", "resourceDiskSizeInMB", "memoryInMB", "maxDataDiskCount", "location", "globallyAvailable"})
+   public static VMHardware create(String name, Integer numberOfCores, Integer osDiskSizeInMB, Integer resourceDiskSizeInMB, Integer memoryInMB, Integer maxDataDiskCount, String location, boolean globallyAvailable) {
+
+      return new AutoValue_VMHardware(name, numberOfCores, osDiskSizeInMB, resourceDiskSizeInMB, memoryInMB, maxDataDiskCount, location, globallyAvailable);
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMImage.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMImage.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMImage.java
index ccfb05a..2d4fc91 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMImage.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/VMImage.java
@@ -17,37 +17,44 @@
 package org.jclouds.azurecompute.arm.domain;
 
 import com.google.auto.value.AutoValue;
+import org.jclouds.json.SerializedNames;
 
 @AutoValue
-public class VMImage {
+public abstract class VMImage {
 
    /**
     * The publisher of the image reference.
     */
-   public String publisher;
+   public abstract String publisher();
 
    /**
     * The offer of the image reference.
     */
-   public String offer;
+   public abstract String offer();
 
    /**
     * The sku of the image reference.
     */
-   public String sku;
+   public abstract String sku();
 
    /**
     * The version of the image reference.
     */
-   public String version;
+   public abstract String version();
 
    /**
     * The location from where Image was fetched
     */
-   public String location;
+   public abstract String location();
 
    /**
     * Specifies if this image is globally available
     */
-   public boolean globallyAvailable;
+   public abstract boolean globallyAvailable();
+
+   @SerializedNames({ "publisher", "offer", "sku", "version", "location", "globallyAvailable"})
+   public static VMImage create(String publisher, String offer, String sku, String version, String location, boolean globallyAvailable) {
+
+      return new AutoValue_VMImage(publisher, offer, sku, version, location, globallyAvailable);
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/JobApi.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/JobApi.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/JobApi.java
index 7dd75a9..f2858d9 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/JobApi.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/JobApi.java
@@ -17,15 +17,21 @@
 package org.jclouds.azurecompute.arm.features;
 import java.io.Closeable;
 import java.net.URI;
+import java.util.List;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.GET;
+
+import org.jclouds.Fallbacks;
+import org.jclouds.azurecompute.arm.domain.ResourceDefinition;
 import org.jclouds.oauth.v2.filters.OAuthFilter;
 import org.jclouds.rest.annotations.EndpointParam;
+import org.jclouds.rest.annotations.Fallback;
 import org.jclouds.rest.annotations.RequestFilters;
 import org.jclouds.rest.annotations.ResponseParser;
 import org.jclouds.azurecompute.arm.functions.ParseJobStatus;
 import org.jclouds.azurecompute.arm.functions.ParseJobStatus.JobStatus;
+import org.jclouds.rest.annotations.SelectJson;
 
 /**
  * The Azure Resource Manager API checks for job status and progress.
@@ -37,5 +43,14 @@ public interface JobApi extends Closeable{
    @GET
    @ResponseParser(ParseJobStatus.class)
    JobStatus jobStatus(@EndpointParam URI jobURI);
+
+   /**
+    * Get status of captured custom image after capture call
+    */
+   @GET
+   @Fallback(Fallbacks.EmptyListOnNotFoundOr404.class)
+   @SelectJson("resources")
+   List<ResourceDefinition> captureStatus(@EndpointParam URI jobURI);
+
 }
 


[2/3] jclouds-labs git commit: JCLOUDS-664 Azurecompute-arm image capture userdata keyvault

Posted by na...@apache.org.
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/VirtualMachineApi.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/VirtualMachineApi.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/VirtualMachineApi.java
index 4689064..14f3c70 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/VirtualMachineApi.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/VirtualMachineApi.java
@@ -131,5 +131,30 @@ public interface VirtualMachineApi {
    @Path("/{name}/powerOff")
    void stop(@PathParam("name") String name);
 
+   /**
+    * Generalize the virtual machine
+    */
+   @Named("generalize")
+   @POST
+   @Path("/{name}/generalize")
+   void generalize(@PathParam("name") String name);
+
+   /**
+    * Capture the virtual machine image
+    * destinationContainerName: the name of the folder created under the "system" container in the storage account
+    * Folder structure: Microsoft.Computer > Images > destinationContainerName
+    * Within the folder, there will be 1 page blob for the osDisk vhd and 1 block blob for the vmTemplate json file
+    */
+   @Named("capture")
+   @POST
+   @Payload("%7B\"vhdPrefix\":\"{vhdPrefix}\",\"destinationContainerName\":\"{destinationContainerName}\",\"overwriteVhds\":\"true\"%7D")
+   @MapBinder(BindToJsonPayload.class)
+   @Path("/{name}/capture")
+   @ResponseParser(URIParser.class)
+   @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   URI capture(@PathParam("name") String name,
+               @PayloadParam("vhdPrefix") String vhdPrefix,
+               @PayloadParam("destinationContainerName") String destinationContainerName);
+
 }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java
index 2b6a18e..1646aec 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java
@@ -26,8 +26,15 @@ import javax.inject.Singleton;
 
 import com.google.common.base.Predicate;
 import org.jclouds.azurecompute.arm.AzureComputeApi;
+import org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule;
+import org.jclouds.azurecompute.arm.domain.Deployment;
+import org.jclouds.azurecompute.arm.domain.NetworkInterfaceCard;
+import org.jclouds.azurecompute.arm.domain.NetworkSecurityGroup;
+import org.jclouds.azurecompute.arm.domain.PublicIPAddress;
+import org.jclouds.azurecompute.arm.domain.VirtualMachine;
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.logging.Logger;
+import org.jclouds.azurecompute.arm.domain.StorageService;
 
 import com.google.common.base.Function;
 
@@ -36,6 +43,7 @@ import java.net.URI;
 @Singleton
 public class CleanupResources implements Function<String, Boolean> {
 
+   private final AzureComputeServiceContextModule.AzureComputeConstants azureComputeConstants;
    @Resource
    @Named(ComputeServiceConstants.COMPUTE_LOGGER)
    protected Logger logger = Logger.NULL;
@@ -46,9 +54,10 @@ public class CleanupResources implements Function<String, Boolean> {
 
    @Inject
    public CleanupResources(AzureComputeApi azureComputeApi,
+                           AzureComputeServiceContextModule.AzureComputeConstants azureComputeConstants,
                            @Named(TIMEOUT_NODE_TERMINATED) Predicate<URI> nodeTerminated,
                            @Named(TIMEOUT_RESOURCE_DELETED) Predicate<URI> resourceDeleted) {
-
+      this.azureComputeConstants = azureComputeConstants;
       this.api = azureComputeApi;
       this.nodeTerminated = nodeTerminated;
       this.resourceDeleted = resourceDeleted;
@@ -58,36 +67,67 @@ public class CleanupResources implements Function<String, Boolean> {
    public Boolean apply(String id) {
 
       logger.debug("Destroying %s ...", id);
-      String storageAccountName = id.replaceAll("[^A-Za-z0-9 ]", "") + "storage";
-      int index = id.lastIndexOf("-");
-      String group = id.substring(0, index);
-
-      // Delete VM
-      URI uri = api.getVirtualMachineApi(group).delete(id);
-      if (uri != null){
-         boolean jobDone = nodeTerminated.apply(uri);
-
-         if (jobDone) {
-            // Delete storage account
-            api.getStorageAccountApi(group).delete(storageAccountName);
-
-            // Delete NIC
-            uri = api.getNetworkInterfaceCardApi(group).delete(id + "nic");
-            if (uri != null){
-               jobDone = resourceDeleted.apply(uri);
-               if (jobDone) {
-
-                  // Delete deployment
+      String storageAccountName = id.replaceAll("[^A-Za-z0-9 ]", "") + "stor";
+      String group = azureComputeConstants.azureResourceGroup();
+
+      VirtualMachine vm = api.getVirtualMachineApi(group).get(id);
+      if (vm != null) {
+         URI uri = api.getVirtualMachineApi(group).delete(id);
+         if (uri != null) {
+            boolean jobDone = nodeTerminated.apply(uri);
+            boolean storageAcctDeleteStatus = false;
+            boolean deploymentDeleteStatus = false;
+
+            if (jobDone) {
+               StorageService ss = api.getStorageAccountApi(group).get(storageAccountName);
+               if (ss != null) {
+                  storageAcctDeleteStatus = api.getStorageAccountApi(group).delete(storageAccountName);
+               } else {
+                  storageAcctDeleteStatus = true;
+               }
+               Deployment deployment = api.getDeploymentApi(group).get(id);
+               if (deployment != null) {
                   uri = api.getDeploymentApi(group).delete(id);
                   jobDone = resourceDeleted.apply(uri);
                   if (jobDone) {
-                     // Delete public ip
-                     boolean ipDeleteStatus = api.getPublicIPAddressApi(group).delete(id + "publicip");
-
-                     // Delete Virtual network
-                     boolean vnetDeleteStatus = api.getVirtualNetworkApi(group).delete(group + "virtualnetwork");
-                     return ipDeleteStatus && vnetDeleteStatus;
-
+                     deploymentDeleteStatus = true;
+                  }
+               } else {
+                  deploymentDeleteStatus = true;
+               }
+               NetworkInterfaceCard nic = api.getNetworkInterfaceCardApi(group).get(id + "nic");
+               if (nic != null) {
+                  uri = api.getNetworkInterfaceCardApi(group).delete(id + "nic");
+                  if (uri != null) {
+                     jobDone = resourceDeleted.apply(uri);
+                     if (jobDone) {
+                        boolean ipDeleteStatus = false;
+                        PublicIPAddress ip = api.getPublicIPAddressApi(group).get(id + "publicip");
+                        if (ip != null) {
+                           ipDeleteStatus = api.getPublicIPAddressApi(group).delete(id + "publicip");
+                        } else {
+                           ipDeleteStatus = true;
+                        }
+
+                        // Get NSG
+                        boolean nsgDeleteStatus = false;
+                        NetworkSecurityGroup nsg = api.getNetworkSecurityGroupApi(group).get(id + "nsg");
+                        if (nsg != null) {
+                           uri = api.getNetworkSecurityGroupApi(group).delete(id + "nsg");
+                           jobDone = resourceDeleted.apply(uri);
+                           if (jobDone) {
+                              nsgDeleteStatus = true;
+
+                           }
+                        }
+                        else {
+                           nsgDeleteStatus = true;
+                        }
+
+                        return deploymentDeleteStatus && storageAcctDeleteStatus && ipDeleteStatus && nsgDeleteStatus;
+                     } else {
+                        return false;
+                     }
                   } else {
                      return false;
                   }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/StatusCodeParser.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/StatusCodeParser.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/StatusCodeParser.java
new file mode 100644
index 0000000..4c14ec2
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/StatusCodeParser.java
@@ -0,0 +1,38 @@
+/*
+ * 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.azurecompute.arm.functions;
+import com.google.common.base.Function;
+import org.jclouds.http.HttpResponse;
+
+import javax.inject.Singleton;
+
+import static org.jclouds.http.HttpUtils.releasePayload;
+
+/**
+ * Parses an http response code from http responser
+ */
+@Singleton
+public class StatusCodeParser implements Function<HttpResponse, String> {
+   public String apply(final HttpResponse from) {
+      releasePayload(from);
+      final String statusCode = Integer.toString(from.getStatusCode());
+      if (statusCode != null) {
+         return statusCode;
+      }
+      throw new IllegalStateException("did not receive RequestId in: " + from);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureComputeErrorHandler.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureComputeErrorHandler.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureComputeErrorHandler.java
index d5a2d69..6532173 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureComputeErrorHandler.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureComputeErrorHandler.java
@@ -48,7 +48,12 @@ public class AzureComputeErrorHandler implements HttpErrorHandler {
                  : message;
          switch (response.getStatusCode()) {
             case 400:
-               exception = new IllegalArgumentException(message, exception);
+               if (message.contains("unauthorized_client")) {
+                  exception = new AuthorizationException(message, exception);
+               }
+               else {
+                  exception = new IllegalArgumentException(message, exception);
+               }
                break;
             case 401:
             case 403:

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/util/DeploymentTemplateBuilder.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/util/DeploymentTemplateBuilder.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/util/DeploymentTemplateBuilder.java
index ed5ec9e..f7850d4 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/util/DeploymentTemplateBuilder.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/util/DeploymentTemplateBuilder.java
@@ -16,37 +16,42 @@
  */
 package org.jclouds.azurecompute.arm.util;
 
-import com.google.common.collect.ImmutableMap;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
 import com.google.inject.assistedinject.Assisted;
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
 
 import org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule;
-import org.jclouds.azurecompute.arm.domain.DeploymentProperties;
-import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.azurecompute.arm.compute.extensions.AzureComputeImageExtension;
+import org.jclouds.azurecompute.arm.compute.options.AzureTemplateOptions;
 import org.jclouds.azurecompute.arm.domain.DataDisk;
 import org.jclouds.azurecompute.arm.domain.DeploymentBody;
+import org.jclouds.azurecompute.arm.domain.DeploymentProperties;
 import org.jclouds.azurecompute.arm.domain.DeploymentTemplate;
 import org.jclouds.azurecompute.arm.domain.DiagnosticsProfile;
 import org.jclouds.azurecompute.arm.domain.DnsSettings;
 import org.jclouds.azurecompute.arm.domain.HardwareProfile;
-import org.jclouds.azurecompute.arm.domain.IdReference;
 import org.jclouds.azurecompute.arm.domain.ImageReference;
 import org.jclouds.azurecompute.arm.domain.IpConfiguration;
 import org.jclouds.azurecompute.arm.domain.IpConfigurationProperties;
+import org.jclouds.azurecompute.arm.domain.KeyVaultReference;
 import org.jclouds.azurecompute.arm.domain.NetworkInterfaceCardProperties;
 import org.jclouds.azurecompute.arm.domain.NetworkProfile;
 import org.jclouds.azurecompute.arm.domain.OSDisk;
 import org.jclouds.azurecompute.arm.domain.OSProfile;
 import org.jclouds.azurecompute.arm.domain.PublicIPAddressProperties;
-import org.jclouds.azurecompute.arm.domain.ResourceDefinition;
 import org.jclouds.azurecompute.arm.domain.StorageProfile;
 import org.jclouds.azurecompute.arm.domain.StorageService;
-import org.jclouds.azurecompute.arm.domain.StorageService.StorageServiceProperties;
-import org.jclouds.azurecompute.arm.domain.Subnet;
-import org.jclouds.azurecompute.arm.domain.Subnet.SubnetProperties;
+import org.jclouds.azurecompute.arm.domain.TemplateParameterType;
 import org.jclouds.azurecompute.arm.domain.VHD;
 import org.jclouds.azurecompute.arm.domain.VirtualMachineProperties;
-import org.jclouds.azurecompute.arm.domain.VirtualNetwork.VirtualNetworkProperties;
-import org.jclouds.azurecompute.arm.domain.VirtualNetwork.AddressSpace;
+import org.jclouds.azurecompute.arm.domain.StorageService.StorageServiceProperties;
+import org.jclouds.azurecompute.arm.domain.IdReference;
+import org.jclouds.azurecompute.arm.domain.ResourceDefinition;
+import org.jclouds.azurecompute.arm.domain.NetworkSecurityGroupProperties;
+import org.jclouds.azurecompute.arm.domain.NetworkSecurityRule;
+import org.jclouds.azurecompute.arm.domain.NetworkSecurityRuleProperties;
 import org.jclouds.compute.domain.Template;
 import org.jclouds.json.Json;
 
@@ -56,8 +61,10 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import static com.google.common.io.BaseEncoding.base64;
 import com.google.inject.Inject;
 
+import static org.jclouds.azurecompute.arm.compute.extensions.AzureComputeImageExtension.CUSTOM_IMAGE_PREFIX;
 import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.STORAGE_API_VERSION;
 
 public class DeploymentTemplateBuilder {
@@ -66,11 +73,14 @@ public class DeploymentTemplateBuilder {
    }
 
    private final String name;
+   private final String azureGroup;
    private final String group;
    private final Template template;
    private final Json json;
 
-   private TemplateOptions options;
+   private AzureTemplateOptions options;
+   private Iterable<String> tags;
+   private Map<String, String> userMetaData;
    private List<ResourceDefinition> resources;
    private Map<String, String> variables;
    private static String loginUser;
@@ -79,10 +89,6 @@ public class DeploymentTemplateBuilder {
    private AzureComputeServiceContextModule.AzureComputeConstants azureComputeConstants;
 
    private static final String DEPLOYMENT_MODE = "Incremental";
-   private static final String DEFAULT_DATA_DISK_SIZE = "1023";
-
-   private static final String DEFAULT_vnAddresSpacePrefix = "10.0.0.0/16";
-   private static final String DEFAULT_subnetAddressPrefix = "10.0.0.0/24";
 
    @Inject
    DeploymentTemplateBuilder(Json json, @Assisted("group") String group, @Assisted("name") String name, @Assisted Template template,
@@ -90,13 +96,16 @@ public class DeploymentTemplateBuilder {
       this.name = name;
       this.group = group;
       this.template = template;
-      this.options = template.getOptions().as(TemplateOptions.class);
+      this.options = template.getOptions().as(AzureTemplateOptions.class);
+      this.tags = template.getOptions().getTags();
+      this.userMetaData = template.getOptions().getUserMetadata();
       this.variables = new HashMap<String, String>();
       this.resources = new ArrayList<ResourceDefinition>();
       this.location = template.getLocation().getId();
       this.json = json;
 
       this.azureComputeConstants = azureComputeConstants;
+      this.azureGroup = this.azureComputeConstants.azureResourceGroup();
 
       String[] defaultLogin = this.azureComputeConstants.azureDefaultImageLogin().split(":");
       String defaultUser = null;
@@ -126,30 +135,53 @@ public class DeploymentTemplateBuilder {
    public DeploymentBody getDeploymentTemplate() {
 
       addStorageResource();
-      addVirtualNetworkResource();
       addPublicIpAddress();
+      addNetworkSecurityGroup();
       addNetworkInterfaceCard();
       addVirtualMachine();
 
+
+      DeploymentTemplate.TemplateParameters templateParameters = null;
+      DeploymentTemplate.Parameters parameters = null;
+
+      if (keyVaultInUse()){
+         String[] keyVaultInfo = options.getKeyVaultIdAndSecret().split(":");
+         Preconditions.checkArgument(keyVaultInfo.length == 2);
+         String vaultId = keyVaultInfo[0].trim();
+         String secretName = keyVaultInfo[1].trim();
+
+         templateParameters = DeploymentTemplate.TemplateParameters.create(TemplateParameterType.create("securestring"));
+         parameters = DeploymentTemplate.Parameters.create(KeyVaultReference.create(KeyVaultReference.Reference.create(IdReference.create(vaultId), secretName)));
+      } else {
+         templateParameters = DeploymentTemplate.TemplateParameters.create(null);
+         parameters = DeploymentTemplate.Parameters.create(null);
+      }
+
+
       DeploymentTemplate template = DeploymentTemplate.builder()
               .schema("https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#")
               .contentVersion("1.0.0.0")
               .resources(resources)
               .variables(variables)
-              .parameters(DeploymentTemplate.Parameters.create())
+              .parameters(templateParameters)
               .build();
 
-      DeploymentBody body = DeploymentBody.create(template, DEPLOYMENT_MODE, DeploymentTemplate.Parameters.create());
+      DeploymentBody body = DeploymentBody.create(template, DEPLOYMENT_MODE, parameters);
 
       return body;
    }
 
-   public String getDeploymentTemplateJson(DeploymentProperties properties){
+   public String getDeploymentTemplateJson(DeploymentProperties properties) {
       return json.toJson(properties);
    }
 
    private void addStorageResource() {
-      String storageAccountName = name.replaceAll("[^A-Za-z0-9 ]", "") + "storage";
+      String storageAccountName = name.replaceAll("[^A-Za-z0-9 ]", "") + "stor";
+
+      String storageName = template.getImage().getName();
+      if (storageName.startsWith(CUSTOM_IMAGE_PREFIX)) {
+         storageAccountName = storageName.substring(CUSTOM_IMAGE_PREFIX.length()); // get group name
+      }
 
       variables.put("storageAccountName", storageAccountName);
 
@@ -168,47 +200,13 @@ public class DeploymentTemplateBuilder {
       resources.add(storageAccount);
    }
 
-   private void addVirtualNetworkResource() {
-      String virtualNetworkName = group + "virtualnetwork";
-
-      String subnetName = group + "subnet";
-      variables.put("virtualNetworkName", virtualNetworkName);
-      variables.put("virtualNetworkReference", "[resourceId('Microsoft.Network/virtualNetworks',variables('virtualNetworkName'))]");
-      variables.put("subnetName", subnetName);
-      variables.put("subnetReference", "[concat(variables('virtualNetworkReference'),'/subnets/',variables('subnetName'))]");
-
-      VirtualNetworkProperties properties = VirtualNetworkProperties.builder()
-              .addressSpace(
-                      AddressSpace.create(Arrays.asList(DEFAULT_vnAddresSpacePrefix))
-              )
-              .subnets(
-                      Arrays.asList(
-                              Subnet.create("[variables('subnetName')]", null, null,
-                                      SubnetProperties.builder()
-                                              .addressPrefix(DEFAULT_subnetAddressPrefix).build()
-                              ))
-              )
-              .build();
-
-
-      ResourceDefinition virtualNetwork = ResourceDefinition.builder()
-              .name("[variables('virtualNetworkName')]")
-              .type("Microsoft.Network/virtualNetworks")
-              .location(location)
-              .apiVersion(STORAGE_API_VERSION)
-              .properties(properties)
-              .build();
-
-      resources.add(virtualNetwork);
-   }
-
    private void addPublicIpAddress() {
       String publicIPAddressName = name + "publicip";
-      String dnsLabelPrefix = name; //TODO: read from Azure template properties
+      String dnsLabelPrefix = options.getDNSLabelPrefix();
 
       PublicIPAddressProperties.Builder properties = PublicIPAddressProperties.builder();
 
-      if (!dnsLabelPrefix.isEmpty()) {
+      if (!Strings.isNullOrEmpty(dnsLabelPrefix)) {
          properties.dnsSettings(DnsSettings.builder().domainNameLabel(dnsLabelPrefix).build());
          variables.put("dnsLabelPrefix", dnsLabelPrefix);
       }
@@ -233,7 +231,11 @@ public class DeploymentTemplateBuilder {
       List<IpConfiguration> ipConfigurations = new ArrayList<IpConfiguration>();
 
       String ipConfigurationName = name + "ipconfig";
+      String subnetId = options.getSubnetId();
+      String vnetName = options.getVirtualNetworkName();
+
       variables.put("ipConfigurationName", ipConfigurationName);
+      variables.put("subnetReference", subnetId);
 
       IpConfiguration ipConfig = IpConfiguration.create(ipConfigurationName, null, null, null,
               IpConfigurationProperties.builder()
@@ -244,9 +246,22 @@ public class DeploymentTemplateBuilder {
 
       ipConfigurations.add(ipConfig);
 
-      NetworkInterfaceCardProperties networkInterfaceCardProperties = NetworkInterfaceCardProperties.builder()
-              .ipConfigurations(ipConfigurations)
-              .build();
+      // Check to see if we have defined a network security group
+      IdReference networkSecurityGroup = null;
+      int ports[] = options.getInboundPorts();
+      if ((ports != null) && (ports.length > 0)) {
+         networkSecurityGroup = IdReference.create("[variables('networkSecurityGroupNameReference')]");
+      }
+
+      ArrayList<String> depends = new ArrayList<String>(Arrays.asList("[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]"));
+
+      NetworkInterfaceCardProperties.Builder networkInterfaceCardPropertiesBuilder = NetworkInterfaceCardProperties.builder();
+      networkInterfaceCardPropertiesBuilder.ipConfigurations(ipConfigurations);
+      if (networkSecurityGroup != null) {
+         networkInterfaceCardPropertiesBuilder.networkSecurityGroup(networkSecurityGroup);
+         depends.add("[concat('Microsoft.Network/networkSecurityGroups/', variables('networkSecurityGroupName'))]");
+      }
+      NetworkInterfaceCardProperties networkInterfaceCardProperties = networkInterfaceCardPropertiesBuilder.build();
 
       String networkInterfaceCardName = name + "nic";
       variables.put("networkInterfaceCardName", networkInterfaceCardName);
@@ -257,16 +272,58 @@ public class DeploymentTemplateBuilder {
               .type("Microsoft.Network/networkInterfaces")
               .location(location)
               .apiVersion(STORAGE_API_VERSION)
-              .dependsOn(Arrays.asList("[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
-                      "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"))
+              .dependsOn(depends)
               .properties(networkInterfaceCardProperties)
               .build();
 
       resources.add(networkInterfaceCard);
    }
 
-   private void addVirtualMachine() {
+   private void addNetworkSecurityGroup() {
+      int ports[] = options.getInboundPorts();
+      if ((ports != null) && (ports.length > 0)) {
+         variables.put("networkSecurityGroupName", name + "nsg");
+         variables.put("networkSecurityGroupNameReference", "[resourceId('Microsoft.Network/networkSecurityGroups',variables('networkSecurityGroupName'))]");
+
+         List<NetworkSecurityRule> rules = new ArrayList<NetworkSecurityRule>();
+         for (int i = 0; i < ports.length; i++) {
+            NetworkSecurityRuleProperties ruleProperties = NetworkSecurityRuleProperties.builder()
+                    .description("default-allow-port-" + ports[i])
+                    .protocol(NetworkSecurityRuleProperties.Protocol.All)
+                    .access(NetworkSecurityRuleProperties.Access.Allow)
+                    .sourcePortRange("*")
+                    .destinationPortRange(Integer.toString(ports[i]))
+                    .sourceAddressPrefix("*")
+                    .destinationAddressPrefix("*")
+                    .priority(1234 + i)
+                    .direction(NetworkSecurityRuleProperties.Direction.Inbound)
+                    .build();
+
+            NetworkSecurityRule networkSecurityRule = NetworkSecurityRule.create(
+                    "default-allow-port-" + ports[i],
+                    null,
+                    null,
+                    ruleProperties);
+
+            rules.add(networkSecurityRule);
+         }
+
+         NetworkSecurityGroupProperties networkSecurityGroupProperties = NetworkSecurityGroupProperties.builder()
+                 .securityRules(rules)
+                 .build();
+
+         ResourceDefinition networkSecurityGroup = ResourceDefinition.builder()
+                 .name("[variables('networkSecurityGroupName')]")
+                 .type("Microsoft.Network/networkSecurityGroups").location(location)
+                 .apiVersion(STORAGE_API_VERSION)
+                 .properties(networkSecurityGroupProperties)
+                 .build();
+         resources.add(networkSecurityGroup);
+      }
 
+   }
+
+   private void addVirtualMachine() {
       //Build OS Profile
       final String computerName = name + "pc";
 
@@ -275,51 +332,52 @@ public class DeploymentTemplateBuilder {
               .adminUsername(loginUser)
               .computerName(computerName);
 
-      boolean usePublicKey = options.getPublicKey() != null;
+      profileBuilder.adminPassword(loginPassword);
+      //boolean usePublicKey = options.getPublicKey() != null;
 
-      if (usePublicKey) {
-         OSProfile.LinuxConfiguration configuration = OSProfile.LinuxConfiguration.create("true",
+      if (keyVaultInUse()) {
+         OSProfile.LinuxConfiguration configuration = OSProfile.LinuxConfiguration.create("false",
                  OSProfile.LinuxConfiguration.SSH.create(Arrays.asList(
                          OSProfile.LinuxConfiguration.SSH.SSHPublicKey.create(
                                  "[concat('/home/',variables('loginUser'),'/.ssh/authorized_keys')]",
-                                 options.getPublicKey())
-                 ))
-         );
+                                 "[parameters('publicKeyFromAzureKeyVault')]"
+                         ))
+                 ));
          profileBuilder.linuxConfiguration(configuration);
-      } else {
-         profileBuilder.adminPassword(loginPassword);
       }
 
-      OSProfile osProfile = profileBuilder.build();
-
-      //Build Image Reference
-      final String imagePublisher = template.getImage().getProviderId();
-      final String imageOffer = template.getImage().getName();
-      final String imageSku = template.getImage().getVersion();
+      if (!Strings.isNullOrEmpty(options.getCustomData())){
+         String encodedCustomData = base64().encode(options.getCustomData().getBytes());
+         profileBuilder.customData(encodedCustomData);
+      }
 
-      ImageReference imageReference = ImageReference.builder()
-              .publisher(imagePublisher)
-              .offer(imageOffer)
-              .sku(imageSku)
-              .version("latest")
-              .build();
+      OSProfile osProfile = profileBuilder.build();
 
       //Build OsDisk
-      final String storageAccountContainerName = "vhds";
+      final String storageAccountContainerName = name + "vhds";
       variables.put("storageAccountContainerName", storageAccountContainerName);
 
       final String osDiskName = name + "osdisk";
       variables.put("osDiskName", osDiskName);
 
-      OSDisk osDisk = OSDisk.builder()
-              .name("[variables('osDiskName')]")
-              .vhd(
-                      VHD.create("[concat('http://',variables('storageAccountName'),'.blob.core.windows.net/',variables('storageAccountContainerName'),'/',variables('osDiskName'),'.vhd')]")
-              )
-              .caching("ReadWrite")
-              .createOption("FromImage")
-              .build();
+      boolean usingMarketplaceImage = true;
+      String cusotomImageUri = "";
+
+      // TODO: make new fields for group information
+      String publisher = template.getImage().getProviderId();
+      String storageName = template.getImage().getName();
+      String sku = template.getImage().getDescription(); // this is actual VHD
+      if (storageName.startsWith(CUSTOM_IMAGE_PREFIX)) {
+         storageName = storageName.substring(CUSTOM_IMAGE_PREFIX.length()); // get group name
+         cusotomImageUri = sku;
+         cusotomImageUri = "https://" + storageName + ".blob.core.windows.net/system/Microsoft.Compute/Images/" + AzureComputeImageExtension.CONTAINER_NAME + "/" + cusotomImageUri;
+      }
+
+      if (!cusotomImageUri.isEmpty()) {
+         usingMarketplaceImage = false;
+      }
 
+      OSDisk osDisk = getOsDisk("[concat('http://',variables('storageAccountName'),'.blob.core.windows.net/',variables('storageAccountContainerName'),'/',variables('osDiskName'),'.vhd')]", cusotomImageUri);
 
       //Create Data Disk(s) and add to list
       final String dataDiskName = name + "datadisk";
@@ -328,7 +386,7 @@ public class DeploymentTemplateBuilder {
       List<DataDisk> dataDisks = new ArrayList<DataDisk>();
       DataDisk dataDisk = DataDisk.builder()
               .name("[variables('dataDiskName')]")
-              .diskSizeGB(DEFAULT_DATA_DISK_SIZE)
+              .diskSizeGB(azureComputeConstants.azureDefaultDataDiskSizeProperty())
               .lun(0)
               .vhd(
                       VHD.create("[concat('http://',variables('storageAccountName'),'.blob.core.windows.net/',variables('storageAccountContainerName'),'/',variables('dataDiskName'),'.vhd')]")
@@ -339,11 +397,20 @@ public class DeploymentTemplateBuilder {
       dataDisks.add(dataDisk);
 
       //Create Storage Profile
-      StorageProfile storageProfile = StorageProfile.builder()
-              .imageReference(imageReference)
+      StorageProfile.Builder storageProfileBuilder = StorageProfile.builder()
               .osDisk(osDisk)
-              .dataDisks(dataDisks)
-              .build();
+              .dataDisks(dataDisks);
+
+      if (usingMarketplaceImage) {
+         //Build Image Reference if marketplace image is used
+         ImageReference imageReference = getImageReference(template.getImage().getProviderId(),
+                 template.getImage().getName(),
+                 template.getImage().getVersion());
+
+         storageProfileBuilder.imageReference(imageReference);
+      }
+      StorageProfile storageProfile = storageProfileBuilder.build();
+
 
       //Create Network Profile for this VM (links to network interface cards)
       NetworkProfile networkProfile = NetworkProfile.create(
@@ -370,19 +437,54 @@ public class DeploymentTemplateBuilder {
               .diagnosticsProfile(diagnosticsProfile)
               .build();
 
+
+      String tagString = Joiner.on(",").join(Lists.newArrayList(tags));
+      if (tagString.isEmpty())
+         tagString = "jclouds";
+      userMetaData.put("tags", tagString);
+
       variables.put("virtualMachineName", name);
       ResourceDefinition virtualMachine = ResourceDefinition.builder()
               .name("[variables('virtualMachineName')]")
               .type("Microsoft.Compute/virtualMachines")
               .location(location)
-              .apiVersion(STORAGE_API_VERSION)
+              .apiVersion("2015-06-15")
               .dependsOn(Arrays.asList("[concat('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]",
                       "[concat('Microsoft.Network/networkInterfaces/', variables('networkInterfaceCardName'))]"))
-              .tags(ImmutableMap.of("displayName", "VirtualMachine"))
+              .tags(userMetaData)
               .properties(properties)
               .build();
 
       resources.add(virtualMachine);
    }
 
+
+   private ImageReference getImageReference(String publisher, String offer, String sku) {
+      return ImageReference.builder()
+              .publisher(publisher)
+              .offer(offer)
+              .sku(sku)
+              .version("latest")
+              .build();
+
+   }
+
+   private OSDisk getOsDisk(String vhdUri, String imageUri) {
+      OSDisk.Builder builder = OSDisk.builder();
+      builder.name("[variables('osDiskName')]");
+      builder.caching("ReadWrite");
+      builder.createOption("FromImage");
+      builder.vhd(VHD.create(vhdUri));
+
+      if (!imageUri.isEmpty()) {
+         builder.osType("Linux");
+         builder.image(VHD.create(imageUri));
+      }
+      return builder.build();
+   }
+
+   private boolean keyVaultInUse(){
+      return !Strings.isNullOrEmpty(options.getKeyVaultIdAndSecret());
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceContextLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceContextLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceContextLiveTest.java
deleted file mode 100644
index 940f785..0000000
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceContextLiveTest.java
+++ /dev/null
@@ -1,284 +0,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.
- */
-package org.jclouds.azurecompute.arm.compute;
-
-import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.inject.Module;
-import org.jclouds.azurecompute.arm.AzureComputeProviderMetadata;
-import org.jclouds.azurecompute.arm.internal.AzureLiveTestUtils;
-import org.jclouds.compute.RunNodesException;
-import org.jclouds.compute.RunScriptOnNodesException;
-import org.jclouds.compute.domain.ComputeMetadata;
-import org.jclouds.compute.domain.ExecResponse;
-import org.jclouds.compute.domain.NodeMetadata;
-import org.jclouds.compute.domain.OsFamily;
-import org.jclouds.compute.domain.Template;
-import org.jclouds.compute.domain.TemplateBuilder;
-import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
-import org.jclouds.domain.Credentials;
-import org.jclouds.domain.LoginCredentials;
-import org.jclouds.providers.ProviderMetadata;
-import org.jclouds.sshj.config.SshjSshClientModule;
-import org.testng.annotations.Test;
-
-import java.util.Map;
-import java.util.Properties;
-import java.util.Random;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.RESOURCE_GROUP_NAME;
-import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
-import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_SCRIPT_COMPLETE;
-
-import static org.jclouds.compute.predicates.NodePredicates.inGroup;
-import static org.jclouds.compute.options.TemplateOptions.Builder.overrideLoginCredentials;
-import static org.jclouds.scriptbuilder.domain.Statements.exec;
-import static org.testng.Assert.assertTrue;
-
-@Test(groups = "live", testName = "AzureComputeServiceContextLiveTest")
-public class AzureComputeServiceContextLiveTest extends BaseComputeServiceContextLiveTest {
-
-   public String azureGroup;
-   protected static final int RAND = new Random().nextInt(999);
-
-   @Override
-   protected Module getSshModule() {
-      return new SshjSshClientModule();
-   }
-
-   @Override protected Properties setupProperties() {
-      azureGroup = "jc" + RAND;
-
-      Properties properties = super.setupProperties();
-      long scriptTimeout = TimeUnit.MILLISECONDS.convert(60, TimeUnit.MINUTES);
-      properties.setProperty(TIMEOUT_SCRIPT_COMPLETE, scriptTimeout + "");
-      properties.setProperty(TIMEOUT_NODE_RUNNING, scriptTimeout + "");
-
-      AzureLiveTestUtils.defaultProperties(properties);
-      checkNotNull(setIfTestSystemPropertyPresent(properties, "oauth.endpoint"), "test.oauth.endpoint");
-
-      properties.put(RESOURCE_GROUP_NAME, azureGroup);
-      return properties;
-   }
-
-   public AzureComputeServiceContextLiveTest() {
-      provider = "azurecompute-arm";
-   }
-
-   @Test
-   public void testDefault() throws RunNodesException {
-
-      final String groupName = this.azureGroup;
-      final TemplateBuilder templateBuilder = view.getComputeService().templateBuilder();
-      templateBuilder.osFamily(OsFamily.UBUNTU);
-      templateBuilder.osVersionMatches("14.04");
-      templateBuilder.hardwareId("Standard_A0");
-      templateBuilder.locationId("westus");
-
-      final Template template = templateBuilder.build();
-
-      try {
-         Set<? extends NodeMetadata> nodes = view.getComputeService().createNodesInGroup(groupName, 1, template);
-         assertThat(nodes).hasSize(1);
-      } finally {
-// Do not destroy         view.getComputeService().destroyNodesMatching(inGroup(groupName));
-      }
-   }
-
-   private LoginCredentials getLogin() {
-      Credentials credentials = new Credentials("jclouds", "Password1!");
-      LoginCredentials login = LoginCredentials.fromCredentials(credentials);
-      return login;
-   }
-
-   @Test(dependsOnMethods = "testDefault")
-   public void testExec() throws RunScriptOnNodesException {
-      final String groupName = this.azureGroup;
-      String command = "echo hello";
-
-      Map<? extends NodeMetadata, ExecResponse> responses = view.getComputeService().runScriptOnNodesMatching(//
-              inGroup(groupName), // predicate used to select nodes
-              exec(command), // what you actually intend to run
-              overrideLoginCredentials(getLogin()) // use my local user &
-                      // ssh key
-                      .runAsRoot(false) // don't attempt to run as root (sudo)
-                      .wrapInInitScript(false)); // run command directly
-
-      assertTrue(responses.size() > 0);
-   }
-
-   public static Predicate<ComputeMetadata> nameStartsWith(final String prefix) {
-      Preconditions.checkNotNull(prefix, "prefix must be defined");
-
-      return new Predicate<ComputeMetadata>() {
-         @Override
-         public boolean apply(ComputeMetadata computeMetadata) {
-            return computeMetadata.getName().startsWith(prefix);
-         }
-
-         @Override
-         public String toString() {
-            return "nameStartsWith(" + prefix + ")";
-         }
-      };
-   }
-
-   @Test(dependsOnMethods = "testExec")
-   public void testStop() throws RunScriptOnNodesException {
-      final String groupName = this.azureGroup;
-      Set<? extends NodeMetadata> nodes = view.getComputeService().suspendNodesMatching(inGroup(groupName));
-      assertTrue(nodes.size() > 0);
-
-      boolean allStopped = false;
-      while (!allStopped) {
-         nodes = view.getComputeService().listNodesDetailsMatching(nameStartsWith(groupName));
-         for (NodeMetadata node : nodes) {
-            if (node.getStatus() != NodeMetadata.Status.SUSPENDED)
-            {
-               // Not stopped yet
-               allStopped = false;
-               try {
-                  Thread.sleep(15 * 1000);
-               } catch (InterruptedException e) {
-               }
-               continue;
-            }
-            else
-            {
-               allStopped = true;
-            }
-         }
-      }
-      assertTrue(allStopped);
-   }
-
-   @Test(dependsOnMethods = "testStop")
-   public void testStart() throws RunScriptOnNodesException {
-      final String groupName = this.azureGroup;
-      Set<? extends NodeMetadata> nodes = view.getComputeService().resumeNodesMatching(inGroup(groupName));
-      assertTrue(nodes.size() > 0);
-
-      boolean allStarted = false;
-      while (!allStarted) {
-         nodes = view.getComputeService().listNodesDetailsMatching(nameStartsWith(groupName));
-         for (NodeMetadata node : nodes) {
-            if (node.getStatus() != NodeMetadata.Status.RUNNING)
-            {
-               // Not started yet
-               allStarted = false;
-               try {
-                  Thread.sleep(15 * 1000);
-               } catch (InterruptedException e) {
-               }
-               continue;
-            }
-            else
-            {
-               allStarted = true;
-            }
-         }
-      }
-      assertTrue(allStarted);
-   }
-
-   @Test(dependsOnMethods = "testStart")
-   public void testRestart() throws RunScriptOnNodesException {
-      final String groupName = this.azureGroup;
-      Set<? extends NodeMetadata> nodes = view.getComputeService().rebootNodesMatching(inGroup(groupName));
-      assertTrue(nodes.size() > 0);
-
-      boolean allRestarted = false;
-      while (!allRestarted) {
-         nodes = view.getComputeService().listNodesDetailsMatching(nameStartsWith(groupName));
-         for (NodeMetadata node : nodes) {
-            if (node.getStatus() != NodeMetadata.Status.RUNNING)
-            {
-               // Not started yet
-               allRestarted = false;
-               try {
-                  Thread.sleep(30 * 1000);
-               } catch (InterruptedException e) {
-               }
-               continue;
-            }
-            else
-            {
-               allRestarted = true;
-            }
-         }
-      }
-      assertTrue(allRestarted);
-
-      view.getComputeService().destroyNodesMatching(inGroup(groupName));
-   }
-
-   @Test(dependsOnMethods = "testRestart")
-   public void testLinuxNode() throws RunNodesException {
-      final String groupName = this.azureGroup;
-      final TemplateBuilder templateBuilder = view.getComputeService().templateBuilder();
-      templateBuilder.osFamily(OsFamily.UBUNTU);
-      templateBuilder.osVersionMatches("14.04");
-      templateBuilder.hardwareId("Standard_A0");
-      templateBuilder.locationId("westus");
-      final Template template = templateBuilder.build();
-
-      try {
-         Set<? extends NodeMetadata> nodes = view.getComputeService().createNodesInGroup(groupName, 1, template);
-         assertThat(nodes).hasSize(1);
-      } finally {
-         view.getComputeService().destroyNodesMatching(inGroup(groupName));
-      }
-   }
-
-   @Test(dependsOnMethods = "testLinuxNode")
-   public void testWindowsNode() throws RunNodesException {
-      final String groupName = this.azureGroup;
-      final TemplateBuilder templateBuilder = view.getComputeService().templateBuilder();
-      templateBuilder.imageId("global/MicrosoftWindowsServer/WindowsServer/Windows-Server-Technical-Preview");
-      templateBuilder.hardwareId("Standard_A0");
-      templateBuilder.locationId("westus");
-      final Template template = templateBuilder.build();
-
-      try {
-         Set<? extends NodeMetadata> nodes = view.getComputeService().createNodesInGroup(groupName, 1, template);
-         assertThat(nodes).hasSize(1);
-      } finally {
-         view.getComputeService().destroyNodesMatching(inGroup(groupName));
-      }
-   }
-
-   @Override
-   protected ProviderMetadata createProviderMetadata() {
-      AzureComputeProviderMetadata pm = AzureComputeProviderMetadata.builder().build();
-      return pm;
-   }
-
-   protected String setIfTestSystemPropertyPresent(Properties overrides, String key) {
-      if (System.getProperties().containsKey("test." + key)) {
-         String val = System.getProperty("test." + key);
-         overrides.setProperty(key, val);
-         return val;
-      } else {
-         return null;
-      }
-   }
-
-}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceLiveTest.java
index ae43511..b1223e2 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceLiveTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceLiveTest.java
@@ -16,33 +16,57 @@
  */
 package org.jclouds.azurecompute.arm.compute;
 
+import org.jclouds.compute.RunScriptOnNodesException;
+import org.jclouds.compute.domain.ExecResponse;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.Template;
 import org.jclouds.compute.internal.BaseComputeServiceLiveTest;
+import org.jclouds.compute.predicates.NodePredicates;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.scriptbuilder.domain.Statement;
+import org.jclouds.scriptbuilder.domain.Statements;
+import org.jclouds.scriptbuilder.statements.java.InstallJDK;
+import org.jclouds.scriptbuilder.statements.login.AdminAccess;
 import org.jclouds.sshj.config.SshjSshClientModule;
 import org.testng.annotations.Test;
-
 import org.jclouds.providers.ProviderMetadata;
-
 import org.jclouds.azurecompute.arm.AzureComputeProviderMetadata;
 
 import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.RESOURCE_GROUP_NAME;
 import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_PORT_OPEN;
 import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_SCRIPT_COMPLETE;
 import org.jclouds.azurecompute.arm.internal.AzureLiveTestUtils;
-
 import com.google.inject.Module;
+import java.util.Map;
 import java.util.Properties;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
+
 import static com.google.common.base.Preconditions.checkNotNull;
+import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
+import org.jclouds.logging.config.LoggingModule;
+
 
 /**
  * Live tests for the {@link org.jclouds.compute.ComputeService} integration.
  */
 @Test(groups = "live", singleThreaded = true, testName = "AzureComputeServiceLiveTest")
 public class AzureComputeServiceLiveTest extends BaseComputeServiceLiveTest {
-   public String azureGroup;
+   protected int nonBlockDurationSeconds = 30;
 
    public AzureComputeServiceLiveTest() {
       provider = "azurecompute-arm";
+      nonBlockDurationSeconds = 300;
+      group = "az-u";
+   }
+
+   @Override
+   protected LoggingModule getLoggingModule() {
+      return new SLF4JLoggingModule();
    }
 
    @Override
@@ -56,18 +80,42 @@ public class AzureComputeServiceLiveTest extends BaseComputeServiceLiveTest {
       return pm;
    }
 
-   @Override protected Properties setupProperties() {
-      azureGroup = "jc" + System.getProperty("user.name").substring(0, 3);
+   @Override
+   protected Properties setupProperties() {
       Properties properties = super.setupProperties();
-      long scriptTimeout = TimeUnit.MILLISECONDS.convert(20, TimeUnit.MINUTES);
+      long scriptTimeout = TimeUnit.MILLISECONDS.convert(60, TimeUnit.MINUTES);
       properties.setProperty(TIMEOUT_SCRIPT_COMPLETE, scriptTimeout + "");
       properties.setProperty(TIMEOUT_NODE_RUNNING, scriptTimeout + "");
-      properties.put(RESOURCE_GROUP_NAME, azureGroup);
+      properties.setProperty(TIMEOUT_PORT_OPEN, scriptTimeout + "");
+      properties.setProperty(TIMEOUT_NODE_TERMINATED, scriptTimeout + "");
+      properties.setProperty(TIMEOUT_NODE_SUSPENDED, scriptTimeout + "");
+      properties.put(RESOURCE_GROUP_NAME, "a4");
 
       AzureLiveTestUtils.defaultProperties(properties);
       checkNotNull(setIfTestSystemPropertyPresent(properties, "oauth.endpoint"), "test.oauth.endpoint");
 
       return properties;
+   }
 
+   @Override
+   protected Template refreshTemplate() {
+      return this.template = addRunScriptToTemplate(this.buildTemplate(this.client.templateBuilder()));
+   }
+
+   @Override
+   protected Template addRunScriptToTemplate(Template template) {
+      template.getOptions().runScript(Statements.newStatementList(new Statement[]{AdminAccess.standard(), Statements.exec("sleep 50"), InstallJDK.fromOpenJDK()}));
+      return template;
+   }
+
+   @Override
+   @Test( enabled = false)
+   protected void weCanCancelTasks(NodeMetadata node) throws InterruptedException, ExecutionException {
+      return;
+   }
+
+   @Override
+   protected Map<? extends NodeMetadata, ExecResponse> runScriptWithCreds(String group, OperatingSystem os, LoginCredentials creds) throws RunScriptOnNodesException {
+      return this.client.runScriptOnNodesMatching(NodePredicates.runningInGroup(group), Statements.newStatementList(Statements.exec("sleep 50"), InstallJDK.fromOpenJDK()), org.jclouds.compute.options.TemplateOptions.Builder.overrideLoginCredentials(creds).nameTask("runScriptWithCreds"));
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeImageExtensionLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeImageExtensionLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeImageExtensionLiveTest.java
new file mode 100644
index 0000000..fecd0fd
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeImageExtensionLiveTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.azurecompute.arm.compute.extensions;
+
+import com.google.inject.Module;
+import org.jclouds.azurecompute.arm.AzureComputeProviderMetadata;
+import org.jclouds.azurecompute.arm.config.AzureComputeProperties;
+import org.jclouds.azurecompute.arm.internal.AzureLiveTestUtils;
+import org.jclouds.compute.config.ComputeServiceProperties;
+import org.jclouds.compute.extensions.internal.BaseImageExtensionLiveTest;
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.testng.annotations.Test;
+
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.RESOURCE_GROUP_NAME;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_PORT_OPEN;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_SCRIPT_COMPLETE;
+
+/**
+ * Live tests for the {@link org.jclouds.compute.extensions.ImageExtension} integration.
+ */
+@Test(groups = "live", singleThreaded = true, testName = "AzureComputeImageExtensionLiveTest")
+public class AzureComputeImageExtensionLiveTest extends BaseImageExtensionLiveTest {
+
+   public AzureComputeImageExtensionLiveTest() {
+      provider = "azurecompute-arm";
+   }
+
+   public static String NAME_PREFIX = "%s";
+
+   @Override
+   protected Module getSshModule() {
+      return new SshjSshClientModule();
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties properties = super.setupProperties();
+      long scriptTimeout = TimeUnit.MILLISECONDS.convert(60, TimeUnit.MINUTES);
+      properties.setProperty(TIMEOUT_SCRIPT_COMPLETE, scriptTimeout + "");
+      properties.setProperty(TIMEOUT_NODE_RUNNING, scriptTimeout + "");
+      properties.setProperty(TIMEOUT_PORT_OPEN, scriptTimeout + "");
+      properties.setProperty(TIMEOUT_NODE_TERMINATED, scriptTimeout + "");
+      properties.setProperty(TIMEOUT_NODE_SUSPENDED, scriptTimeout + "");
+      properties.put(RESOURCE_GROUP_NAME, "a5");
+
+      properties.put(ComputeServiceProperties.POLL_INITIAL_PERIOD, 1000);
+      properties.put(ComputeServiceProperties.POLL_MAX_PERIOD, 10000);
+      properties.setProperty(AzureComputeProperties.OPERATION_TIMEOUT, "46000000");
+      properties.setProperty(AzureComputeProperties.OPERATION_POLL_INITIAL_PERIOD, "5");
+      properties.setProperty(AzureComputeProperties.OPERATION_POLL_MAX_PERIOD, "15");
+      properties.setProperty(AzureComputeProperties.TCP_RULE_FORMAT, "tcp_%s-%s");
+      properties.setProperty(AzureComputeProperties.TCP_RULE_REGEXP, "tcp_\\d{1,5}-\\d{1,5}");
+
+      AzureLiveTestUtils.defaultProperties(properties);
+      checkNotNull(setIfTestSystemPropertyPresent(properties, "oauth.endpoint"), "test.oauth.endpoint");
+
+      return properties;
+
+   }
+
+   @Override
+   protected ProviderMetadata createProviderMetadata() {
+      AzureComputeProviderMetadata pm = AzureComputeProviderMetadata.builder().build();
+      return pm;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/DeploymentApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/DeploymentApiLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/DeploymentApiLiveTest.java
index 0e2baef..7493a63 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/DeploymentApiLiveTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/DeploymentApiLiveTest.java
@@ -18,6 +18,7 @@ package org.jclouds.azurecompute.arm.features;
 
 import com.google.common.base.Predicate;
 import com.google.common.net.UrlEscapers;
+import org.jclouds.azurecompute.arm.compute.options.AzureTemplateOptions;
 import org.jclouds.azurecompute.arm.domain.Deployment;
 import org.jclouds.azurecompute.arm.domain.Deployment.ProvisioningState;
 import org.jclouds.azurecompute.arm.domain.DeploymentBody;
@@ -141,7 +142,7 @@ public class DeploymentApiLiveTest extends BaseAzureComputeApiLiveTest {
    public void testCreate() {
       String rsakey = new String("ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAmfk/QSF0pvnrpdz+Ah2KulGruKU+8FFBdlw938MpOysRdmp7uwpH6Z7+5VNGNdxFIAyc/W3UaZXF9hTsU8+78TlwkZpsr2mzU+ycu37XLAQ8Uv7hjsAN0DkKKPrZ9lgUUfZVKV/8E/JIAs03gIbL6zO3y7eYJQ5fNeZb+nji7tQT+YLpGq/FDegvraPKVMQbCSCZhsHyWhdPLyFlu9/30npZ0ahYOPI/KyZxFDtM/pHp88+ZAk9Icq5owaLRWcJQqrBGWqjbZnHtjdDqvHZ+C0wPhdJZPyfkHOrSYTwSQBXfX4JLRRCz3J1jf62MbQWT1o6Y4JEs1ZP1Skxu6zR96Q== mocktest");
 
-      TemplateOptions options = new TemplateOptions();
+      TemplateOptions options = new AzureTemplateOptions();
       options.authorizePublicKey(rsakey);
       DeploymentTemplateBuilder templateBuilder = getDeploymentTemplateBuilderWithOptions(options);
       DeploymentBody deploymentTemplateBody = templateBuilder.getDeploymentTemplate();

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/DeploymentTemplateBuilderTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/DeploymentTemplateBuilderTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/DeploymentTemplateBuilderTest.java
index bc505e7..ad5b1f3 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/DeploymentTemplateBuilderTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/DeploymentTemplateBuilderTest.java
@@ -18,18 +18,17 @@ package org.jclouds.azurecompute.arm.features;
 
 import org.jclouds.azurecompute.arm.internal.BaseAzureComputeApiMockTest;
 import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.azurecompute.arm.compute.options.AzureTemplateOptions;
 import org.jclouds.azurecompute.arm.domain.DeploymentBody;
 import org.jclouds.azurecompute.arm.domain.ImageReference;
 import org.jclouds.azurecompute.arm.domain.IpConfiguration;
 import org.jclouds.azurecompute.arm.domain.IpConfigurationProperties;
 import org.jclouds.azurecompute.arm.domain.NetworkInterfaceCardProperties;
-import org.jclouds.azurecompute.arm.domain.OSProfile;
 import org.jclouds.azurecompute.arm.domain.PublicIPAddressProperties;
 import org.jclouds.azurecompute.arm.domain.ResourceDefinition;
 import org.jclouds.azurecompute.arm.domain.StorageService;
 import org.jclouds.azurecompute.arm.domain.StorageService.StorageServiceProperties;
 import org.jclouds.azurecompute.arm.domain.VirtualMachineProperties;
-import org.jclouds.azurecompute.arm.domain.VirtualNetwork.VirtualNetworkProperties;
 import org.jclouds.azurecompute.arm.util.DeploymentTemplateBuilder;
 import org.jclouds.compute.domain.Hardware;
 import org.jclouds.compute.domain.HardwareBuilder;
@@ -56,6 +55,8 @@ import static org.testng.Assert.fail;
 public class DeploymentTemplateBuilderTest extends BaseAzureComputeApiMockTest {
 
    final String group = "jcgroup";
+   final String vnetName = group + "virtualnetwork";
+   final String subnetId = "";
 
    @Test
    public void testResourceGroup() {
@@ -72,22 +73,6 @@ public class DeploymentTemplateBuilderTest extends BaseAzureComputeApiMockTest {
    }
 
    @Test
-   void testVirtualNetwork() {
-      DeploymentTemplateBuilder builder = getMockDeploymentTemplateBuilderWithEmptyOptions();
-      DeploymentBody deploymentBody = builder.getDeploymentTemplate();
-      List<ResourceDefinition> resources = deploymentBody.template().resources();
-      Map<String, String> variables = deploymentBody.template().variables();
-
-      ResourceDefinition resource = getResourceByType(resources, "Microsoft.Network/virtualNetworks");
-
-      VirtualNetworkProperties properties = (VirtualNetworkProperties) resource.properties();
-      assertTrue(properties.addressSpace().addressPrefixes().size() > 0);
-      assertTrue(properties.subnets().size() > 0);
-
-      assertTrue(variables.containsKey(parseVariableName(resource.name())));
-   }
-
-   @Test
    void testPublicIpAddress() {
       DeploymentTemplateBuilder builder = getMockDeploymentTemplateBuilderWithEmptyOptions();
       DeploymentBody deploymentBody = builder.getDeploymentTemplate();
@@ -147,42 +132,49 @@ public class DeploymentTemplateBuilderTest extends BaseAzureComputeApiMockTest {
    }
 
    @Test
-   void testVirtualMachineWithSSH() {
+   void testCustomOptions(){
+      final String dnsLabelPrefix = "mydnslabel";
+      final String customData = "echo customData";
+      final String customData64 = "ZWNobyBjdXN0b21EYXRh";
+      final String keyvaultString = "/url/to/vault/:publickeysecret";
+
+      AzureTemplateOptions options = new AzureTemplateOptions()
+            .customData(customData)
+            .DNSLabelPrefix(dnsLabelPrefix)
+            .keyVaultIdAndSecret(keyvaultString);
 
-      String rsakey = new String("ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAmfk/QSF0pvnrpdz+Ah2KulGruKU+8FFBdlw938MpOysRdmp7uwpH6Z7+5VNGNdxFIAyc/W3UaZXF9hTsU8+78TlwkZpsr2mzU+ycu37XLAQ8Uv7hjsAN0DkKKPrZ9lgUUfZVKV/8E/JIAs03gIbL6zO3y7eYJQ5fNeZb+nji7tQT+YLpGq/FDegvraPKVMQbCSCZhsHyWhdPLyFlu9/30npZ0ahYOPI/KyZxFDtM/pHp88+ZAk9Icq5owaLRWcJQqrBGWqjbZnHtjdDqvHZ+C0wPhdJZPyfkHOrSYTwSQBXfX4JLRRCz3J1jf62MbQWT1o6Y4JEs1ZP1Skxu6zR96Q== mocktest");
+      options.virtualNetworkName(vnetName);
+      options.subnetId(subnetId);
 
-      TemplateOptions options = new TemplateOptions();
-      options.authorizePublicKey(rsakey);
+      assertEquals(options.as(AzureTemplateOptions.class).getCustomData(), customData);
+      assertEquals(options.getDNSLabelPrefix(), dnsLabelPrefix);
+      assertEquals(options.as(AzureTemplateOptions.class).getKeyVaultIdAndSecret(), keyvaultString);
 
       DeploymentTemplateBuilder builder = getMockDeploymentTemplateBuilderWithOptions(options);
-      Template template = builder.getTemplate();
 
       DeploymentBody deploymentBody = builder.getDeploymentTemplate();
-      List<ResourceDefinition> resources = deploymentBody.template().resources();
-      Map<String, String> variables = deploymentBody.template().variables();
 
-      ResourceDefinition resource = getResourceByType(resources, "Microsoft.Compute/virtualMachines");
-      assertNotNull(resource);
+      List<ResourceDefinition> resources = deploymentBody.template().resources();
+      ResourceDefinition publicIpResource = getResourceByType(resources, "Microsoft.Network/publicIPAddresses");
+      assertNotNull(publicIpResource);
 
-      VirtualMachineProperties properties = (VirtualMachineProperties) resource.properties();
-      assertEquals(properties.hardwareProfile().vmSize(), template.getHardware().getId());
+      PublicIPAddressProperties ipProperties = (PublicIPAddressProperties) publicIpResource.properties();
+      assertEquals(ipProperties.dnsSettings().domainNameLabel(), dnsLabelPrefix);
 
-      ImageReference image = properties.storageProfile().imageReference();
-      assertEquals(image.publisher(), template.getImage().getProviderId());
-      assertEquals(image.offer(), template.getImage().getName());
-      assertEquals(image.sku(), template.getImage().getVersion());
-      assertEquals(image.version(), "latest");
+      ResourceDefinition vmResource = getResourceByType(resources, "Microsoft.Compute/virtualMachines");
+      assertNotNull(vmResource);
 
-      // Check that ssh key is in place
-      OSProfile.LinuxConfiguration osConfig = properties.osProfile().linuxConfiguration();
-      assertEquals(osConfig.disablePasswordAuthentication(), "true");
-      assertTrue(osConfig.ssh().publicKeys().size() > 0);
-      assertEquals(osConfig.ssh().publicKeys().get(0).keyData(), rsakey);
+      VirtualMachineProperties virtualMachineProperties = (VirtualMachineProperties) vmResource.properties();
+      assertEquals(virtualMachineProperties.osProfile().customData(), customData64);
 
-      assertTrue(variables.containsKey(parseVariableName(resource.name())));
+      //populated when keyvault is used to get public key.
+      assertNotNull(virtualMachineProperties.osProfile().linuxConfiguration().ssh().publicKeys());
    }
 
    private Template getMockTemplate(TemplateOptions options) {
+      ((AzureTemplateOptions)options).virtualNetworkName(vnetName);
+      ((AzureTemplateOptions)options).subnetId(subnetId);
+
       Location provider = (new LocationBuilder()).scope(LocationScope.PROVIDER).id("azurecompute-arm").description("azurecompute-arm").build();
       Location region = (new LocationBuilder()).scope(LocationScope.REGION).id("northeurope").description("North Europe").parent(provider).build();
       OperatingSystem os = OperatingSystem.builder().name("osName").version("osVersion").description("osDescription").arch("X86_32").build();
@@ -193,13 +185,19 @@ public class DeploymentTemplateBuilderTest extends BaseAzureComputeApiMockTest {
    }
 
    private DeploymentTemplateBuilder getMockDeploymentTemplateBuilderWithEmptyOptions() {
-      TemplateOptions options = new TemplateOptions();
+      AzureTemplateOptions options = new AzureTemplateOptions();
+      options.virtualNetworkName(vnetName);
+      options.subnetId(subnetId);
+
       Template template = getMockTemplate(options);
       DeploymentTemplateBuilder templateBuilder = api.deploymentTemplateFactory().create(group, "mydeployment", template);
       return templateBuilder;
    }
 
    private DeploymentTemplateBuilder getMockDeploymentTemplateBuilderWithOptions(TemplateOptions options) {
+      ((AzureTemplateOptions)options).virtualNetworkName(vnetName);
+      ((AzureTemplateOptions)options).subnetId(subnetId);
+
       Template template = getMockTemplate(options);
       DeploymentTemplateBuilder templateBuilder = api.deploymentTemplateFactory().create(group, "mydeployment", template);
       return templateBuilder;

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/JobApiMockTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/JobApiMockTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/JobApiMockTest.java
index 3e55df1..7364145 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/JobApiMockTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/JobApiMockTest.java
@@ -18,12 +18,15 @@ package org.jclouds.azurecompute.arm.features;
 
 import java.io.IOException;
 import java.net.URI;
+import java.util.List;
 
+import org.jclouds.azurecompute.arm.domain.ResourceDefinition;
 import org.jclouds.azurecompute.arm.functions.ParseJobStatus.JobStatus;
 import org.jclouds.azurecompute.arm.internal.BaseAzureComputeApiMockTest;
 import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
 
 @Test(groups = "unit", testName = "JobApiMockTest", singleThreaded = true)
 public class JobApiMockTest extends BaseAzureComputeApiMockTest {
@@ -70,4 +73,24 @@ public class JobApiMockTest extends BaseAzureComputeApiMockTest {
       assertSent(server, "GET", requestUrl);
    }
 
+   public void testCaptureJobStatus() throws IOException, InterruptedException {
+      server.enqueue(jsonResponse("/resourceDefinition.json").setResponseCode(200));
+
+      List<ResourceDefinition> resourceDefinitionsList = api.getJobApi().captureStatus(URI.create(requestUrl));
+
+      assertTrue(resourceDefinitionsList.size() > 0);
+
+      assertSent(server, "GET", requestUrl);
+   }
+
+   public void testCaptureJobStatusFailed() throws InterruptedException {
+      server.enqueue(response404());
+
+      List<ResourceDefinition> resourceDefinitionsList = api.getJobApi().captureStatus(URI.create(requestUrl));
+
+      assertEquals(resourceDefinitionsList.size(), 0);
+
+      assertSent(server, "GET", requestUrl);
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/NetworkInterfaceCardApiMockTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/NetworkInterfaceCardApiMockTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/NetworkInterfaceCardApiMockTest.java
index 12ad073..bda8cad 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/NetworkInterfaceCardApiMockTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/NetworkInterfaceCardApiMockTest.java
@@ -107,7 +107,8 @@ public class NetworkInterfaceCardApiMockTest extends BaseAzureComputeApiMockTest
               NetworkInterfaceCardProperties.create(null, null, null,
                       Arrays.asList(IpConfiguration.create("myipconfig", null, null, null,
                               IpConfigurationProperties.create(null, null, "Dynamic", IdReference.create(SubnetID), null))
-                      )
+                      ),
+                      null
               );
 
       final Map<String, String> tags = ImmutableMap.of("mycustomtag", "foobar");

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/TemplateToDeploymentTemplateLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/TemplateToDeploymentTemplateLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/TemplateToDeploymentTemplateLiveTest.java
index be2eb3e..12d6255 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/TemplateToDeploymentTemplateLiveTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/TemplateToDeploymentTemplateLiveTest.java
@@ -17,10 +17,12 @@
 package org.jclouds.azurecompute.arm.features;
 
 import com.google.common.net.UrlEscapers;
-import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.azurecompute.arm.compute.options.AzureTemplateOptions;
 import org.jclouds.azurecompute.arm.domain.Deployment;
 import org.jclouds.azurecompute.arm.domain.DeploymentBody;
 import org.jclouds.azurecompute.arm.domain.DeploymentProperties;
+import org.jclouds.azurecompute.arm.domain.Subnet;
+import org.jclouds.azurecompute.arm.domain.VirtualNetwork;
 import org.jclouds.azurecompute.arm.internal.BaseAzureComputeApiLiveTest;
 import org.jclouds.azurecompute.arm.util.DeploymentTemplateBuilder;
 import org.jclouds.compute.domain.Hardware;
@@ -31,6 +33,7 @@ import org.jclouds.compute.domain.OperatingSystem;
 import org.jclouds.compute.domain.OsFamily;
 import org.jclouds.compute.domain.Template;
 import org.jclouds.compute.domain.internal.TemplateImpl;
+import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.domain.Location;
 import org.jclouds.domain.LocationBuilder;
 import org.jclouds.domain.LocationScope;
@@ -47,12 +50,50 @@ public class TemplateToDeploymentTemplateLiveTest extends BaseAzureComputeApiLiv
    private int pollingInterval = 3; // how frequently to poll for create status
    private String resourceGroup;
    private String deploymentName;
+   private String vnetName;
+   private String subnetId;
 
    @BeforeClass
    @Override
    public void setup() {
       super.setup();
       resourceGroup = getResourceGroupName();
+
+      //Subnets belong to a virtual network so that needs to be created first
+      VirtualNetwork vn = getOrCreateVirtualNetwork(VIRTUAL_NETWORK_NAME);
+      assertNotNull(vn);
+      vnetName = vn.name();
+
+      //Subnet needs to be up & running before NIC can be created
+      Subnet subnet = getOrCreateSubnet(DEFAULT_SUBNET_NAME, VIRTUAL_NETWORK_NAME);
+      assertNotNull(subnet);
+      assertNotNull(subnet.id());
+      subnetId = subnet.id();
+   }
+
+   @Test(groups = "live")
+   public void testValidateDeploymentTemplateLinuxNodeWithOptions() {
+      Long now = System.currentTimeMillis();
+      deploymentName = "jc" + now;
+
+      AzureTemplateOptions options = new AzureTemplateOptions();
+      options.virtualNetworkName(vnetName);
+      options.subnetId(subnetId);
+
+      options.inboundPorts(22, 8080);
+
+      DeploymentTemplateBuilder templateBuilder = getDeploymentTemplateBuilderWithOptions(options);
+
+      DeploymentBody deploymentTemplateBody = templateBuilder.getDeploymentTemplate();
+
+      DeploymentProperties properties = DeploymentProperties.create(deploymentTemplateBody);
+
+      String deploymentTemplate = templateBuilder.getDeploymentTemplateJson(properties);
+      deploymentTemplate = UrlEscapers.urlFormParameterEscaper().escape(deploymentTemplate);
+
+      //Validates that template is syntactically correct
+      Deployment deployment = api().validate(deploymentName, deploymentTemplate);
+      assertNotNull(deployment);
    }
 
    @Test(groups = "live")
@@ -75,13 +116,44 @@ public class TemplateToDeploymentTemplateLiveTest extends BaseAzureComputeApiLiv
    }
 
    @Test(groups = "live")
+   public void testValidateDeploymentTemplateWithCustomOptions() {
+      Long now = System.currentTimeMillis();
+      deploymentName = "jc" + now;
+
+      String rsakey = new String("ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAmfk/QSF0pvnrpdz+Ah2KulGruKU+8FFBdlw938MpOysRdmp7uwpH6Z7+5VNGNdxFIAyc/W3UaZXF9hTsU8+78TlwkZpsr2mzU+ycu37XLAQ8Uv7hjsAN0DkKKPrZ9lgUUfZVKV/8E/JIAs03gIbL6zO3y7eYJQ5fNeZb+nji7tQT+YLpGq/FDegvraPKVMQbCSCZhsHyWhdPLyFlu9/30npZ0ahYOPI/KyZxFDtM/pHp88+ZAk9Icq5owaLRWcJQqrBGWqjbZnHtjdDqvHZ+C0wPhdJZPyfkHOrSYTwSQBXfX4JLRRCz3J1jf62MbQWT1o6Y4JEs1ZP1Skxu6zR96Q== mocktest");
+      TemplateOptions options = new AzureTemplateOptions()
+              .DNSLabelPrefix("mydnslabel")
+              .virtualNetworkAddressPrefix("10.0.0.0/20")
+              .subnetAddressPrefix("10.0.0.0/25")
+              .authorizePublicKey(rsakey);
+
+      ((AzureTemplateOptions)options).virtualNetworkName(vnetName);
+      ((AzureTemplateOptions)options).subnetId(subnetId);
+
+      DeploymentTemplateBuilder templateBuilder = getDeploymentTemplateBuilderWithOptions(options);
+
+      DeploymentBody deploymentTemplateBody = templateBuilder.getDeploymentTemplate();
+
+      DeploymentProperties properties = DeploymentProperties.create(deploymentTemplateBody);
+
+      String deploymentTemplate = templateBuilder.getDeploymentTemplateJson(properties);
+      deploymentTemplate = UrlEscapers.urlFormParameterEscaper().escape(deploymentTemplate);
+
+      Deployment deployment = api().validate(deploymentName, deploymentTemplate);
+      assertNotNull(deployment);
+   }
+
+   @Test(groups = "live")
    public void testValidateDeploymentTemplateLinuxNodeWithSSH() {
       Long now = System.currentTimeMillis();
       deploymentName = "jc" + now;
 
       String rsakey = new String("ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAmfk/QSF0pvnrpdz+Ah2KulGruKU+8FFBdlw938MpOysRdmp7uwpH6Z7+5VNGNdxFIAyc/W3UaZXF9hTsU8+78TlwkZpsr2mzU+ycu37XLAQ8Uv7hjsAN0DkKKPrZ9lgUUfZVKV/8E/JIAs03gIbL6zO3y7eYJQ5fNeZb+nji7tQT+YLpGq/FDegvraPKVMQbCSCZhsHyWhdPLyFlu9/30npZ0ahYOPI/KyZxFDtM/pHp88+ZAk9Icq5owaLRWcJQqrBGWqjbZnHtjdDqvHZ+C0wPhdJZPyfkHOrSYTwSQBXfX4JLRRCz3J1jf62MbQWT1o6Y4JEs1ZP1Skxu6zR96Q== mocktest");
 
-      TemplateOptions options = new TemplateOptions();
+      AzureTemplateOptions options = new AzureTemplateOptions();
+      options.virtualNetworkName(vnetName);
+      options.subnetId(subnetId);
+
       options.authorizePublicKey(rsakey);
       DeploymentTemplateBuilder templateBuilder = getDeploymentTemplateBuilderWithOptions(options);
 
@@ -103,8 +175,12 @@ public class TemplateToDeploymentTemplateLiveTest extends BaseAzureComputeApiLiv
 
       String rsakey = new String("ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAmfk/QSF0pvnrpdz+Ah2KulGruKU+8FFBdlw938MpOysRdmp7uwpH6Z7+5VNGNdxFIAyc/W3UaZXF9hTsU8+78TlwkZpsr2mzU+ycu37XLAQ8Uv7hjsAN0DkKKPrZ9lgUUfZVKV/8E/JIAs03gIbL6zO3y7eYJQ5fNeZb+nji7tQT+YLpGq/FDegvraPKVMQbCSCZhsHyWhdPLyFlu9/30npZ0ahYOPI/KyZxFDtM/pHp88+ZAk9Icq5owaLRWcJQqrBGWqjbZnHtjdDqvHZ+C0wPhdJZPyfkHOrSYTwSQBXfX4JLRRCz3J1jf62MbQWT1o6Y4JEs1ZP1Skxu6zR96Q== mocktest");
 
-      TemplateOptions options = new TemplateOptions();
+      AzureTemplateOptions options = new AzureTemplateOptions();
+      options.virtualNetworkName(vnetName);
+      options.subnetId(subnetId);
+
       options.authorizePublicKey(rsakey);
+      options.inboundPorts(22, 8080);
       DeploymentTemplateBuilder templateBuilder = getDeploymentTemplateBuilderWithOptions(options);
 
       DeploymentBody deploymentTemplateBody = templateBuilder.getDeploymentTemplate();
@@ -144,7 +220,7 @@ public class TemplateToDeploymentTemplateLiveTest extends BaseAzureComputeApiLiv
 
    private Template getTemplate(TemplateOptions options) {
       Location provider = (new LocationBuilder()).scope(LocationScope.PROVIDER).id("azurecompute-arm").description("azurecompute-arm").build();
-      Location region = (new LocationBuilder()).scope(LocationScope.REGION).id("northeurope").description("North Europe").parent(provider).build();
+      Location region = (new LocationBuilder()).scope(LocationScope.REGION).id(LOCATION).description(LOCATIONDESCRIPTION).parent(provider).build();
 
       OperatingSystem os = OperatingSystem.builder()
               .family(OsFamily.UBUNTU)
@@ -168,7 +244,10 @@ public class TemplateToDeploymentTemplateLiveTest extends BaseAzureComputeApiLiv
    }
 
    private DeploymentTemplateBuilder getDeploymentTemplateBuilderWithEmptyOptions() {
-      TemplateOptions options = new TemplateOptions();
+      AzureTemplateOptions options = new AzureTemplateOptions();
+      options.virtualNetworkName(vnetName);
+      options.subnetId(subnetId);
+
       Template template = getTemplate(options);
       DeploymentTemplateBuilder templateBuilder = api.deploymentTemplateFactory().create(resourceGroup, deploymentName, template);
       return templateBuilder;

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2b36a75f/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiLiveTest.java
index f117f1c..5271e2a 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiLiveTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineApiLiveTest.java
@@ -17,6 +17,8 @@
 package org.jclouds.azurecompute.arm.features;
 
 import com.google.common.base.Predicate;
+import com.google.gson.internal.LinkedTreeMap;
+import com.google.common.collect.Iterables;
 import org.jclouds.azurecompute.arm.domain.DataDisk;
 import org.jclouds.azurecompute.arm.domain.DiagnosticsProfile;
 import org.jclouds.azurecompute.arm.domain.HardwareProfile;
@@ -33,16 +35,28 @@ import org.jclouds.azurecompute.arm.domain.VirtualMachine;
 import org.jclouds.azurecompute.arm.domain.VirtualMachineInstance;
 import org.jclouds.azurecompute.arm.domain.VirtualMachineProperties;
 import org.jclouds.azurecompute.arm.functions.ParseJobStatus;
+import org.jclouds.azurecompute.arm.internal.AzureLiveTestUtils;
 import org.jclouds.azurecompute.arm.internal.BaseAzureComputeApiLiveTest;
 import org.jclouds.util.Predicates2;
+import org.jclouds.azurecompute.arm.domain.ResourceDefinition;
+import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.RESOURCE_GROUP_NAME;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_SCRIPT_COMPLETE;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_PORT_OPEN;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
 import static org.testng.Assert.assertNotNull;
 
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
 
 import static org.testng.Assert.assertTrue;
 
@@ -60,6 +74,23 @@ public class VirtualMachineApiLiveTest extends BaseAzureComputeApiLiveTest {
       nicName = nic.name();
    }
 
+   @Override
+   protected Properties setupProperties() {
+      Properties properties = super.setupProperties();
+      long scriptTimeout = TimeUnit.MILLISECONDS.convert(60, TimeUnit.MINUTES);
+      properties.setProperty(TIMEOUT_SCRIPT_COMPLETE, scriptTimeout + "");
+      properties.setProperty(TIMEOUT_NODE_RUNNING, scriptTimeout + "");
+      properties.setProperty(TIMEOUT_PORT_OPEN, scriptTimeout + "");
+      properties.setProperty(TIMEOUT_NODE_TERMINATED, scriptTimeout + "");
+      properties.setProperty(TIMEOUT_NODE_SUSPENDED, scriptTimeout + "");
+      properties.put(RESOURCE_GROUP_NAME, getResourceGroupName());
+
+      AzureLiveTestUtils.defaultProperties(properties);
+      checkNotNull(setIfTestSystemPropertyPresent(properties, "oauth.endpoint"), "test.oauth.endpoint");
+
+      return properties;
+   }
+
    private String getName() {
       if (vmName == null) {
          vmName = String.format("%3.24s",
@@ -70,7 +101,6 @@ public class VirtualMachineApiLiveTest extends BaseAzureComputeApiLiveTest {
 
    @Test
    public void testCreate() {
-
       StorageAccountApi storageApi = api.getStorageAccountApi(getResourceGroupName());
       StorageService storageAccount = storageApi.get(getStorageServiceName());
       String blob = storageAccount.storageServiceProperties().primaryEndpoints().get("blob");
@@ -109,22 +139,7 @@ public class VirtualMachineApiLiveTest extends BaseAzureComputeApiLiveTest {
    public void testStop() {
       api().stop(getName());
       //Poll until resource is ready to be used
-      boolean jobDone = Predicates2.retry(new Predicate<String>() {
-         @Override
-         public boolean apply(String name) {
-            String status = "";
-            List<VirtualMachineInstance.VirtualMachineStatus> statuses = api().getInstanceDetails(name).statuses();
-            for (int c = 0; c < statuses.size(); c++) {
-               if (statuses.get(c).code().substring(0, 10).equals("PowerState")) {
-                  status = statuses.get(c).displayStatus();
-                  break;
-               }
-            }
-            return status.equals("VM stopped");
-         }
-      }, 60 * 4 * 1000).apply(getName());
-      assertTrue(jobDone, "stop operation did not complete in the configured timeout");
-
+      nodeSuspendedPredicate.apply(getName());
    }
 
    @Test(dependsOnMethods = "testGet")
@@ -194,11 +209,53 @@ public class VirtualMachineApiLiveTest extends BaseAzureComputeApiLiveTest {
    @Test(dependsOnMethods = "testCreate")
    public void testList() {
       List<VirtualMachine> list = api().list();
-      VirtualMachine vm = api().get(getName());
-      assertTrue(list.contains(vm));
+      final VirtualMachine vm = api().get(getName());
+
+      boolean vmPresent = Iterables.any(list, new Predicate<VirtualMachine>() {
+         public boolean apply(VirtualMachine input) {
+            return input.name().equals(vm.name());
+         }
+      });
+
+      assertTrue(vmPresent);
+   }
+
+   @Test(dependsOnMethods = "testRestart")
+   public void testGeneralize() throws IllegalStateException {
+      api().stop(getName());
+      //Poll until resource is ready to be used
+
+      if (nodeSuspendedPredicate.apply(getName())) {
+         api().generalize(getName());
+      }
+   }
+
+   @Test(dependsOnMethods = "testGeneralize")
+   public void testCapture() throws IllegalStateException {
+      URI uri = api().capture(getName(), getName(), getName());
+      if (uri != null) {
+         if (imageAvailablePredicate.apply(uri)) {
+            List<ResourceDefinition> definitions = api.getJobApi().captureStatus(uri);
+            if (definitions != null) {
+               for (ResourceDefinition definition : definitions) {
+                  LinkedTreeMap<String, String> properties = (LinkedTreeMap<String, String>) definition.properties();
+                  Object storageObject = properties.get("storageProfile");
+                  LinkedTreeMap<String, String> properties2 = (LinkedTreeMap<String, String>) storageObject;
+                  Object osDiskObject = properties2.get("osDisk");
+                  LinkedTreeMap<String, String> osProperties = (LinkedTreeMap<String, String>) osDiskObject;
+                  Object dataDisksObject = properties2.get("dataDisks");
+                  ArrayList<Object> dataProperties = (ArrayList<Object>) dataDisksObject;
+                  LinkedTreeMap<String, String> datadiskObject = (LinkedTreeMap<String, String>) dataProperties.get(0);
+
+                  Assert.assertNotNull(osProperties.get("name"));
+                  Assert.assertNotNull(datadiskObject.get("name"));
+               }
+            }
+         }
+      }
    }
 
-   @Test(dependsOnMethods = {"testRestart", "testList", "testGet"}, alwaysRun = true)
+   @Test(dependsOnMethods = "testCapture", alwaysRun = true)
    public void testDelete() throws Exception {
       URI uri = api().delete(getName());
 
@@ -227,8 +284,10 @@ public class VirtualMachineApiLiveTest extends BaseAzureComputeApiLiveTest {
       VHD vhd = VHD.create(blob + "vhds/" + getName() + ".vhd");
       VHD vhd2 = VHD.create(blob + "vhds/" + getName() + "data.vhd");
       DataDisk dataDisk = DataDisk.create(getName() + "data", "100", 0, vhd2, "Empty");
-      OSDisk osDisk = OSDisk.create(null, getName(), vhd, "ReadWrite", "FromImage");
-      StorageProfile storageProfile = StorageProfile.create(imgRef, osDisk, null);
+      List<DataDisk> dataDisks = new ArrayList<DataDisk>();
+      dataDisks.add(dataDisk);
+      OSDisk osDisk = OSDisk.create(null, getName(), vhd, "ReadWrite", "FromImage", null);
+      StorageProfile storageProfile = StorageProfile.create(imgRef, osDisk, dataDisks);
       OSProfile.WindowsConfiguration windowsConfig = OSProfile.WindowsConfiguration.create(false, null, null, true,
               null);
       OSProfile osProfile = OSProfile.create(getName(), "azureuser", "RFe3&432dg", null, null, windowsConfig);