JCLOUDS-1273/JCLOUDS-1226: Support multiple resource groups in ARM

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

Branch: refs/heads/master
Commit: 83c0a3c7b255ec744c6150ce76c40e8301280c79
Parents: cc13cfe
Author: Ignasi Barrera <[email protected]>
Authored: Mon Apr 24 14:49:57 2017 +0200
Committer: Ignasi Barrera <[email protected]>
Committed: Wed Apr 26 00:12:53 2017 +0200

----------------------------------------------------------------------
 .../azurecompute/arm/AzureComputeApi.java       |   8 +-
 .../arm/compute/AzureComputeService.java        |  15 +-
 .../arm/compute/AzureComputeServiceAdapter.java |  88 ++++----
 .../AzureComputeServiceContextModule.java       |  18 +-
 .../arm/compute/domain/LocationAndName.java     |  50 +++++
 .../domain/RegionAndIdAndIngressRules.java      |  66 ------
 .../compute/domain/ResourceGroupAndName.java    |  50 +++++
 .../ResourceGroupAndNameAndIngressRules.java    |  71 +++++++
 .../extensions/AzureComputeImageExtension.java  |  39 ++--
 .../AzureComputeSecurityGroupExtension.java     | 100 +++++-----
 .../compute/functions/CustomImageToVMImage.java |   4 +-
 .../NetworkSecurityGroupToSecurityGroup.java    |   7 +-
 .../functions/TemplateToAvailabilitySet.java    |   8 +-
 .../compute/functions/VMHardwareToHardware.java |  20 +-
 .../arm/compute/functions/VMImageToImage.java   |  25 ++-
 .../functions/VirtualMachineToNodeMetadata.java |  50 ++---
 .../functions/VirtualMachineToStatus.java       |  11 +-
 .../loaders/CreateSecurityGroupIfNeeded.java    |  15 +-
 .../compute/loaders/DefaultResourceGroup.java   |  62 ++++++
 .../loaders/ResourceGroupForLocation.java       |  62 ------
 .../compute/options/AzureTemplateOptions.java   |  78 ++++----
 .../arm/compute/strategy/CleanupResources.java  |  79 +++-----
 .../CreateResourceGroupThenCreateNodes.java     | 193 ------------------
 .../CreateResourcesThenCreateNodes.java         | 199 +++++++++++++++++++
 .../azurecompute/arm/domain/IdReference.java    |  39 +++-
 .../azurecompute/arm/domain/RegionAndId.java    |  50 -----
 .../azurecompute/arm/domain/VMHardware.java     |  11 +-
 .../azurecompute/arm/domain/VMImage.java        |   8 +
 .../azurecompute/arm/features/JobApi.java       |   2 +-
 .../jclouds/azurecompute/arm/util/VMImages.java |   2 +-
 .../compute/AzureComputeServiceLiveTest.java    |  29 +--
 .../compute/AzureTemplateBuilderLiveTest.java   |   2 +-
 .../AzureComputeImageExtensionLiveTest.java     |  24 +--
 ...reComputeSecurityGroupExtensionLiveTest.java |  40 ++--
 .../arm/domain/IdReferenceTest.java             |  62 ++++++
 .../arm/features/ImageApiLiveTest.java          |  30 +--
 .../arm/features/LoadBalancerApiLiveTest.java   |  50 ++---
 .../arm/internal/AzureLiveTestUtils.java        |  10 +-
 .../internal/BaseAzureComputeApiLiveTest.java   |   2 +-
 39 files changed, 884 insertions(+), 795 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java
index 70814868..a25690f 100644
--- 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java
+++ 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java
@@ -17,15 +17,16 @@
 package org.jclouds.azurecompute.arm;
 
 import java.io.Closeable;
+
 import javax.ws.rs.PathParam;
 
 import org.jclouds.azurecompute.arm.features.AvailabilitySetApi;
 import org.jclouds.azurecompute.arm.features.DeploymentApi;
+import org.jclouds.azurecompute.arm.features.DiskApi;
 import org.jclouds.azurecompute.arm.features.ImageApi;
 import org.jclouds.azurecompute.arm.features.JobApi;
 import org.jclouds.azurecompute.arm.features.LoadBalancerApi;
 import org.jclouds.azurecompute.arm.features.LocationApi;
-import org.jclouds.azurecompute.arm.features.DiskApi;
 import org.jclouds.azurecompute.arm.features.NetworkInterfaceCardApi;
 import org.jclouds.azurecompute.arm.features.NetworkSecurityGroupApi;
 import org.jclouds.azurecompute.arm.features.NetworkSecurityRuleApi;
@@ -47,7 +48,7 @@ import org.jclouds.rest.annotations.Delegate;
  * @see <a href="https://msdn.microsoft.com/en-us/library/azure/dn790568.aspx"; 
>doc</a>
  */
 public interface AzureComputeApi extends Closeable {
-
+   
    /**
     * The Azure Resource Manager API includes operations for managing resource 
groups in your subscription.
     *
@@ -56,6 +57,9 @@ public interface AzureComputeApi extends Closeable {
    @Delegate
    ResourceGroupApi getResourceGroupApi();
 
+   /**
+    * Provides access to the Job tracking API.
+    */
    @Delegate
    JobApi getJobApi();
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeService.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeService.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeService.java
index f566a69..dcb9c44 100644
--- 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeService.java
+++ 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeService.java
@@ -31,8 +31,8 @@ import javax.inject.Provider;
 import javax.inject.Singleton;
 
 import org.jclouds.Constants;
+import org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName;
 import org.jclouds.azurecompute.arm.compute.strategy.CleanupResources;
-import org.jclouds.azurecompute.arm.domain.ResourceGroup;
 import org.jclouds.collect.Memoized;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.callables.RunScriptOnNode;
@@ -62,7 +62,6 @@ import org.jclouds.scriptbuilder.functions.InitAdminAccess;
 import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
-import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.ListeningExecutorService;
@@ -70,7 +69,6 @@ import 
com.google.common.util.concurrent.ListeningExecutorService;
 @Singleton
 public class AzureComputeService extends BaseComputeService {
    private final CleanupResources cleanupResources;
-   private final LoadingCache<String, ResourceGroup> resourceGroupMap;
 
    @Inject
    protected AzureComputeService(ComputeServiceContext context, Map<String, 
Credentials> credentialStore,
@@ -89,14 +87,13 @@ public class AzureComputeService extends BaseComputeService 
{
          PersistNodeCredentials persistNodeCredentials, Timeouts timeouts,
          @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService 
userExecutor,
          CleanupResources cleanupResources, Optional<ImageExtension> 
imageExtension,
-         Optional<SecurityGroupExtension> securityGroupExtension, 
LoadingCache<String, ResourceGroup> resourceGroupMap) {
+         Optional<SecurityGroupExtension> securityGroupExtension) {
       super(context, credentialStore, images, sizes, locations, 
listNodesStrategy, getImageStrategy,
             getNodeMetadataStrategy, runNodesAndAddToSetStrategy, 
rebootNodeStrategy, destroyNodeStrategy,
             startNodeStrategy, stopNodeStrategy, templateBuilderProvider, 
templateOptionsProvider, nodeRunning,
             nodeTerminated, nodeSuspended, initScriptRunnerFactory, 
initAdminAccess, runScriptOnNodeFactory,
             persistNodeCredentials, timeouts, userExecutor, imageExtension, 
securityGroupExtension);
       this.cleanupResources = cleanupResources;
-      this.resourceGroupMap = resourceGroupMap;
    }
 
    @Override
@@ -105,11 +102,11 @@ public class AzureComputeService extends 
BaseComputeService {
       ImmutableSet.Builder<String> resourceGroups = ImmutableSet.builder();
 
       for (NodeMetadata deadNode : deadNodes) {
-         ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(deadNode.getLocation().getId());
-
-         resourceGroups.add(resourceGroup.name());
+         String resourceGroupName = 
ResourceGroupAndName.fromSlashEncoded(deadNode.getId()).resourceGroup();
+         resourceGroups.add(resourceGroupName);
+         
          if (deadNode.getGroup() != null) {
-            regionGroups.put(resourceGroup.name(), deadNode.getGroup());
+            regionGroups.put(resourceGroupName, deadNode.getGroup());
          }
 
          try {

http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
index 8f9b67c..2e9e0f6 100644
--- 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
+++ 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/AzureComputeServiceAdapter.java
@@ -19,10 +19,12 @@ package org.jclouds.azurecompute.arm.compute;
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.collect.ImmutableList.builder;
 import static com.google.common.collect.ImmutableList.of;
-import static com.google.common.collect.Iterables.contains;
 import static com.google.common.collect.Iterables.filter;
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static com.google.common.collect.Iterables.transform;
+import static com.google.common.collect.Lists.newArrayList;
+import static 
org.jclouds.azurecompute.arm.compute.domain.LocationAndName.fromSlashEncoded;
+import static 
org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName.fromResourceGroupAndName;
 import static 
org.jclouds.azurecompute.arm.compute.functions.VMImageToImage.decodeFieldsFromUniqueId;
 import static 
org.jclouds.azurecompute.arm.compute.functions.VMImageToImage.getMarketplacePlanFromImageMetadata;
 import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.IMAGE_PUBLISHERS;
@@ -40,6 +42,7 @@ import javax.inject.Singleton;
 
 import org.jclouds.azurecompute.arm.AzureComputeApi;
 import 
org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule.PublicIpAvailablePredicateFactory;
+import org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName;
 import org.jclouds.azurecompute.arm.compute.functions.CustomImageToVMImage;
 import org.jclouds.azurecompute.arm.compute.options.AzureTemplateOptions;
 import org.jclouds.azurecompute.arm.compute.strategy.CleanupResources;
@@ -62,7 +65,6 @@ import org.jclouds.azurecompute.arm.domain.Offer;
 import org.jclouds.azurecompute.arm.domain.Plan;
 import org.jclouds.azurecompute.arm.domain.PublicIPAddress;
 import org.jclouds.azurecompute.arm.domain.PublicIPAddressProperties;
-import org.jclouds.azurecompute.arm.domain.RegionAndId;
 import org.jclouds.azurecompute.arm.domain.ResourceGroup;
 import org.jclouds.azurecompute.arm.domain.ResourceProviderMetaData;
 import org.jclouds.azurecompute.arm.domain.SKU;
@@ -90,7 +92,6 @@ import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
 import com.google.common.base.Splitter;
 import com.google.common.base.Supplier;
-import com.google.common.cache.LoadingCache;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -115,20 +116,18 @@ public class AzureComputeServiceAdapter implements 
ComputeServiceAdapter<Virtual
    private final List<String> imagePublishers;
    private final Supplier<Set<String>> regionIds;
    private final PublicIpAvailablePredicateFactory publicIpAvailable;
-   private final LoadingCache<String, ResourceGroup> resourceGroupMap;
    private final CustomImageToVMImage customImagetoVmImage;
 
    @Inject
    AzureComputeServiceAdapter(final AzureComputeApi api, 
@Named(IMAGE_PUBLISHERS) String imagePublishers,
          CleanupResources cleanupResources, @Region Supplier<Set<String>> 
regionIds,
-         PublicIpAvailablePredicateFactory publicIpAvailable, 
LoadingCache<String, ResourceGroup> resourceGroupMap,
+         PublicIpAvailablePredicateFactory publicIpAvailable,
          CustomImageToVMImage customImagetoVmImage) {
       this.api = api;
       this.imagePublishers = 
Splitter.on(',').trimResults().omitEmptyStrings().splitToList(imagePublishers);
       this.cleanupResources = cleanupResources;
       this.regionIds = regionIds;
       this.publicIpAvailable = publicIpAvailable;
-      this.resourceGroupMap = resourceGroupMap;
       this.customImagetoVmImage = customImagetoVmImage;
    }
 
@@ -138,15 +137,15 @@ public class AzureComputeServiceAdapter implements 
ComputeServiceAdapter<Virtual
 
       String locationName = template.getLocation().getId();
       Image image = template.getImage();
-      String hardwareId = template.getHardware().getId();
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(locationName);
+      String hardwareId = 
fromSlashEncoded(template.getHardware().getId()).name();
       // TODO ARM specific options
       AzureTemplateOptions templateOptions = 
template.getOptions().as(AzureTemplateOptions.class);
       String subnetId = templateOptions.getSubnetId();
+      String resourceGroupName = templateOptions.getResourceGroup();
       
       IdReference availabilitySet = 
getAvailabilitySetIdReference(templateOptions.getAvailabilitySet());
       StorageProfile storageProfile = createStorageProfile(image, 
templateOptions.getDataDisks());
-      NetworkInterfaceCard nic = createNetworkInterfaceCard(subnetId, name, 
locationName, resourceGroup.name(), template.getOptions());
+      NetworkInterfaceCard nic = createNetworkInterfaceCard(subnetId, name, 
locationName, resourceGroupName, template.getOptions());
       HardwareProfile hardwareProfile = 
HardwareProfile.builder().vmSize(hardwareId).build();
       OSProfile osProfile = createOsProfile(name, template);
       NetworkProfile networkProfile = 
NetworkProfile.builder().networkInterfaces(of(IdReference.create(nic.id()))).build();
@@ -165,14 +164,14 @@ public class AzureComputeServiceAdapter implements 
ComputeServiceAdapter<Virtual
       Map<String, String> metadataAndTags = 
metadataAndTagsAsCommaDelimitedValue(template.getOptions());
       Plan plan = getMarketplacePlanFromImageMetadata(template.getImage());
 
-      VirtualMachine virtualMachine = 
api.getVirtualMachineApi(resourceGroup.name()).createOrUpdate(name, 
template.getLocation().getId(),
+      VirtualMachine virtualMachine = 
api.getVirtualMachineApi(resourceGroupName).createOrUpdate(name, 
template.getLocation().getId(),
             virtualMachineProperties, metadataAndTags, plan);
 
       // Safe to pass null credentials here, as jclouds will default populate
       // the node with the default credentials from the image, or the ones in
       // the options, if provided.
-      RegionAndId regionAndId = RegionAndId.fromRegionAndId(locationName, 
name);
-      return new NodeAndInitialCredentials<VirtualMachine>(virtualMachine, 
regionAndId.slashEncode(), null);
+      ResourceGroupAndName resourceGroupAndName = 
fromResourceGroupAndName(resourceGroupName, name);
+      return new NodeAndInitialCredentials<VirtualMachine>(virtualMachine, 
resourceGroupAndName.slashEncode(), null);
    }
 
    @Override
@@ -183,8 +182,7 @@ public class AzureComputeServiceAdapter implements 
ComputeServiceAdapter<Virtual
          for (VMSize vmSize : vmSizes) {
             VMHardware hwProfile = VMHardware
                     .create(vmSize.name(), vmSize.numberOfCores(), 
vmSize.osDiskSizeInMB(),
-                            vmSize.resourceDiskSizeInMB(), 
vmSize.memoryInMB(), vmSize.maxDataDiskCount(), location.name(),
-                            false);
+                            vmSize.resourceDiskSizeInMB(), 
vmSize.memoryInMB(), vmSize.maxDataDiskCount(), location.name());
             hwProfiles.add(hwProfile);
          }
       }
@@ -221,9 +219,8 @@ public class AzureComputeServiceAdapter implements 
ComputeServiceAdapter<Virtual
       return osImages;
    }
    
-   private List<VMImage> listCustomImagesByLocation(String location) {
-      ResourceGroup resourceGroup = resourceGroupMap.getUnchecked(location);
-      List<org.jclouds.azurecompute.arm.domain.Image> customImages = 
api.getVirtualMachineImageApi(resourceGroup.name()).list();
+   private List<VMImage> listCustomImagesByResourceGroup(String resourceGroup) 
{
+      List<org.jclouds.azurecompute.arm.domain.Image> customImages = 
api.getVirtualMachineImageApi(resourceGroup).list();
       return Lists.transform(customImages, customImagetoVmImage);
    }
 
@@ -231,16 +228,29 @@ public class AzureComputeServiceAdapter implements 
ComputeServiceAdapter<Virtual
    public Iterable<VMImage> listImages() {
       final ImmutableList.Builder<VMImage> osImages = ImmutableList.builder();
       
-      Iterable<String> availableLocationNames = transform(listLocations(), new 
Function<Location, String>() {
-         @Override
-         public String apply(Location location) {
-            return location.name();
-         }
-      });
+      final List<String> availableLocationNames = 
newArrayList(transform(listLocations(),
+            new Function<Location, String>() {
+               @Override
+               public String apply(Location location) {
+                  return location.name();
+               }
+            }));
 
       for (String locationName : availableLocationNames) {
          osImages.addAll(listImagesByLocation(locationName));
-         osImages.addAll(listCustomImagesByLocation(locationName));
+      }
+
+      // We need to look for custom images in all resource groups
+      Iterable<ResourceGroup> resourceGroupsInLocation = 
filter(api.getResourceGroupApi().list(),
+            new Predicate<ResourceGroup>() {
+               @Override
+               public boolean apply(ResourceGroup input) {
+                  return availableLocationNames.contains(input.location());
+               }
+            });
+      
+      for (ResourceGroup resourceGroup : resourceGroupsInLocation) {
+         
osImages.addAll(listCustomImagesByResourceGroup(resourceGroup.name()));
       }
 
       return osImages.build();
@@ -249,10 +259,10 @@ public class AzureComputeServiceAdapter implements 
ComputeServiceAdapter<Virtual
    @Override
    public VMImage getImage(final String id) {
       VMImage image = decodeFieldsFromUniqueId(id);
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(image.location());
 
       if (image.custom()) {
-         org.jclouds.azurecompute.arm.domain.Image vmImage = 
api.getVirtualMachineImageApi(resourceGroup.name()).get(image.name());
+         org.jclouds.azurecompute.arm.domain.Image vmImage = 
api.getVirtualMachineImageApi(image.resourceGroup()).get(
+               image.name());
          return vmImage == null ? null : customImagetoVmImage.apply(vmImage);
       }
 
@@ -304,9 +314,8 @@ public class AzureComputeServiceAdapter implements 
ComputeServiceAdapter<Virtual
 
    @Override
    public VirtualMachine getNode(final String id) {
-      RegionAndId regionAndId = RegionAndId.fromSlashEncoded(id);
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(regionAndId.region());
-      return 
api.getVirtualMachineApi(resourceGroup.name()).get(regionAndId.id());
+      ResourceGroupAndName resourceGroupAndName = 
ResourceGroupAndName.fromSlashEncoded(id);
+      return 
api.getVirtualMachineApi(resourceGroupAndName.resourceGroup()).get(resourceGroupAndName.name());
    }
 
    @Override
@@ -316,23 +325,20 @@ public class AzureComputeServiceAdapter implements 
ComputeServiceAdapter<Virtual
 
    @Override
    public void rebootNode(final String id) {
-      RegionAndId regionAndId = RegionAndId.fromSlashEncoded(id);
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(regionAndId.region());
-      api.getVirtualMachineApi(resourceGroup.name()).restart(regionAndId.id());
+      ResourceGroupAndName resourceGroupAndName = 
ResourceGroupAndName.fromSlashEncoded(id);
+      
api.getVirtualMachineApi(resourceGroupAndName.resourceGroup()).restart(resourceGroupAndName.name());
    }
 
    @Override
    public void resumeNode(final String id) {
-      RegionAndId regionAndId = RegionAndId.fromSlashEncoded(id);
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(regionAndId.region());
-      api.getVirtualMachineApi(resourceGroup.name()).start(regionAndId.id());
+      ResourceGroupAndName resourceGroupAndName = 
ResourceGroupAndName.fromSlashEncoded(id);
+      
api.getVirtualMachineApi(resourceGroupAndName.resourceGroup()).start(resourceGroupAndName.name());
    }
 
    @Override
    public void suspendNode(final String id) {
-      RegionAndId regionAndId = RegionAndId.fromSlashEncoded(id);
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(regionAndId.region());
-      api.getVirtualMachineApi(resourceGroup.name()).stop(regionAndId.id());
+      ResourceGroupAndName resourceGroupAndName = 
ResourceGroupAndName.fromSlashEncoded(id);
+      
api.getVirtualMachineApi(resourceGroupAndName.resourceGroup()).stop(resourceGroupAndName.name());
    }
 
    @Override
@@ -346,10 +352,10 @@ public class AzureComputeServiceAdapter implements 
ComputeServiceAdapter<Virtual
 
    @Override
    public Iterable<VirtualMachine> listNodesByIds(final Iterable<String> ids) {
-      return filter(listNodes(), new Predicate<VirtualMachine>() {
+      return transform(ids, new Function<String, VirtualMachine>() {
          @Override
-         public boolean apply(VirtualMachine virtualMachine) {
-            return contains(ids, virtualMachine.id());
+         public VirtualMachine apply(String input) {
+            return getNode(input);
          }
       });
    }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java
index cf3c90c..11d3ab1 100644
--- 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java
+++ 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzureComputeServiceContextModule.java
@@ -34,7 +34,7 @@ import javax.inject.Singleton;
 import org.jclouds.azurecompute.arm.AzureComputeApi;
 import org.jclouds.azurecompute.arm.compute.AzureComputeService;
 import org.jclouds.azurecompute.arm.compute.AzureComputeServiceAdapter;
-import org.jclouds.azurecompute.arm.compute.domain.RegionAndIdAndIngressRules;
+import 
org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndNameAndIngressRules;
 import 
org.jclouds.azurecompute.arm.compute.extensions.AzureComputeImageExtension;
 import 
org.jclouds.azurecompute.arm.compute.extensions.AzureComputeSecurityGroupExtension;
 import org.jclouds.azurecompute.arm.compute.functions.LocationToLocation;
@@ -44,9 +44,9 @@ import 
org.jclouds.azurecompute.arm.compute.functions.VMHardwareToHardware;
 import org.jclouds.azurecompute.arm.compute.functions.VMImageToImage;
 import 
org.jclouds.azurecompute.arm.compute.functions.VirtualMachineToNodeMetadata;
 import 
org.jclouds.azurecompute.arm.compute.loaders.CreateSecurityGroupIfNeeded;
-import org.jclouds.azurecompute.arm.compute.loaders.ResourceGroupForLocation;
+import org.jclouds.azurecompute.arm.compute.loaders.DefaultResourceGroup;
 import org.jclouds.azurecompute.arm.compute.options.AzureTemplateOptions;
-import 
org.jclouds.azurecompute.arm.compute.strategy.CreateResourceGroupThenCreateNodes;
+import 
org.jclouds.azurecompute.arm.compute.strategy.CreateResourcesThenCreateNodes;
 import org.jclouds.azurecompute.arm.domain.Image;
 import org.jclouds.azurecompute.arm.domain.Location;
 import org.jclouds.azurecompute.arm.domain.NetworkSecurityGroup;
@@ -116,12 +116,12 @@ public class AzureComputeServiceContextModule extends
 
       bind(TemplateOptions.class).to(AzureTemplateOptions.class);
       
bind(NodeAndTemplateOptionsToStatement.class).to(NodeAndTemplateOptionsToStatementWithoutPublicKey.class);
-      
bind(CreateNodesInGroupThenAddToSet.class).to(CreateResourceGroupThenCreateNodes.class);
+      
bind(CreateNodesInGroupThenAddToSet.class).to(CreateResourcesThenCreateNodes.class);
 
-      bind(new TypeLiteral<CacheLoader<RegionAndIdAndIngressRules, String>>() {
+      bind(new TypeLiteral<CacheLoader<ResourceGroupAndNameAndIngressRules, 
String>>() {
       }).to(CreateSecurityGroupIfNeeded.class);
       bind(new TypeLiteral<CacheLoader<String, ResourceGroup>>() {
-      }).to(ResourceGroupForLocation.class);
+      }).to(DefaultResourceGroup.class);
 
       bind(new TypeLiteral<ImageExtension>() {
       }).to(AzureComputeImageExtension.class);
@@ -131,14 +131,14 @@ public class AzureComputeServiceContextModule extends
 
    @Provides
    @Singleton
-   protected final LoadingCache<RegionAndIdAndIngressRules, String> 
securityGroupMap(
-         CacheLoader<RegionAndIdAndIngressRules, String> in) {
+   protected final LoadingCache<ResourceGroupAndNameAndIngressRules, String> 
securityGroupMap(
+         CacheLoader<ResourceGroupAndNameAndIngressRules, String> in) {
       return CacheBuilder.newBuilder().build(in);
    }
 
    @Provides
    @Singleton
-   protected final LoadingCache<String, ResourceGroup> 
resourceGroupMap(CacheLoader<String, ResourceGroup> in) {
+   protected final LoadingCache<String, ResourceGroup> 
defaultResourceGroup(CacheLoader<String, ResourceGroup> in) {
       return CacheBuilder.newBuilder().build(in);
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/domain/LocationAndName.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/domain/LocationAndName.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/domain/LocationAndName.java
new file mode 100644
index 0000000..0142448
--- /dev/null
+++ 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/domain/LocationAndName.java
@@ -0,0 +1,50 @@
+/*
+ * 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.domain;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+
+@AutoValue
+public abstract class LocationAndName {
+
+   public abstract String location();
+   public abstract String name();
+   
+   protected LocationAndName() {
+      
+   }
+   
+   public static LocationAndName fromSlashEncoded(String id) {
+      Iterable<String> parts = Splitter.on('/').split(checkNotNull(id, "id"));
+      checkArgument(Iterables.size(parts) == 2, "id must be in format 
location/name");
+      return new AutoValue_LocationAndName(Iterables.get(parts, 0), 
Iterables.get(parts, 1));
+   }
+
+   public static LocationAndName fromLocationAndName(String location, String 
name) {
+      return new AutoValue_LocationAndName(location, name);
+   }
+
+   public String slashEncode() {
+      return location() + "/" + name();
+   }
+   
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/domain/RegionAndIdAndIngressRules.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/domain/RegionAndIdAndIngressRules.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/domain/RegionAndIdAndIngressRules.java
deleted file mode 100644
index fa9730d..0000000
--- 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/domain/RegionAndIdAndIngressRules.java
+++ /dev/null
@@ -1,66 +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.domain;
-
-import org.jclouds.azurecompute.arm.domain.RegionAndId;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Objects;
-
-@AutoValue
-public abstract class RegionAndIdAndIngressRules {
-
-   abstract RegionAndId regionAndId(); // Intentionally hidden
-   public abstract int[] inboundPorts();
-
-   RegionAndIdAndIngressRules() {
-
-   }
-
-   public static RegionAndIdAndIngressRules create(String region, String id, 
int[] inboundPorts) {
-      return new 
AutoValue_RegionAndIdAndIngressRules(RegionAndId.fromRegionAndId(region, id), 
inboundPorts);
-   }
-
-   public String id() {
-      return regionAndId().id();
-   }
-
-   public String region() {
-      return regionAndId().region();
-   }
-
-   // Intentionally delegate equals and hashcode to the fields in the parent
-   // class so that we can search only by region/id in a map
-
-   @Override
-   public int hashCode() {
-      return Objects.hashCode(region(), id());
-   }
-
-   @Override
-   public boolean equals(Object obj) {
-      if (obj == this) {
-         return true;
-      }
-      if (!(obj instanceof RegionAndId)) {
-         return false;
-      }
-      RegionAndId that = (RegionAndId) obj;
-      return Objects.equal(region(), that.region()) && Objects.equal(id(), 
that.id());
-   }
-
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/domain/ResourceGroupAndName.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/domain/ResourceGroupAndName.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/domain/ResourceGroupAndName.java
new file mode 100644
index 0000000..c06056e
--- /dev/null
+++ 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/domain/ResourceGroupAndName.java
@@ -0,0 +1,50 @@
+/*
+ * 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.domain;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+
+@AutoValue
+public abstract class ResourceGroupAndName {
+
+   public abstract String resourceGroup();
+   public abstract String name();
+   
+   protected ResourceGroupAndName() {
+      
+   }
+   
+   public static ResourceGroupAndName fromSlashEncoded(String id) {
+      Iterable<String> parts = Splitter.on('/').split(checkNotNull(id, "id"));
+      checkArgument(Iterables.size(parts) == 2, "id must be in format 
resourcegroup/name");
+      return new AutoValue_ResourceGroupAndName(Iterables.get(parts, 0), 
Iterables.get(parts, 1));
+   }
+
+   public static ResourceGroupAndName fromResourceGroupAndName(String 
resourceGroup, String name) {
+      return new AutoValue_ResourceGroupAndName(resourceGroup, name);
+   }
+
+   public String slashEncode() {
+      return resourceGroup() + "/" + name();
+   }
+   
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/domain/ResourceGroupAndNameAndIngressRules.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/domain/ResourceGroupAndNameAndIngressRules.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/domain/ResourceGroupAndNameAndIngressRules.java
new file mode 100644
index 0000000..2b07406
--- /dev/null
+++ 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/domain/ResourceGroupAndNameAndIngressRules.java
@@ -0,0 +1,71 @@
+/*
+ * 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.domain;
+
+import static 
org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName.fromResourceGroupAndName;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Objects;
+
+@AutoValue
+public abstract class ResourceGroupAndNameAndIngressRules {
+
+   abstract ResourceGroupAndName resourceGroupAndName(); // Intentionally 
hidden
+   
+   public abstract String location();
+
+   public abstract int[] inboundPorts();
+
+   ResourceGroupAndNameAndIngressRules() {
+
+   }
+
+   public static ResourceGroupAndNameAndIngressRules create(String 
resourceGroup, String location, String name,
+         int[] inboundPorts) {
+      return new 
AutoValue_ResourceGroupAndNameAndIngressRules(fromResourceGroupAndName(resourceGroup,
 name), location,
+            inboundPorts);
+   }
+
+   public String name() {
+      return resourceGroupAndName().name();
+   }
+
+   public String resourceGroup() {
+      return resourceGroupAndName().resourceGroup();
+   }
+
+   // Intentionally delegate equals and hashcode to the fields in the parent
+   // class so that we can search only by region/id in a map
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(resourceGroup(), name());
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (obj == this) {
+         return true;
+      }
+      if (!(obj instanceof ResourceGroupAndName)) {
+         return false;
+      }
+      ResourceGroupAndName that = (ResourceGroupAndName) obj;
+      return Objects.equal(resourceGroup(), that.resourceGroup()) && 
Objects.equal(name(), that.name());
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeImageExtension.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeImageExtension.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeImageExtension.java
index 4bfa449..7d654d5 100644
--- 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeImageExtension.java
+++ 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeImageExtension.java
@@ -19,6 +19,7 @@ package org.jclouds.azurecompute.arm.compute.extensions;
 import static com.google.common.base.Functions.compose;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
+import static 
org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName.fromSlashEncoded;
 import static 
org.jclouds.azurecompute.arm.compute.functions.VMImageToImage.decodeFieldsFromUniqueId;
 import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.TIMEOUT_RESOURCE_DELETED;
 import static 
org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
@@ -32,11 +33,10 @@ import org.jclouds.Constants;
 import org.jclouds.azurecompute.arm.AzureComputeApi;
 import 
org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule.ImageAvailablePredicateFactory;
 import 
org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule.VirtualMachineInStatePredicateFactory;
+import org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName;
 import org.jclouds.azurecompute.arm.compute.functions.CustomImageToVMImage;
 import org.jclouds.azurecompute.arm.domain.IdReference;
 import org.jclouds.azurecompute.arm.domain.ImageProperties;
-import org.jclouds.azurecompute.arm.domain.RegionAndId;
-import org.jclouds.azurecompute.arm.domain.ResourceGroup;
 import org.jclouds.azurecompute.arm.domain.VMImage;
 import org.jclouds.azurecompute.arm.domain.VirtualMachine;
 import org.jclouds.compute.domain.CloneImageTemplate;
@@ -49,7 +49,6 @@ import org.jclouds.logging.Logger;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
-import com.google.common.cache.LoadingCache;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.inject.Inject;
@@ -66,25 +65,21 @@ public class AzureComputeImageExtension implements 
ImageExtension {
    private final ListeningExecutorService userExecutor;
    private final ImageAvailablePredicateFactory imageAvailablePredicate;
    private final VirtualMachineInStatePredicateFactory nodeSuspendedPredicate;
-   private final LoadingCache<String, ResourceGroup> resourceGroupMap;
    private final Function<VMImage, Image> vmImageToImage;
    private final Predicate<URI> resourceDeleted;
    private final CustomImageToVMImage customImagetoVmImage;
 
    @Inject
-   AzureComputeImageExtension(AzureComputeApi api,
-         ImageAvailablePredicateFactory imageAvailablePredicate,
+   AzureComputeImageExtension(AzureComputeApi api, 
ImageAvailablePredicateFactory imageAvailablePredicate,
          @Named(TIMEOUT_NODE_SUSPENDED) VirtualMachineInStatePredicateFactory 
nodeSuspendedPredicate,
          @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService 
userExecutor,
-         Function<VMImage, Image> vmImageToImage, LoadingCache<String, 
ResourceGroup> resourceGroupMap,
-         @Named(TIMEOUT_RESOURCE_DELETED) Predicate<URI> resourceDeleted,
+         Function<VMImage, Image> vmImageToImage, 
@Named(TIMEOUT_RESOURCE_DELETED) Predicate<URI> resourceDeleted,
          CustomImageToVMImage customImagetoVmImage) {
       this.api = api;
       this.imageAvailablePredicate = imageAvailablePredicate;
       this.nodeSuspendedPredicate = nodeSuspendedPredicate;
       this.userExecutor = userExecutor;
       this.vmImageToImage = vmImageToImage;
-      this.resourceGroupMap = resourceGroupMap;
       this.resourceDeleted = resourceDeleted;
       this.customImagetoVmImage = customImagetoVmImage;
    }
@@ -97,27 +92,27 @@ public class AzureComputeImageExtension implements 
ImageExtension {
    @Override
    public ListenableFuture<Image> createImage(ImageTemplate template) {
       final CloneImageTemplate cloneTemplate = (CloneImageTemplate) template;
-      final RegionAndId regionAndId = 
RegionAndId.fromSlashEncoded(cloneTemplate.getSourceNodeId());
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(regionAndId.region());
-      final String resourceGroupName = resourceGroup.name();
+      final ResourceGroupAndName resourceGroupAndName = 
fromSlashEncoded(cloneTemplate.getSourceNodeId());
+      final String resourceGroupName = resourceGroupAndName.resourceGroup();
+      final String vmName = resourceGroupAndName.name();
 
-      final VirtualMachine vm = 
api.getVirtualMachineApi(resourceGroupName).get(regionAndId.id());
+      final VirtualMachine vm = 
api.getVirtualMachineApi(resourceGroupName).get(vmName);
       final IdReference vmIdRef = IdReference.create(vm.id());
 
-      logger.debug(">> stopping node %s...", regionAndId.slashEncode());
-      api.getVirtualMachineApi(resourceGroupName).stop(regionAndId.id());
-      
checkState(nodeSuspendedPredicate.create(resourceGroupName).apply(regionAndId.id()),
-            "Node %s was not suspended within the configured time limit", 
regionAndId.slashEncode());
+      logger.debug(">> stopping node %s...", cloneTemplate.getSourceNodeId());
+      api.getVirtualMachineApi(resourceGroupName).stop(vmName);
+      
checkState(nodeSuspendedPredicate.create(resourceGroupName).apply(vmName),
+            "Node %s was not suspended within the configured time limit", 
cloneTemplate.getSourceNodeId());
 
       return userExecutor.submit(new Callable<Image>() {
          @Override
          public Image call() throws Exception {
-            logger.debug(">> generalizing virtal machine %s...", 
regionAndId.id());
+            logger.debug(">> generalizing virtal machine %s...", vmName);
 
-            
api.getVirtualMachineApi(resourceGroupName).generalize(regionAndId.id());
+            api.getVirtualMachineApi(resourceGroupName).generalize(vmName);
 
             org.jclouds.azurecompute.arm.domain.Image imageFromVM = 
api.getVirtualMachineImageApi(resourceGroupName)
-                  .createOrUpdate(cloneTemplate.getName(), 
regionAndId.region(),
+                  .createOrUpdate(cloneTemplate.getName(), vm.location(),
                         
ImageProperties.builder().sourceVirtualMachine(vmIdRef).build());
 
             
checkState(imageAvailablePredicate.create(resourceGroupName).apply(imageFromVM.name()),
@@ -134,9 +129,7 @@ public class AzureComputeImageExtension implements 
ImageExtension {
       checkArgument(image.custom(), "Only custom images can be deleted");
 
       logger.debug(">> deleting image %s", id);
-
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(image.location());
-      URI uri = 
api.getVirtualMachineImageApi(resourceGroup.name()).delete(image.name());
+      URI uri = 
api.getVirtualMachineImageApi(image.resourceGroup()).delete(image.name());
       return resourceDeleted.apply(uri);
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtension.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtension.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtension.java
index f23cfd8..eafb4f1 100644
--- 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtension.java
+++ 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtension.java
@@ -20,10 +20,10 @@ import static 
com.google.common.base.Preconditions.checkState;
 import static com.google.common.base.Predicates.equalTo;
 import static com.google.common.base.Predicates.notNull;
 import static com.google.common.collect.Iterables.any;
-import static com.google.common.collect.Iterables.concat;
 import static com.google.common.collect.Iterables.filter;
 import static com.google.common.collect.Iterables.transform;
 import static 
org.jclouds.azurecompute.arm.config.AzureComputeProperties.TIMEOUT_RESOURCE_DELETED;
+import static org.jclouds.compute.predicates.NodePredicates.locationId;
 
 import java.net.URI;
 import java.util.ArrayList;
@@ -36,6 +36,7 @@ import javax.inject.Named;
 
 import org.jclouds.azurecompute.arm.AzureComputeApi;
 import 
org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule.SecurityGroupAvailablePredicateFactory;
+import org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName;
 import org.jclouds.azurecompute.arm.domain.IdReference;
 import org.jclouds.azurecompute.arm.domain.NetworkInterfaceCard;
 import org.jclouds.azurecompute.arm.domain.NetworkSecurityGroup;
@@ -45,12 +46,10 @@ import 
org.jclouds.azurecompute.arm.domain.NetworkSecurityRuleProperties;
 import 
org.jclouds.azurecompute.arm.domain.NetworkSecurityRuleProperties.Access;
 import 
org.jclouds.azurecompute.arm.domain.NetworkSecurityRuleProperties.Direction;
 import 
org.jclouds.azurecompute.arm.domain.NetworkSecurityRuleProperties.Protocol;
-import org.jclouds.azurecompute.arm.domain.RegionAndId;
 import org.jclouds.azurecompute.arm.domain.ResourceGroup;
 import org.jclouds.azurecompute.arm.domain.VirtualMachine;
 import org.jclouds.azurecompute.arm.features.NetworkSecurityGroupApi;
 import org.jclouds.azurecompute.arm.features.NetworkSecurityRuleApi;
-import org.jclouds.collect.Memoized;
 import org.jclouds.compute.domain.SecurityGroup;
 import org.jclouds.compute.domain.SecurityGroupBuilder;
 import org.jclouds.compute.extensions.SecurityGroupExtension;
@@ -63,11 +62,8 @@ import org.jclouds.net.domain.IpProtocol;
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
-import com.google.common.base.Splitter;
-import com.google.common.base.Supplier;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Ordering;
 
@@ -78,64 +74,67 @@ public class AzureComputeSecurityGroupExtension implements 
SecurityGroupExtensio
 
    private final AzureComputeApi api;
    private final Function<NetworkSecurityGroup, SecurityGroup> 
securityGroupConverter;
-   private final Supplier<Set<? extends Location>> locations;
    private final SecurityGroupAvailablePredicateFactory securityGroupAvailable;
    private final Predicate<URI> resourceDeleted;
-   private final LoadingCache<String, ResourceGroup> resourceGroupMap;
+   private final LoadingCache<String, ResourceGroup> defaultResourceGroup;
 
    @Inject
-   AzureComputeSecurityGroupExtension(AzureComputeApi api, @Memoized 
Supplier<Set<? extends Location>> locations,
+   AzureComputeSecurityGroupExtension(AzureComputeApi api,
          Function<NetworkSecurityGroup, SecurityGroup> groupConverter,
          SecurityGroupAvailablePredicateFactory securityRuleAvailable,
          @Named(TIMEOUT_RESOURCE_DELETED) Predicate<URI> resourceDeleted,
-         LoadingCache<String, ResourceGroup> resourceGroupMap) {
+         LoadingCache<String, ResourceGroup> defaultResourceGroup) {
       this.api = api;
-      this.locations = locations;
       this.securityGroupConverter = groupConverter;
       this.securityGroupAvailable = securityRuleAvailable;
       this.resourceDeleted = resourceDeleted;
-      this.resourceGroupMap = resourceGroupMap;
+      this.defaultResourceGroup = defaultResourceGroup;
    }
 
    @Override
    public Set<SecurityGroup> listSecurityGroups() {
-      return ImmutableSet.copyOf(concat(transform(locations.get(), new 
Function<Location, Set<SecurityGroup>>() {
-         @Override
-         public Set<SecurityGroup> apply(Location input) {
-            return listSecurityGroupsInLocation(input);
-         }
-      })));
+      ImmutableSet.Builder<SecurityGroup> securityGroups = 
ImmutableSet.builder();
+      for (ResourceGroup rg : api.getResourceGroupApi().list()) {
+         securityGroups.addAll(securityGroupsInResourceGroup(rg.name()));
+      }
+      return securityGroups.build();
+   }
+   
+   private Set<SecurityGroup> securityGroupsInResourceGroup(String 
resourceGroup) {
+      List<NetworkSecurityGroup> networkGroups = 
api.getNetworkSecurityGroupApi(resourceGroup).list();
+      return ImmutableSet.copyOf(transform(filter(networkGroups, notNull()), 
securityGroupConverter));
    }
 
    @Override
    public Set<SecurityGroup> listSecurityGroupsInLocation(Location location) {
-      logger.debug(">> getting security groups for %s...", location);
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(location.getId());
-      List<NetworkSecurityGroup> networkGroups = 
api.getNetworkSecurityGroupApi(resourceGroup.name()).list();
-      return ImmutableSet.copyOf(transform(filter(networkGroups, notNull()), 
securityGroupConverter));
+      // Even though the resource groups are in a location, each security group
+      // can be in a different resource group, so we need to inspect all teh
+      // existing resource groups, and filter afterwards
+      return ImmutableSet.copyOf(filter(listSecurityGroups(), 
locationId(location.getId())));
    }
 
    @Override
    public Set<SecurityGroup> listSecurityGroupsForNode(String nodeId) {
       logger.debug(">> getting security groups for node %s...", nodeId);
 
-      final RegionAndId regionAndId = RegionAndId.fromSlashEncoded(nodeId);
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(regionAndId.region());
+      final ResourceGroupAndName resourceGroupAndName = 
ResourceGroupAndName.fromSlashEncoded(nodeId);
 
-      VirtualMachine vm = 
api.getVirtualMachineApi(resourceGroup.name()).get(regionAndId.id());
+      VirtualMachine vm = 
api.getVirtualMachineApi(resourceGroupAndName.resourceGroup()).get(
+            resourceGroupAndName.name());
       if (vm == null) {
-         throw new IllegalArgumentException("Node " + regionAndId.id() + " was 
not found");
+         throw new IllegalArgumentException("Node " + nodeId + " was not 
found");
       }
       List<IdReference> networkInterfacesIdReferences = 
vm.properties().networkProfile().networkInterfaces();
       List<NetworkSecurityGroup> networkGroups = new 
ArrayList<NetworkSecurityGroup>();
 
       for (IdReference networkInterfaceCardIdReference : 
networkInterfacesIdReferences) {
-         String nicName = 
Iterables.getLast(Splitter.on("/").split(networkInterfaceCardIdReference.id()));
-         NetworkInterfaceCard card = 
api.getNetworkInterfaceCardApi(resourceGroup.name()).get(nicName);
+         String nicName = networkInterfaceCardIdReference.name();
+         String nicResourceGroup = 
networkInterfaceCardIdReference.resourceGroup();
+         NetworkInterfaceCard card = 
api.getNetworkInterfaceCardApi(nicResourceGroup).get(nicName);
          if (card != null && card.properties().networkSecurityGroup() != null) 
{
-            String secGroupName = Iterables.getLast(Splitter.on("/").split(
-                  card.properties().networkSecurityGroup().id()));
-            NetworkSecurityGroup group = 
api.getNetworkSecurityGroupApi(resourceGroup.name()).get(secGroupName);
+            String secGroupName = 
card.properties().networkSecurityGroup().name();
+            String sgResourceGroup = 
card.properties().networkSecurityGroup().resourceGroup();
+            NetworkSecurityGroup group = 
api.getNetworkSecurityGroupApi(sgResourceGroup).get(secGroupName);
             networkGroups.add(group);
          }
       }
@@ -146,15 +145,15 @@ public class AzureComputeSecurityGroupExtension 
implements SecurityGroupExtensio
    @Override
    public SecurityGroup getSecurityGroupById(String id) {
       logger.debug(">> getting security group %s...", id);
-      final RegionAndId regionAndId = RegionAndId.fromSlashEncoded(id);
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(regionAndId.region());
-      NetworkSecurityGroup securityGroup = 
api.getNetworkSecurityGroupApi(resourceGroup.name()).get(regionAndId.id());
+      final ResourceGroupAndName resourceGroupAndName = 
ResourceGroupAndName.fromSlashEncoded(id);
+      NetworkSecurityGroup securityGroup = 
api.getNetworkSecurityGroupApi(resourceGroupAndName.resourceGroup()).get(
+            resourceGroupAndName.name());
       return securityGroup == null ? null : 
securityGroupConverter.apply(securityGroup);
    }
 
    @Override
    public SecurityGroup createSecurityGroup(String name, Location location) {
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(location.getId());
+      ResourceGroup resourceGroup = 
defaultResourceGroup.getUnchecked(location.getId());
 
       logger.debug(">> creating security group %s in %s...", name, location);
 
@@ -170,9 +169,9 @@ public class AzureComputeSecurityGroupExtension implements 
SecurityGroupExtensio
    public boolean removeSecurityGroup(String id) {
       logger.debug(">> deleting security group %s...", id);
 
-      final RegionAndId regionAndId = RegionAndId.fromSlashEncoded(id);
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(regionAndId.region());
-      URI uri = 
api.getNetworkSecurityGroupApi(resourceGroup.name()).delete(regionAndId.id());
+      final ResourceGroupAndName resourceGroupAndName = 
ResourceGroupAndName.fromSlashEncoded(id);
+      URI uri = 
api.getNetworkSecurityGroupApi(resourceGroupAndName.resourceGroup())
+            .delete(resourceGroupAndName.name());
       return resourceDeleted.apply(uri);
    }
 
@@ -199,17 +198,16 @@ public class AzureComputeSecurityGroupExtension 
implements SecurityGroupExtensio
 
       // TODO: Support Azure network tags somehow?
 
-      final RegionAndId regionAndId = 
RegionAndId.fromSlashEncoded(group.getId());
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(regionAndId.region());
+      final ResourceGroupAndName resourceGroupAndName = 
ResourceGroupAndName.fromSlashEncoded(group.getId());
 
-      NetworkSecurityGroupApi groupApi = 
api.getNetworkSecurityGroupApi(resourceGroup.name());
-      NetworkSecurityGroup networkSecurityGroup = 
groupApi.get(regionAndId.id());
+      NetworkSecurityGroupApi groupApi = 
api.getNetworkSecurityGroupApi(resourceGroupAndName.resourceGroup());
+      NetworkSecurityGroup networkSecurityGroup = 
groupApi.get(resourceGroupAndName.name());
 
       if (networkSecurityGroup == null) {
          throw new IllegalArgumentException("Security group " + 
group.getName() + " was not found");
       }
 
-      NetworkSecurityRuleApi ruleApi = 
api.getNetworkSecurityRuleApi(resourceGroup.name(), 
networkSecurityGroup.name());
+      NetworkSecurityRuleApi ruleApi = 
api.getNetworkSecurityRuleApi(resourceGroupAndName.resourceGroup(), 
networkSecurityGroup.name());
       int nextPriority = getRuleStartingPriority(networkSecurityGroup);
 
       for (String ipRange : ipRanges) {
@@ -228,7 +226,8 @@ public class AzureComputeSecurityGroupExtension implements 
SecurityGroupExtensio
 
          ruleApi.createOrUpdate(ruleName, properties);
 
-         
checkState(securityGroupAvailable.create(resourceGroup.name()).apply(networkSecurityGroup.name()),
+         checkState(
+               
securityGroupAvailable.create(resourceGroupAndName.resourceGroup()).apply(networkSecurityGroup.name()),
                "Security group was not updated in the configured timeout");
       }
 
@@ -244,17 +243,17 @@ public class AzureComputeSecurityGroupExtension 
implements SecurityGroupExtensio
 
       logger.debug(">> deleting ip permissions matching [%s] from %s...", 
ruleName, group.getName());
 
-      final RegionAndId regionAndId = 
RegionAndId.fromSlashEncoded(group.getId());
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(regionAndId.region());
+      final ResourceGroupAndName resourceGroupAndName = 
ResourceGroupAndName.fromSlashEncoded(group.getId());
 
-      NetworkSecurityGroupApi groupApi = 
api.getNetworkSecurityGroupApi(resourceGroup.name());
-      NetworkSecurityGroup networkSecurityGroup = 
groupApi.get(regionAndId.id());
+      NetworkSecurityGroupApi groupApi = 
api.getNetworkSecurityGroupApi(resourceGroupAndName.resourceGroup());
+      NetworkSecurityGroup networkSecurityGroup = 
groupApi.get(resourceGroupAndName.name());
 
       if (networkSecurityGroup == null) {
          throw new IllegalArgumentException("Security group " + 
group.getName() + " was not found");
       }
 
-      NetworkSecurityRuleApi ruleApi = 
api.getNetworkSecurityRuleApi(resourceGroup.name(), 
networkSecurityGroup.name());
+      NetworkSecurityRuleApi ruleApi = 
api.getNetworkSecurityRuleApi(resourceGroupAndName.resourceGroup(),
+            networkSecurityGroup.name());
       Iterable<NetworkSecurityRule> rules = filter(ruleApi.list(), new 
Predicate<NetworkSecurityRule>() {
          @Override
          public boolean apply(NetworkSecurityRule input) {
@@ -270,7 +269,8 @@ public class AzureComputeSecurityGroupExtension implements 
SecurityGroupExtensio
       for (NetworkSecurityRule matchingRule : rules) {
          logger.debug(">> deleting network security rule %s from %s...", 
matchingRule.name(), group.getName());
          ruleApi.delete(matchingRule.name());
-         
checkState(securityGroupAvailable.create(resourceGroup.name()).apply(networkSecurityGroup.name()),
+         checkState(
+               
securityGroupAvailable.create(resourceGroupAndName.resourceGroup()).apply(networkSecurityGroup.name()),
                "Security group was not updated in the configured timeout");
       }
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/CustomImageToVMImage.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/CustomImageToVMImage.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/CustomImageToVMImage.java
index 9cb2188..2220535 100644
--- 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/CustomImageToVMImage.java
+++ 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/CustomImageToVMImage.java
@@ -16,6 +16,8 @@
  */
 package org.jclouds.azurecompute.arm.compute.functions;
 
+import static 
org.jclouds.azurecompute.arm.domain.IdReference.extractResourceGroup;
+
 import org.jclouds.azurecompute.arm.domain.Image;
 import org.jclouds.azurecompute.arm.domain.VMImage;
 
@@ -25,7 +27,7 @@ public class CustomImageToVMImage implements Function<Image, 
VMImage> {
 
    @Override
    public VMImage apply(Image input) {
-      return 
VMImage.customImage().customImageId(input.id()).location(input.location()).name(input.name())
+      return 
VMImage.customImage().resourceGroup(extractResourceGroup(input.id())).customImageId(input.id()).location(input.location()).name(input.name())
             
.offer(input.properties().storageProfile().osDisk().osType()).build();
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/NetworkSecurityGroupToSecurityGroup.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/NetworkSecurityGroupToSecurityGroup.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/NetworkSecurityGroupToSecurityGroup.java
index 65f5b0d..71d51aa 100644
--- 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/NetworkSecurityGroupToSecurityGroup.java
+++ 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/NetworkSecurityGroupToSecurityGroup.java
@@ -18,8 +18,10 @@ package org.jclouds.azurecompute.arm.compute.functions;
 
 import static com.google.common.collect.Iterables.filter;
 import static com.google.common.collect.Iterables.transform;
+import static 
org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName.fromResourceGroupAndName;
 import static 
org.jclouds.azurecompute.arm.compute.functions.NetworkSecurityRuleToIpPermission.InboundRule;
 import static 
org.jclouds.azurecompute.arm.compute.functions.VirtualMachineToNodeMetadata.getLocation;
+import static 
org.jclouds.azurecompute.arm.domain.IdReference.extractResourceGroup;
 
 import java.util.Set;
 
@@ -27,7 +29,6 @@ import javax.inject.Singleton;
 
 import org.jclouds.azurecompute.arm.domain.NetworkSecurityGroup;
 import org.jclouds.azurecompute.arm.domain.NetworkSecurityRule;
-import org.jclouds.azurecompute.arm.domain.RegionAndId;
 import org.jclouds.collect.Memoized;
 import org.jclouds.compute.domain.SecurityGroup;
 import org.jclouds.compute.domain.SecurityGroupBuilder;
@@ -54,8 +55,8 @@ public class NetworkSecurityGroupToSecurityGroup implements 
Function<NetworkSecu
    public SecurityGroup apply(NetworkSecurityGroup input) {
       SecurityGroupBuilder builder = new SecurityGroupBuilder();
 
-      builder.id(RegionAndId.fromRegionAndId(input.location(), 
input.name()).slashEncode());
-      builder.providerId(input.properties().resourceGuid());
+      builder.id(fromResourceGroupAndName(extractResourceGroup(input.id()), 
input.name()).slashEncode());
+      builder.providerId(input.id());
       builder.name(input.name());
       builder.location(getLocation(locations, input.location()));
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/TemplateToAvailabilitySet.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/TemplateToAvailabilitySet.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/TemplateToAvailabilitySet.java
index 2732b6e..7359ace 100644
--- 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/TemplateToAvailabilitySet.java
+++ 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/TemplateToAvailabilitySet.java
@@ -29,14 +29,12 @@ import javax.inject.Singleton;
 import org.jclouds.azurecompute.arm.AzureComputeApi;
 import org.jclouds.azurecompute.arm.compute.options.AzureTemplateOptions;
 import org.jclouds.azurecompute.arm.domain.AvailabilitySet;
-import org.jclouds.azurecompute.arm.domain.ResourceGroup;
 import org.jclouds.compute.domain.Template;
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.logging.Logger;
 
 import com.google.common.base.Function;
-import com.google.common.cache.LoadingCache;
 
 @Singleton
 public class TemplateToAvailabilitySet implements Function<Template, 
AvailabilitySet> {
@@ -46,12 +44,10 @@ public class TemplateToAvailabilitySet implements 
Function<Template, Availabilit
    protected Logger logger = Logger.NULL;
 
    private final AzureComputeApi api;
-   private final LoadingCache<String, ResourceGroup> resourceGroupMap;
 
    @Inject
-   TemplateToAvailabilitySet(AzureComputeApi api, LoadingCache<String, 
ResourceGroup> resourceGroupMap) {
+   TemplateToAvailabilitySet(AzureComputeApi api) {
       this.api = api;
-      this.resourceGroupMap = resourceGroupMap;
    }
 
    @Nullable
@@ -62,7 +58,7 @@ public class TemplateToAvailabilitySet implements 
Function<Template, Availabilit
 
       AvailabilitySet availabilitySet = null;
       String location = input.getLocation().getId();
-      String resourceGroup = resourceGroupMap.getUnchecked(location).name();
+      String resourceGroup = options.getResourceGroup();
 
       if (options.getAvailabilitySetName() != null) {
          availabilitySet = 
api.getAvailabilitySetApi(resourceGroup).get(options.getAvailabilitySetName());

http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java
index 5303e25..d877aba 100644
--- 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java
+++ 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMHardwareToHardware.java
@@ -16,9 +16,10 @@
  */
 package org.jclouds.azurecompute.arm.compute.functions;
 
-import com.google.common.base.Supplier;
-import com.google.common.collect.FluentIterable;
-import com.google.inject.Inject;
+import static 
org.jclouds.azurecompute.arm.compute.domain.LocationAndName.fromLocationAndName;
+
+import java.util.Set;
+
 import org.jclouds.azurecompute.arm.domain.VMHardware;
 import org.jclouds.collect.Memoized;
 import org.jclouds.compute.domain.Hardware;
@@ -26,14 +27,15 @@ import org.jclouds.compute.domain.HardwareBuilder;
 import org.jclouds.compute.domain.Processor;
 import org.jclouds.compute.domain.Volume;
 import org.jclouds.compute.domain.VolumeBuilder;
+import org.jclouds.domain.Location;
+import org.jclouds.location.predicates.LocationPredicates;
 
 import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import org.jclouds.domain.Location;
-import org.jclouds.location.predicates.LocationPredicates;
-
-import java.util.Set;
+import com.google.inject.Inject;
 
 public class VMHardwareToHardware implements Function<VMHardware, Hardware> {
 
@@ -49,10 +51,10 @@ public class VMHardwareToHardware implements 
Function<VMHardware, Hardware> {
       final HardwareBuilder builder = new HardwareBuilder()
               .name(from.name())
               .providerId(from.name())
-              .id(from.name())
+              .id(fromLocationAndName(from.location(), 
from.name()).slashEncode())
               .processors(ImmutableList.of(new Processor(from.numberOfCores(), 
2)))
               .ram(from.memoryInMB())
-              .location(from.globallyAvailable() ? null : 
FluentIterable.from(locations.get())
+              .location(FluentIterable.from(locations.get())
                       .firstMatch(LocationPredicates.idEquals(from.location()))
                       .get());
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java
index 4f02100..2a5075c 100644
--- 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java
+++ 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VMImageToImage.java
@@ -16,6 +16,12 @@
  */
 package org.jclouds.azurecompute.arm.compute.functions;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.tryFind;
+import static java.util.Arrays.asList;
+import static 
org.jclouds.azurecompute.arm.domain.IdReference.extractResourceGroup;
+import static org.jclouds.azurecompute.arm.util.VMImages.isCustom;
+
 import java.util.Map;
 import java.util.Set;
 
@@ -40,11 +46,6 @@ import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Inject;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.Iterables.tryFind;
-import static java.util.Arrays.asList;
-import static org.jclouds.azurecompute.arm.util.VMImages.isCustom;
-
 public class VMImageToImage implements Function<VMImage, Image> {
 
    private static final Map<String, OsFamily> OTHER_OS_MAP = 
ImmutableMap.<String, OsFamily> builder()
@@ -64,8 +65,8 @@ public class VMImageToImage implements Function<VMImage, 
Image> {
 
    public static String encodeFieldsToUniqueIdCustom(boolean 
globallyAvailable, String locationName,
          ImageReference imageReference) {
-      return (globallyAvailable ? "global" : locationName) + "/" + 
imageReference.customImageId()
-            .substring(imageReference.customImageId().lastIndexOf("/") + 1);
+      return extractResourceGroup(imageReference.customImageId()) + "/" + 
(globallyAvailable ? "global" : locationName)
+            + "/" + 
imageReference.customImageId().substring(imageReference.customImageId().lastIndexOf("/")
 + 1);
    }
 
    public static String encodeFieldsToUniqueId(VMImage imageReference) {
@@ -74,7 +75,8 @@ public class VMImageToImage implements Function<VMImage, 
Image> {
    }
 
    public static String encodeFieldsToUniqueIdCustom(VMImage imageReference) {
-      return (imageReference.globallyAvailable() ? "global" : 
imageReference.location()) + "/" + imageReference.name();
+      return imageReference.resourceGroup() + "/"
+            + (imageReference.globallyAvailable() ? "global" : 
imageReference.location()) + "/" + imageReference.name();
    }
 
    public static VMImage decodeFieldsFromUniqueId(final String id) {
@@ -82,10 +84,11 @@ public class VMImageToImage implements Function<VMImage, 
Image> {
       String[] fields = checkNotNull(id, "id").split("/");
       if (isCustom(id)) {
          /* id fields indexes
-         0: imageReference.location + "/" +
-         1: imageReference.name
+         0: imageReference.resourceGroup
+         1: imageReference.location + "/" +
+         2: imageReference.name
          */
-         vmImage = 
VMImage.customImage().location(fields[0]).name(fields[1]).build();
+         vmImage = 
VMImage.customImage().resourceGroup(fields[0]).location(fields[1]).name(fields[2]).build();
       } else {
          /* id fields indexes
          0: imageReference.location + "/" +

http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToNodeMetadata.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToNodeMetadata.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToNodeMetadata.java
index 9bad6e5..bcfd4fe 100644
--- 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToNodeMetadata.java
+++ 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToNodeMetadata.java
@@ -20,8 +20,11 @@ import static 
com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Strings.nullToEmpty;
 import static com.google.common.collect.Iterables.find;
 import static 
org.jclouds.azurecompute.arm.compute.AzureComputeServiceAdapter.GROUP_KEY;
+import static 
org.jclouds.azurecompute.arm.compute.domain.LocationAndName.fromLocationAndName;
+import static 
org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName.fromResourceGroupAndName;
 import static 
org.jclouds.azurecompute.arm.compute.functions.VMImageToImage.encodeFieldsToUniqueId;
 import static 
org.jclouds.azurecompute.arm.compute.functions.VMImageToImage.encodeFieldsToUniqueIdCustom;
+import static 
org.jclouds.azurecompute.arm.domain.IdReference.extractResourceGroup;
 import static 
org.jclouds.compute.util.ComputeServiceUtils.addMetadataAndParseTagsFromCommaDelimitedValue;
 import static org.jclouds.location.predicates.LocationPredicates.idEquals;
 
@@ -34,13 +37,12 @@ import javax.inject.Inject;
 import javax.inject.Named;
 
 import org.jclouds.azurecompute.arm.AzureComputeApi;
+import org.jclouds.azurecompute.arm.compute.domain.LocationAndName;
 import 
org.jclouds.azurecompute.arm.compute.functions.VirtualMachineToStatus.StatusAndBackendStatus;
 import org.jclouds.azurecompute.arm.domain.IdReference;
 import org.jclouds.azurecompute.arm.domain.IpConfiguration;
 import org.jclouds.azurecompute.arm.domain.NetworkInterfaceCard;
 import org.jclouds.azurecompute.arm.domain.PublicIPAddress;
-import org.jclouds.azurecompute.arm.domain.RegionAndId;
-import org.jclouds.azurecompute.arm.domain.ResourceGroup;
 import org.jclouds.azurecompute.arm.domain.StorageProfile;
 import org.jclouds.azurecompute.arm.domain.VirtualMachine;
 import org.jclouds.collect.Memoized;
@@ -58,10 +60,7 @@ import org.jclouds.logging.Logger;
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
-import com.google.common.base.Splitter;
 import com.google.common.base.Supplier;
-import com.google.common.cache.LoadingCache;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
 public class VirtualMachineToNodeMetadata implements Function<VirtualMachine, 
NodeMetadata> {
@@ -74,20 +73,18 @@ public class VirtualMachineToNodeMetadata implements 
Function<VirtualMachine, No
    private final GroupNamingConvention nodeNamingConvention;
    private final Supplier<Set<? extends Location>> locations;
    private final Supplier<Map<String, ? extends Hardware>> hardwares;
-   private final LoadingCache<String, ResourceGroup> resourceGroupMap;
    private final ImageCacheSupplier imageCache;
    private final VirtualMachineToStatus virtualMachineToStatus;
 
    @Inject
    VirtualMachineToNodeMetadata(AzureComputeApi api, 
GroupNamingConvention.Factory namingConvention,
          Supplier<Map<String, ? extends Hardware>> hardwares, @Memoized 
Supplier<Set<? extends Location>> locations,
-         Map<String, Credentials> credentialStore, LoadingCache<String, 
ResourceGroup> resourceGroupMap,
-         @Memoized Supplier<Set<? extends Image>> imageCache, 
VirtualMachineToStatus virtualMachineToStatus) {
+         Map<String, Credentials> credentialStore, @Memoized Supplier<Set<? 
extends Image>> imageCache,
+         VirtualMachineToStatus virtualMachineToStatus) {
       this.api = api;
       this.nodeNamingConvention = namingConvention.createWithoutPrefix();
       this.locations = locations;
       this.hardwares = hardwares;
-      this.resourceGroupMap = resourceGroupMap;
       this.virtualMachineToStatus = virtualMachineToStatus;
       checkArgument(imageCache instanceof ImageCacheSupplier,
             "This provider needs an instance of the ImageCacheSupplier");
@@ -96,10 +93,9 @@ public class VirtualMachineToNodeMetadata implements 
Function<VirtualMachine, No
 
    @Override
    public NodeMetadata apply(VirtualMachine virtualMachine) {
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(virtualMachine.location());
-
       NodeMetadataBuilder builder = new NodeMetadataBuilder();
-      builder.id(RegionAndId.fromRegionAndId(virtualMachine.location(), 
virtualMachine.name()).slashEncode());
+      
builder.id(fromResourceGroupAndName(extractResourceGroup(virtualMachine.id()), 
virtualMachine.name())
+            .slashEncode());
       builder.providerId(virtualMachine.id());
       builder.name(virtualMachine.name());
       builder.hostname(virtualMachine.name());
@@ -125,8 +121,7 @@ public class VirtualMachineToNodeMetadata implements 
Function<VirtualMachine, No
       String locationName = virtualMachine.location();
       builder.location(getLocation(locations, locationName));
 
-      Optional<? extends Image> image = 
findImage(virtualMachine.properties().storageProfile(), locationName,
-            resourceGroup.name());
+      Optional<? extends Image> image = 
findImage(virtualMachine.properties().storageProfile(), locationName);
       
       if (image.isPresent()) {
          builder.imageId(image.get().getId());
@@ -137,7 +132,8 @@ public class VirtualMachineToNodeMetadata implements 
Function<VirtualMachine, No
                virtualMachine.id(), virtualMachine.id());
       }
 
-      
builder.hardware(getHardware(virtualMachine.properties().hardwareProfile().vmSize()));
+      
builder.hardware(getHardware(fromLocationAndName(virtualMachine.location(), 
virtualMachine.properties()
+            .hardwareProfile().vmSize())));
 
       return builder.build();
    }
@@ -158,12 +154,8 @@ public class VirtualMachineToNodeMetadata implements 
Function<VirtualMachine, No
       return privateIpAddresses;
    }
 
-   private NetworkInterfaceCard getNetworkInterfaceCard(IdReference 
networkInterfaceCardIdReference) {
-      
Iterables.get(Splitter.on("/").split(networkInterfaceCardIdReference.id()), 2);
-      String resourceGroup = 
Iterables.get(Splitter.on("/").split(networkInterfaceCardIdReference.id()), 4);
-      String nicName = 
Iterables.getLast(Splitter.on("/").split(networkInterfaceCardIdReference.id()));
-      return api.getNetworkInterfaceCardApi(resourceGroup).get(nicName);
-
+   private NetworkInterfaceCard getNetworkInterfaceCard(IdReference nic) {
+      return 
api.getNetworkInterfaceCardApi(nic.resourceGroup()).get(nic.name());
    }
 
    private Iterable<String> getPublicIpAddresses(List<IdReference> 
idReferences) {
@@ -172,11 +164,11 @@ public class VirtualMachineToNodeMetadata implements 
Function<VirtualMachine, No
          NetworkInterfaceCard networkInterfaceCard = 
getNetworkInterfaceCard(networkInterfaceCardIdReference);
          if (networkInterfaceCard != null && networkInterfaceCard.properties() 
!= null
                && networkInterfaceCard.properties().ipConfigurations() != 
null) {
-            String resourceGroup = 
Iterables.get(Splitter.on("/").split(networkInterfaceCardIdReference.id()), 4);
+            String resourceGroup = 
networkInterfaceCardIdReference.resourceGroup();
             for (IpConfiguration ipConfiguration : 
networkInterfaceCard.properties().ipConfigurations()) {
                if (ipConfiguration.properties().publicIPAddress() != null) {
-                  String publicIpId = 
ipConfiguration.properties().publicIPAddress().id();
-                  PublicIPAddress publicIp = 
api.getPublicIPAddressApi(resourceGroup).get(Iterables.getLast(Splitter.on("/").split(publicIpId)));
+                  IdReference publicIpId = 
ipConfiguration.properties().publicIPAddress();
+                  PublicIPAddress publicIp = 
api.getPublicIPAddressApi(resourceGroup).get(publicIpId.name());
                   if (publicIp != null && publicIp.properties().ipAddress() != 
null) {
                      publicIpAddresses.add(publicIp.properties().ipAddress());
                   }
@@ -191,8 +183,7 @@ public class VirtualMachineToNodeMetadata implements 
Function<VirtualMachine, No
       return find(locations.get(), idEquals(nullToEmpty(locationName)), null);
    }
 
-   protected Optional<? extends Image> findImage(final StorageProfile 
storageProfile, String locatioName,
-         String azureGroup) {
+   protected Optional<? extends Image> findImage(final StorageProfile 
storageProfile, String locatioName) {
       if (storageProfile.imageReference() != null) {
          // FIXME check this condition
          String imageId = storageProfile.imageReference().customImageId() != 
null ?
@@ -205,11 +196,12 @@ public class VirtualMachineToNodeMetadata implements 
Function<VirtualMachine, No
       }
    }
 
-   protected Hardware getHardware(final String vmSize) {
-      return Iterables.find(hardwares.get().values(), new 
Predicate<Hardware>() {
+   protected Hardware getHardware(final LocationAndName hardwareId) {
+      final String slashEncoded = hardwareId.slashEncode();
+      return find(hardwares.get().values(), new Predicate<Hardware>() {
          @Override
          public boolean apply(Hardware input) {
-            return input.getId().equals(vmSize);
+            return input.getId().equals(slashEncoded);
          }
       });
    }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/83c0a3c7/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToStatus.java
----------------------------------------------------------------------
diff --git 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToStatus.java
 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToStatus.java
index c0e333e..9669f50 100644
--- 
a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToStatus.java
+++ 
b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/functions/VirtualMachineToStatus.java
@@ -17,13 +17,13 @@
 package org.jclouds.azurecompute.arm.compute.functions;
 
 import static com.google.common.collect.Iterables.transform;
+import static 
org.jclouds.azurecompute.arm.domain.IdReference.extractResourceGroup;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
 import org.jclouds.azurecompute.arm.AzureComputeApi;
 import 
org.jclouds.azurecompute.arm.compute.functions.VirtualMachineToStatus.StatusAndBackendStatus;
-import org.jclouds.azurecompute.arm.domain.ResourceGroup;
 import org.jclouds.azurecompute.arm.domain.Status;
 import org.jclouds.azurecompute.arm.domain.VirtualMachine;
 import org.jclouds.azurecompute.arm.domain.VirtualMachineInstance;
@@ -36,7 +36,6 @@ import com.google.auto.value.AutoValue;
 import com.google.common.base.Function;
 import com.google.common.base.Functions;
 import com.google.common.base.Joiner;
-import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableMap;
 
 @Singleton
@@ -80,17 +79,15 @@ public class VirtualMachineToStatus implements 
Function<VirtualMachine, StatusAn
          NodeMetadata.Status.UNRECOGNIZED);
 
    private final AzureComputeApi api;
-   private final LoadingCache<String, ResourceGroup> resourceGroupMap;
 
    @Inject
-   VirtualMachineToStatus(AzureComputeApi api, LoadingCache<String, 
ResourceGroup> resourceGroupMap) {
+   VirtualMachineToStatus(AzureComputeApi api) {
       this.api = api;
-      this.resourceGroupMap = resourceGroupMap;
    }
 
    @Override
    public StatusAndBackendStatus apply(VirtualMachine virtualMachine) {
-      ResourceGroup resourceGroup = 
resourceGroupMap.getUnchecked(virtualMachine.location());
+      String resourceGroup = extractResourceGroup(virtualMachine.id());
       ProvisioningState provisioningState = 
virtualMachine.properties().provisioningState();
 
       NodeMetadata.Status status = 
PROVISIONINGSTATE_TO_NODESTATUS.apply(provisioningState);
@@ -99,7 +96,7 @@ public class VirtualMachineToStatus implements 
Function<VirtualMachine, StatusAn
       if (ProvisioningState.SUCCEEDED.equals(provisioningState)) {
          // If the provisioning succeeded, we need to query the *real* status 
of
          // the VM
-         VirtualMachineInstance instanceDetails = 
api.getVirtualMachineApi(resourceGroup.name()).getInstanceDetails(
+         VirtualMachineInstance instanceDetails = 
api.getVirtualMachineApi(resourceGroup).getInstanceDetails(
                virtualMachine.name());
          if (instanceDetails != null && instanceDetails.powerState() != null) {
             status = 
POWERSTATE_TO_NODESTATUS.apply(instanceDetails.powerState());

Reply via email to