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:32 UTC

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

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);