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

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

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