You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by an...@apache.org on 2015/02/27 12:31:19 UTC

[4/4] jclouds-labs git commit: AzureCompute: initial work to support ComputeServiceAdapter

AzureCompute: initial work to support ComputeServiceAdapter

improve DeploymentToNodeMetadata
enhance Deployment value object
add support for get/set NetworkConfiguration
added AzureComputeTemplateOptions to manage networkConfigurations
add support for create/checkAvailable/list storageAccounts
add support for inboundPorts
add more RoleSize
add support for SecurityGroupExtension by using NetworkSecurityGroup
fix destroy node
fix LocationToLocation
fix OsImageToImage


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

Branch: refs/heads/master
Commit: e97ddaeeb59acc516e3d4d8398cf2da5e5492487
Parents: c78a66c
Author: Andrea Turli <an...@gmail.com>
Authored: Tue Dec 16 09:57:04 2014 +0100
Committer: andreaturli <an...@gmail.com>
Committed: Fri Feb 27 12:30:50 2015 +0100

----------------------------------------------------------------------
 azurecompute/README.md                          |   2 +-
 azurecompute/pom.xml                            |  12 +
 .../jclouds/azurecompute/AzureComputeApi.java   |  36 ++
 .../AzureComputeProviderMetadata.java           |  38 +-
 .../AzureManagementApiMetadata.java             |  14 +-
 .../binders/DeploymentParamsToXML.java          |  27 +-
 .../binders/NetworkConfigurationToXML.java      |  62 +++
 .../binders/NetworkSecurityGroupToXML.java      |  45 ++
 .../jclouds/azurecompute/binders/RoleToXML.java |  74 +++
 .../jclouds/azurecompute/binders/RuleToXML.java |  46 ++
 .../binders/StorageServiceParamsToXML.java      |  49 ++
 .../compute/AzureComputeServiceAdapter.java     | 262 +++++++++--
 .../AzureComputeServiceContextModule.java       | 121 ++++-
 .../AzureComputeSecurityGroupExtension.java     | 449 +++++++++++++++++++
 .../functions/DeploymentToNodeMetadata.java     |  96 +++-
 .../compute/functions/LocationToLocation.java   |  56 +++
 .../compute/functions/OSImageToImage.java       |  23 +-
 .../compute/functions/RoleSizeToHardware.java   |  36 +-
 .../functions/internal/OperatingSystems.java    |  83 ++++
 ...ServiceAndVirtualNetworkThenCreateNodes.java | 270 +++++++++++
 ...eNodeCredentialsButOverrideFromTemplate.java |  57 +++
 .../config/AzureComputeHttpApiModule.java       |  24 +
 .../config/AzureComputeParserModule.java        |  27 ++
 .../config/AzureComputeProperties.java          |  21 +-
 .../domain/NetworkConfiguration.java            |  22 +-
 .../org/jclouds/azurecompute/domain/Role.java   |   9 +-
 .../azurecompute/features/DeploymentApi.java    |   1 +
 .../features/NetworkSecurityGroupApi.java       | 167 +++++++
 .../features/StorageAccountApi.java             |  97 ++++
 .../azurecompute/features/SubscriptionApi.java  |  53 +++
 .../features/VirtualMachineApi.java             |  40 +-
 .../features/VirtualNetworkApi.java             |  76 ++++
 .../options/AzureComputeTemplateOptions.java    | 324 +++++++++++++
 .../util/NetworkSecurityGroups.java             |  73 +++
 .../xml/ConfigurationSetHandler.java            |   8 +-
 .../xml/NetworkConfigurationHandler.java        |  44 +-
 .../ResourceExtensionParameterValueHandler.java |  60 +++
 .../xml/ResourceExtensionReferenceHandler.java  | 100 +++++
 .../jclouds/azurecompute/xml/RoleHandler.java   |  19 +-
 .../azurecompute/xml/StorageServiceHandler.java |   2 +-
 .../jclouds/azurecompute/xml/SubnetHandler.java |   3 +
 .../xml/VirtualNetworkConfigurationHandler.java |  73 +++
 .../xml/VirtualNetworkSiteHandler.java          |   5 +
 .../AzureComputeServiceAdapterLiveTest.java     | 130 ++++++
 .../AzureComputeServiceContextLiveTest.java     |  81 ++++
 ...reComputeSecurityGroupExtensionLiveTest.java |  45 ++
 .../features/CloudServiceApiLiveTest.java       |  26 +-
 .../features/CloudServiceApiMockTest.java       |  55 +--
 .../features/DeploymentApiLiveTest.java         | 132 ++++++
 .../features/DeploymentApiMockTest.java         |  69 +--
 .../azurecompute/features/DiskApiLiveTest.java  |   2 +
 .../azurecompute/features/DiskApiMockTest.java  |  36 +-
 .../features/LocationApiMockTest.java           |  17 +-
 .../features/OSImageApiMockTest.java            |  41 +-
 .../features/OperationApiMockTest.java          |  18 +-
 .../features/SubscriptionApiLiveTest.java       |  42 ++
 .../features/VirtualMachineApiLiveTest.java     | 161 +++++++
 .../features/VirtualMachineApiMockTest.java     |  26 +-
 .../features/VirtualNetworkApiLiveTest.java     | 112 +++++
 .../features/VirtualNetworkApiMockTest.java     |  95 ++++
 .../internal/BaseAzureComputeApiLiveTest.java   | 163 ++++++-
 .../internal/BaseAzureComputeApiMockTest.java   |   7 +-
 .../azurecompute/xml/DeploymentHandlerTest.java |   6 +-
 .../xml/NetworkConfigurationHandlerTest.java    |  50 +++
 .../azurecompute/xml/RoleHandlerTest.java       |  80 ++++
 .../test/resources/deploymentparams-windows.xml |   2 +-
 .../src/test/resources/deploymentparams.xml     |   2 +-
 .../src/test/resources/networkconfiguration.xml |  18 +
 azurecompute/src/test/resources/role.xml        |  56 +++
 docker/src/test/resources/logback.xml           |   2 +-
 70 files changed, 4211 insertions(+), 369 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e97ddaee/azurecompute/README.md
----------------------------------------------------------------------
diff --git a/azurecompute/README.md b/azurecompute/README.md
index b38c672..9e44b67 100644
--- a/azurecompute/README.md
+++ b/azurecompute/README.md
@@ -20,7 +20,7 @@ openssl x509 -inform pem -in $HOME/.jclouds/azure.pem -outform der -out $HOME/.j
 
 Once you do this, you will set the following to run the live tests.
 ```bash
-mvn -Plive -Dtest.jclouds.azurecompute.subscription-id=12345678-abcd-dcba-abdc-ba0987654321
+mvn -Plive -Dtest.azurecompute.endpoint=https://management.core.windows.net/12345678-abcd-dcba-abdc-ba0987654321
 -Dtest.azurecompute.credential=P12_EXPORT_PASSWORD
 -Dtest.azurecompute.identity=$HOME/.jclouds/azure.p12
 ```

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e97ddaee/azurecompute/pom.xml
----------------------------------------------------------------------
diff --git a/azurecompute/pom.xml b/azurecompute/pom.xml
index 48f2a54..4231412 100644
--- a/azurecompute/pom.xml
+++ b/azurecompute/pom.xml
@@ -86,12 +86,24 @@
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>org.apache.jclouds</groupId>
+      <artifactId>jclouds-compute</artifactId>
+      <version>${project.parent.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>org.apache.jclouds.driver</groupId>
       <artifactId>jclouds-slf4j</artifactId>
       <version>${project.parent.version}</version>
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>org.apache.jclouds.driver</groupId>
       <artifactId>jclouds-sshj</artifactId>
       <version>${project.parent.version}</version>

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e97ddaee/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeApi.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeApi.java b/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeApi.java
index 5946838..5c35a85 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeApi.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeApi.java
@@ -24,9 +24,13 @@ import org.jclouds.azurecompute.features.CloudServiceApi;
 import org.jclouds.azurecompute.features.DeploymentApi;
 import org.jclouds.azurecompute.features.DiskApi;
 import org.jclouds.azurecompute.features.LocationApi;
+import org.jclouds.azurecompute.features.NetworkSecurityGroupApi;
 import org.jclouds.azurecompute.features.OSImageApi;
 import org.jclouds.azurecompute.features.OperationApi;
+import org.jclouds.azurecompute.features.StorageAccountApi;
+import org.jclouds.azurecompute.features.SubscriptionApi;
 import org.jclouds.azurecompute.features.VirtualMachineApi;
+import org.jclouds.azurecompute.features.VirtualNetworkApi;
 import org.jclouds.rest.annotations.Delegate;
 
 /**
@@ -100,4 +104,36 @@ public interface AzureComputeApi extends Closeable {
     */
    @Delegate
    DiskApi getDiskApi();
+
+   /**
+    * The Service Management API includes operations for retrieving information about a subscription.
+    *
+    * @see <a href="http://msdn.microsoft.com/en-us/library/azure/gg715315.aspx">docs</a>
+    */
+   @Delegate
+   SubscriptionApi getSubscriptionApi();
+
+   /**
+    * The Service Management API includes operations for managing the virtual networks in your subscription.
+    *
+    * @see <a href="http://msdn.microsoft.com/en-us/library/jj157182.aspx">docs</a>
+    */
+   @Delegate
+   VirtualNetworkApi getVirtualNetworkApi();
+
+   /**
+    * The Service Management API includes operations for managing the storage accounts in your subscription.
+    *
+    * @see <a href="http://msdn.microsoft.com/en-us/library/ee460790.aspx">docs</a>
+    */
+   @Delegate
+   StorageAccountApi getStorageAccountApi();
+
+   /**
+    * The Service Management API includes operations for managing the Network Security Groups in your
+    * subscription.
+    *
+    */
+   @Delegate
+   NetworkSecurityGroupApi getNetworkSecurityGroupApi();
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e97ddaee/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeProviderMetadata.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeProviderMetadata.java b/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeProviderMetadata.java
index e9bb875..60e00d1 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeProviderMetadata.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/AzureComputeProviderMetadata.java
@@ -16,11 +16,14 @@
  */
 package org.jclouds.azurecompute;
 
-import static org.jclouds.azurecompute.config.AzureComputeProperties.SUBSCRIPTION_ID;
-
+import static org.jclouds.azurecompute.config.AzureComputeProperties.OPERATION_POLL_INITIAL_PERIOD;
+import static org.jclouds.azurecompute.config.AzureComputeProperties.OPERATION_POLL_MAX_PERIOD;
+import static org.jclouds.azurecompute.config.AzureComputeProperties.OPERATION_TIMEOUT;
+import static org.jclouds.compute.config.ComputeServiceProperties.TEMPLATE;
 import java.net.URI;
 import java.util.Properties;
 
+import org.jclouds.azurecompute.config.AzureComputeProperties;
 import org.jclouds.providers.ProviderMetadata;
 import org.jclouds.providers.internal.BaseProviderMetadata;
 
@@ -42,27 +45,32 @@ public class AzureComputeProviderMetadata extends BaseProviderMetadata {
       super(builder());
    }
 
-   public AzureComputeProviderMetadata(Builder builder) {
-      super(builder);
-   }
-
    public static Properties defaultProperties() {
-      Properties properties = new Properties();
+      Properties properties = AzureManagementApiMetadata.defaultProperties();
+      properties.setProperty(TEMPLATE, "osFamily=UBUNTU,loginUser=jclouds");
+      properties.setProperty(OPERATION_TIMEOUT, "" + 60 * 1000);
+      properties.setProperty(OPERATION_POLL_INITIAL_PERIOD, "" + 5);
+      properties.setProperty(OPERATION_POLL_MAX_PERIOD, "" + 15);
+      properties.setProperty(AzureComputeProperties.TCP_RULE_FORMAT, "tcp_%s-%s");
       return properties;
    }
 
+   public AzureComputeProviderMetadata(Builder builder) {
+      super(builder);
+   }
+
    public static class Builder extends BaseProviderMetadata.Builder {
 
       protected Builder() {
          id("azurecompute")
-         .name("Microsoft Azure Service Management Service")
-         .apiMetadata(new AzureManagementApiMetadata())
-         .endpoint("https://management.core.windows.net/${" + SUBSCRIPTION_ID + "}")
-         .homepage(URI.create("https://www.windowsazure.com/"))
-         .console(URI.create("https://windows.azure.com/default.aspx"))
-         .linkedServices("azureblob", "azurequeue", "azuretable")
-         .iso3166Codes("US-TX", "US-IL", "IE-D", "SG", "NL-NH", "HK")
-         .defaultProperties(AzureComputeProviderMetadata.defaultProperties());
+                 .name("Microsoft Azure Service Management Service")
+                 .apiMetadata(new AzureManagementApiMetadata())
+                 .endpoint("https://management.core.windows.net/SUBSCRIPTION_ID")
+                 .homepage(URI.create("https://www.windowsazure.com/"))
+                 .console(URI.create("https://windows.azure.com/default.aspx"))
+                 .linkedServices("azureblob", "azurequeue", "azuretable")
+                 .iso3166Codes("US-TX", "US-IL", "IE-D", "SG", "NL-NH", "HK")
+                 .defaultProperties(AzureComputeProviderMetadata.defaultProperties());
       }
 
       @Override

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e97ddaee/azurecompute/src/main/java/org/jclouds/azurecompute/AzureManagementApiMetadata.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/AzureManagementApiMetadata.java b/azurecompute/src/main/java/org/jclouds/azurecompute/AzureManagementApiMetadata.java
index 0caad94..8e5fa86 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/AzureManagementApiMetadata.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/AzureManagementApiMetadata.java
@@ -16,14 +16,14 @@
  */
 package org.jclouds.azurecompute;
 
-import static org.jclouds.azurecompute.config.AzureComputeProperties.SUBSCRIPTION_ID;
-
+import static org.jclouds.reflect.Reflection2.typeToken;
 import java.net.URI;
 import java.util.Properties;
 
 import org.jclouds.apis.ApiMetadata;
 import org.jclouds.azurecompute.compute.config.AzureComputeServiceContextModule;
 import org.jclouds.azurecompute.config.AzureComputeHttpApiModule;
+import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.rest.internal.BaseHttpApiMetadata;
 
 import com.google.common.collect.ImmutableSet;
@@ -56,17 +56,17 @@ public class AzureManagementApiMetadata extends BaseHttpApiMetadata<AzureCompute
 
       protected Builder() {
          id("azurecompute")
-         .name("Microsoft Azure Service Management Service API")
-         .version("2014-06-01")
+                 .name("Microsoft Azure Service Management Service API")
+                 .version("2014-10-01")
          .identityName("Path to Management Certificate .p12 file, or PEM string")
          .credentialName("Password to Management Certificate")
-         .defaultEndpoint("https://management.core.windows.net/${" + SUBSCRIPTION_ID + "}")
          .endpointName("Service Management Endpoint ending in your Subscription Id")
          .documentation(URI.create("http://msdn.microsoft.com/en-us/library/ee460799"))
          .defaultProperties(AzureManagementApiMetadata.defaultProperties())
+         .view(typeToken(ComputeServiceContext.class))
          .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
-               .add(AzureComputeServiceContextModule.class)
-               .add(AzureComputeHttpApiModule.class).build());
+                 .add(AzureComputeServiceContextModule.class)
+                 .add(AzureComputeHttpApiModule.class).build());
       }
 
       @Override

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e97ddaee/azurecompute/src/main/java/org/jclouds/azurecompute/binders/DeploymentParamsToXML.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/binders/DeploymentParamsToXML.java b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/DeploymentParamsToXML.java
index 26301d6..bc9f657 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/binders/DeploymentParamsToXML.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/DeploymentParamsToXML.java
@@ -16,8 +16,6 @@
  */
 package org.jclouds.azurecompute.binders;
 
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
 import static com.google.common.base.Throwables.propagate;
 import static org.jclouds.azurecompute.domain.OSImage.Type.LINUX;
 
@@ -68,7 +66,9 @@ public final class DeploymentParamsToXML implements Binder {
                .e("UserName").t(params.username()).up()
                .e("UserPassword").t(params.password()).up()
                .e("DisableSshPasswordAuthentication").t("false").up()
-               .e("SSH").up()
+               .e("SSH")
+                    .e("PublicKeys").up()
+                    .e("KeyPairs").up()
                .up(); // Linux ConfigurationSet
          } else {
             throw new IllegalArgumentException("Unrecognized os type " + params);
@@ -88,25 +88,34 @@ public final class DeploymentParamsToXML implements Binder {
          }
 
          inputEndpoints.up();
-         configBuilder.e("SubnetNames").up()
-            .up();
+         //configBuilder.e("SubnetNames").up().up();
+
+         XMLBuilder subnetNames = configBuilder.e("SubnetNames");
+         for (String subnetName : params.subnetNames()) {
+            subnetNames.e("SubnetName").t(subnetName).up()
+                    .up(); //subnetName
+         }
 
          builder.up() //ConfigurationSets
+            // TODO No Disk should be specified for a Role if using a VMImage
             .e("DataVirtualHardDisks").up()
             .e("OSVirtualHardDisk")
             .e("HostCaching").t("ReadWrite").up()
             .e("MediaLink").t(params.mediaLink().toASCIIString()).up()
+            // TODO
+            /// If you are using a VM image, it must be specified as VMImageName for the role, not as SourceImageNamefor OSVirtualHardDisk.</Message></Error>]
             .e("SourceImageName").t(params.sourceImageName()).up()
             .e("OS").t(params.os() == LINUX ? "Linux" : "Windows").up()
-            .up() //OSVirtualHardDisk
-            .e("RoleSize").t(UPPER_UNDERSCORE.to(UPPER_CAMEL, params.size().name())).up()
+             .up() //OSVirtualHardDisk
+            .e("RoleSize").t(params.size().getText()).up()
             .up() //Role
-            .up(); //RoleList
-
+            .up() //RoleList
+                 .e("VirtualNetworkName").t(params.virtualNetworkName()).up();
          // TODO: Undeprecate this method as forcing users to wrap a String in guava's ByteSource is not great.
          return (R) request.toBuilder().payload(builder.asString()).build();
       } catch (Exception e) {
          throw propagate(e);
       }
    }
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e97ddaee/azurecompute/src/main/java/org/jclouds/azurecompute/binders/NetworkConfigurationToXML.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/binders/NetworkConfigurationToXML.java b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/NetworkConfigurationToXML.java
new file mode 100644
index 0000000..87ffb5e
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/NetworkConfigurationToXML.java
@@ -0,0 +1,62 @@
+/*
+ * 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.binders;
+
+import static com.google.common.base.Throwables.propagate;
+
+import org.jclouds.azurecompute.domain.NetworkConfiguration;
+import org.jclouds.azurecompute.domain.NetworkConfiguration.Subnet;
+import org.jclouds.azurecompute.domain.NetworkConfiguration.VirtualNetworkSite;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.Binder;
+
+import com.jamesmurty.utils.XMLBuilder;
+
+public class NetworkConfigurationToXML implements Binder {
+
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Object input) {
+      NetworkConfiguration networkConfiguration = NetworkConfiguration.class.cast(input);
+
+      try {
+         XMLBuilder builder = XMLBuilder.create("NetworkConfiguration", "http://schemas.microsoft.com/ServiceHosting/2011/07/NetworkConfiguration")
+                 .e("VirtualNetworkConfiguration");
+         if (networkConfiguration.virtualNetworkConfiguration().dns() == null) {
+            builder.e("Dns");
+         } else {
+            builder.e("Dns").t(networkConfiguration.virtualNetworkConfiguration().dns());
+         }
+         if (!networkConfiguration.virtualNetworkConfiguration().virtualNetworkSites().isEmpty()) {
+            XMLBuilder virtualNetworkSitesBuilder = builder.e("VirtualNetworkSites");
+            for (VirtualNetworkSite virtualNetworkSite : networkConfiguration.virtualNetworkConfiguration().virtualNetworkSites()) {
+               XMLBuilder virtualNetworkSiteBuilder = virtualNetworkSitesBuilder.e("VirtualNetworkSite").a("name",
+                       virtualNetworkSite.name()).a("Location", virtualNetworkSite.location());
+               virtualNetworkSiteBuilder.e("AddressSpace")
+                                        .e("AddressPrefix").t(virtualNetworkSite.addressSpace().addressPrefix()).up();
+               XMLBuilder subnetBuilder = virtualNetworkSiteBuilder.e("Subnets");
+                  for (Subnet subnet : virtualNetworkSite.subnets()) {
+                     subnetBuilder.e("Subnet").a("name", subnet.name()).e("AddressPrefix").t(subnet.addressPrefix());
+               }
+            }
+         }
+         return (R) request.toBuilder().payload(builder.asString()).build();
+      } catch (Exception e) {
+         throw propagate(e);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e97ddaee/azurecompute/src/main/java/org/jclouds/azurecompute/binders/NetworkSecurityGroupToXML.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/binders/NetworkSecurityGroupToXML.java b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/NetworkSecurityGroupToXML.java
new file mode 100644
index 0000000..6290df6
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/NetworkSecurityGroupToXML.java
@@ -0,0 +1,45 @@
+/*
+ * 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.binders;
+
+import static com.google.common.base.Throwables.propagate;
+
+import org.jclouds.azurecompute.domain.NetworkSecurityGroup;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.Binder;
+
+import com.jamesmurty.utils.XMLBuilder;
+
+public class NetworkSecurityGroupToXML implements Binder {
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Object input) {
+      NetworkSecurityGroup networkSecurityGroup = NetworkSecurityGroup.class.cast(input);
+      try {
+         XMLBuilder builder = XMLBuilder.create("NetworkSecurityGroup", "http://schemas.microsoft.com/windowsazure")
+                 .e("Name").t(networkSecurityGroup.name()).up();
+         if (networkSecurityGroup.label() != null) {
+            builder.e("Label").t(networkSecurityGroup.label()).up();
+         }
+         if (networkSecurityGroup.location() != null) {
+            builder.e("Location").t(networkSecurityGroup.location()).up();
+         }
+         return (R) request.toBuilder().payload(builder.asString()).build();
+      } catch (Exception e) {
+         throw propagate(e);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e97ddaee/azurecompute/src/main/java/org/jclouds/azurecompute/binders/RoleToXML.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/binders/RoleToXML.java b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/RoleToXML.java
new file mode 100644
index 0000000..41c3a83
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/RoleToXML.java
@@ -0,0 +1,74 @@
+/*
+ * 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.binders;
+
+import static com.google.common.base.Throwables.propagate;
+
+import org.jclouds.azurecompute.domain.Role;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.Binder;
+
+import com.jamesmurty.utils.XMLBuilder;
+
+public class RoleToXML implements Binder {
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Object input) {
+      Role role = Role.class.cast(input);
+
+      try {
+         XMLBuilder builder = XMLBuilder.create("PersistentVMRole", "http://schemas.microsoft.com/windowsazure")
+                 .e("RoleName").t(role.roleName()).up()
+                 .e("RoleType").t(role.roleType()).up()
+                 .e("ConfigurationSets");
+
+         if (!role.configurationSets().isEmpty()) {
+            for (Role.ConfigurationSet configurationSet : role.configurationSets()) {
+               XMLBuilder configBuilder = builder.e("ConfigurationSet"); // Network
+               configBuilder.e("ConfigurationSetType").t(configurationSet.configurationSetType()).up();
+
+               XMLBuilder inputEndpoints = configBuilder.e("InputEndpoints");
+               for (Role.ConfigurationSet.InputEndpoint endpoint : configurationSet.inputEndpoints()) {
+                  XMLBuilder inputBuilder = inputEndpoints.e("InputEndpoint");
+                  inputBuilder.e("LocalPort").t(Integer.toString(endpoint.localPort())).up()
+                          .e("Name").t(endpoint.name()).up()
+                          .e("Port").t(Integer.toString(endpoint.port())).up()
+                          .e("Protocol").t(endpoint.protocol().toLowerCase()).up()
+                          .up(); //InputEndpoint
+               }
+               XMLBuilder subnetNames = configBuilder.e("SubnetNames");
+               for (Role.ConfigurationSet.SubnetName subnetName : configurationSet.subnetNames()) {
+                  subnetNames.e("SubnetName").t(subnetName.name()).up();
+               }
+               configBuilder.e("NetworkSecurityGroup").t(configurationSet.networkSecurityGroup()).up();
+            }
+         }
+         builder.e("DataVirtualHardDisks").up()
+                .e("OSVirtualHardDisk")
+                .e("HostCaching").t(role.osVirtualHardDisk().hostCaching()).up()
+                .e("DiskName").t(role.osVirtualHardDisk().diskName()).up()
+                .e("MediaLink").t(role.osVirtualHardDisk().mediaLink().toString()).up()
+                .e("SourceImageName").t(role.osVirtualHardDisk().sourceImageName()).up()
+                .e("OS").t(role.osVirtualHardDisk().os().toString()).up()
+                .up() // DataVirtualHardDisks
+                .e("RoleSize").t(role.roleSize().getText());
+         return (R) request.toBuilder().payload(builder.asString()).build();
+      } catch (Exception e) {
+         throw propagate(e);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e97ddaee/azurecompute/src/main/java/org/jclouds/azurecompute/binders/RuleToXML.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/binders/RuleToXML.java b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/RuleToXML.java
new file mode 100644
index 0000000..ec4a1f6
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/RuleToXML.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.binders;
+
+import static com.google.common.base.Throwables.propagate;
+
+import org.jclouds.azurecompute.domain.Rule;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.Binder;
+
+import com.jamesmurty.utils.XMLBuilder;
+
+public final class RuleToXML implements Binder {
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Object input) {
+      Rule rule = Rule.class.cast(input);
+      try {
+         String xml = XMLBuilder.create("Rule", "http://schemas.microsoft.com/windowsazure")
+                         .e("Type").t(rule.type()).up()
+                         .e("Priority").t(rule.priority()).up()
+                         .e("Action").t(rule.action()).up()
+                         .e("SourceAddressPrefix").t(rule.sourceAddressPrefix()).up()
+                         .e("SourcePortRange").t(rule.sourcePortRange()).up()
+                         .e("DestinationAddressPrefix").t(rule.destinationAddressPrefix()).up()
+                         .e("DestinationPortRange").t(rule.destinationPortRange()).up()
+                         .e("Protocol").t(rule.protocol()).up().asString();
+         return (R) request.toBuilder().payload(xml).build();
+      } catch (Exception e) {
+         throw propagate(e);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e97ddaee/azurecompute/src/main/java/org/jclouds/azurecompute/binders/StorageServiceParamsToXML.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/binders/StorageServiceParamsToXML.java b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/StorageServiceParamsToXML.java
new file mode 100644
index 0000000..f6a934e
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/binders/StorageServiceParamsToXML.java
@@ -0,0 +1,49 @@
+/*
+ * 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.binders;
+
+import static com.google.common.base.Throwables.propagate;
+import org.jclouds.azurecompute.domain.StorageServiceParams;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.Binder;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.BaseEncoding;
+import com.jamesmurty.utils.XMLBuilder;
+
+public final class StorageServiceParamsToXML implements Binder {
+
+   @Override public <R extends HttpRequest> R bindToRequest(R request, Object input) {
+      StorageServiceParams params = StorageServiceParams.class.cast(input);
+
+      try {
+         XMLBuilder builder = XMLBuilder.create("CreateStorageServiceInput", "http://schemas.microsoft.com/windowsazure")
+            .e("ServiceName").t(params.name()).up()
+            //.e("Description").up()
+            .e("Label").t(BaseEncoding.base64().encode(params.label().getBytes(Charsets.UTF_16))).up()
+            .e("Location").t(params.location()).up()
+            //.e("GeoReplicationEnabled").up()
+            //.e("ExtendedProperties").up()
+            //.e("SecondaryReadEnabled").up()
+            .e("AccountType").t(params.accountType().name()).up();
+         return (R) request.toBuilder().payload(builder.asString()).build();
+      } catch (Exception e) {
+         throw propagate(e);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e97ddaee/azurecompute/src/main/java/org/jclouds/azurecompute/compute/AzureComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/compute/AzureComputeServiceAdapter.java b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/AzureComputeServiceAdapter.java
index 9663a75..aed8570 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/compute/AzureComputeServiceAdapter.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/AzureComputeServiceAdapter.java
@@ -16,89 +16,293 @@
  */
 package org.jclouds.azurecompute.compute;
 
+import static com.google.common.base.Predicates.notNull;
+import static java.lang.String.format;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.jclouds.azurecompute.domain.Deployment.InstanceStatus.READY_ROLE;
+import static org.jclouds.util.Predicates2.retry;
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
 import javax.inject.Singleton;
 
 import org.jclouds.azurecompute.AzureComputeApi;
+import org.jclouds.azurecompute.compute.config.AzureComputeServiceContextModule.AzureComputeConstants;
+import org.jclouds.azurecompute.config.AzureComputeProperties;
+import org.jclouds.azurecompute.domain.CloudService;
 import org.jclouds.azurecompute.domain.Deployment;
+import org.jclouds.azurecompute.domain.Deployment.RoleInstance;
+import org.jclouds.azurecompute.domain.DeploymentParams;
+import org.jclouds.azurecompute.domain.DeploymentParams.ExternalEndpoint;
+import org.jclouds.azurecompute.domain.Location;
 import org.jclouds.azurecompute.domain.OSImage;
 import org.jclouds.azurecompute.domain.RoleSize;
+import org.jclouds.azurecompute.options.AzureComputeTemplateOptions;
 import org.jclouds.compute.ComputeServiceAdapter;
+import org.jclouds.compute.domain.OsFamily;
 import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.logging.Logger;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Splitter;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 
 /**
  * defines the connection between the {@link AzureComputeApi} implementation and the
  * jclouds {@link org.jclouds.compute.ComputeService}
  */
 @Singleton
-public class AzureComputeServiceAdapter implements ComputeServiceAdapter<Deployment, RoleSize, OSImage, String> {
+public class AzureComputeServiceAdapter implements ComputeServiceAdapter<Deployment, RoleSize, OSImage, Location> {
+
+   private static final String DEFAULT_LOGIN_USER = "jclouds";
+   private static final String DEFAULT_LOGIN_PASSWORD = "Azur3Compute!";
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   private final AzureComputeApi api;
+   private final Predicate<String> operationSucceededPredicate;
+   private final AzureComputeConstants azureComputeConstants;
+
+   @Inject
+   AzureComputeServiceAdapter(final AzureComputeApi api, Predicate<String> operationSucceededPredicate, AzureComputeConstants azureComputeConstants) {
+      this.api = api;
+      this.operationSucceededPredicate = operationSucceededPredicate;
+      this.azureComputeConstants = azureComputeConstants;
+   }
 
    @Override
    public NodeAndInitialCredentials<Deployment> createNodeWithGroupEncodedIntoName(
-         String group, String name, Template template) {
-      // TODO Auto-generated method stub
-      return null;
+           final String group, final String name, Template template) {
+
+      // azure-specific options
+      AzureComputeTemplateOptions templateOptions = template.getOptions().as(AzureComputeTemplateOptions.class);
+
+      final String loginUser = templateOptions.getLoginUser() != null ? templateOptions.getLoginUser() : DEFAULT_LOGIN_USER;
+      final String loginPassword = templateOptions.getLoginPassword() != null ? templateOptions.getLoginPassword() : DEFAULT_LOGIN_PASSWORD;
+      final String location = template.getLocation().getId();
+      final int[] inboundPorts = template.getOptions().getInboundPorts();
+
+      final String storageAccountName = templateOptions.getStorageAccountName().get();
+      final String virtualNetworkName = templateOptions.getVirtualNetworkName().get();
+      final String subnetName = templateOptions.getSubnetName().get();
+
+      logger.debug("Creating a cloud service with name '%s', label '%s' in location '%s'", name, name, location);
+      String createCloudServiceRequestId = api.getCloudServiceApi().createWithLabelInLocation(name, name, location);
+      if (!operationSucceededPredicate.apply(createCloudServiceRequestId)) {
+         final String message = generateIllegalStateExceptionMessage(createCloudServiceRequestId, azureComputeConstants.operationTimeout());
+         logger.warn(message);
+         throw new IllegalStateException(message);
+      }
+      logger.info("Cloud Service (%s) created with operation id: %s", name, createCloudServiceRequestId);
+
+      final OSImage.Type os = template.getImage().getOperatingSystem().getFamily().equals(OsFamily.WINDOWS) ? OSImage.Type.WINDOWS : OSImage.Type.LINUX;
+      Set<ExternalEndpoint> externalEndpoints = Sets.newHashSet();
+      for (int inboundPort : inboundPorts) {
+         externalEndpoints.add(ExternalEndpoint.inboundTcpToLocalPort(inboundPort, inboundPort));
+      }
+      final DeploymentParams params = DeploymentParams.builder()
+              .name(name)
+              .os(os)
+              .username(loginUser)
+              .password(loginPassword)
+              .sourceImageName(template.getImage().getId())
+              .mediaLink(createMediaLink(storageAccountName, name))
+              .size(RoleSize.Type.fromString(template.getHardware().getName()))
+              .externalEndpoints(externalEndpoints)
+              .subnetName(subnetName)
+              .virtualNetworkName(virtualNetworkName)
+              .build();
+
+      logger.debug("Creating a deployment with params '%s' ...", params);
+      String createDeploymentRequestId = api.getDeploymentApiForService(name).create(params);
+      if (!operationSucceededPredicate.apply(createDeploymentRequestId)) {
+         final String message = generateIllegalStateExceptionMessage(createCloudServiceRequestId, azureComputeConstants.operationTimeout());
+         logger.warn(message);
+         logger.debug("Deleting cloud service (%s) ...", name);
+         deleteCloudService(name);
+         logger.debug("Cloud service (%s) deleted.", name);
+      }
+      logger.info("Deployment created with operation id: %s", createDeploymentRequestId);
+
+      if (!retry(new Predicate<String>() {
+         public boolean apply(String name) {
+            return FluentIterable.from(api.getDeploymentApiForService(name).get(name).roleInstanceList())
+                    .allMatch(new Predicate<RoleInstance>() {
+                       @Override
+                       public boolean apply(RoleInstance input) {
+                          return input != null && input.instanceStatus() == READY_ROLE;
+                       }
+                    });
+         }
+      }, 30 * 60, 1, SECONDS).apply(name)) {
+         logger.warn("Instances %s of %s has not reached the status %s within %sms so it will be destroyed.",
+                 Iterables.toString(api.getDeploymentApiForService(name).get(name).roleInstanceList()), name,
+                 READY_ROLE, azureComputeConstants.operationTimeout());
+         api.getDeploymentApiForService(group).delete(name);
+         api.getCloudServiceApi().delete(name);
+         throw new IllegalStateException(format("Deployment %s is being destroyed as its instanceStatus didn't reach " +
+                 "status %s after %ss. Please, try by increasing `jclouds.azure.operation-timeout` and " +
+                 " try again", name, READY_ROLE, 30 * 60));
+      }
+
+      Deployment deployment = api.getDeploymentApiForService(name).get(name);
+
+      return new NodeAndInitialCredentials(deployment, deployment.name(),
+              LoginCredentials.builder().user(loginUser).password(loginPassword).build());
+   }
+
+   public static String generateIllegalStateExceptionMessage(String operationId, long timeout) {
+      final String warnMessage = format("%s has not been completed within %sms.", operationId, timeout);
+      return format("%s. Please, try by increasing `%s` and try again", warnMessage, AzureComputeProperties.OPERATION_TIMEOUT);
    }
 
    @Override
    public Iterable<RoleSize> listHardwareProfiles() {
-      // TODO Auto-generated method stub
-      return null;
+      return api.getSubscriptionApi().listRoleSizes();
    }
 
    @Override
    public Iterable<OSImage> listImages() {
-      // TODO Auto-generated method stub
-      return null;
+      List<OSImage> osImages = Lists.newArrayList();
+      for (OSImage osImage : api.getOSImageApi().list()) {
+         Iterable<String> locations = Splitter.on(";").split(osImage.location());
+         for (String location : locations) {
+            osImages.add(OSImage.create(
+                    osImage.name(),
+                    location,
+                    osImage.affinityGroup(),
+                    osImage.label(),
+                    osImage.description(),
+                    osImage.category(),
+                    osImage.os(),
+                    osImage.publisherName(),
+                    osImage.mediaLink(),
+                    osImage.logicalSizeInGB(),
+                    osImage.eula()
+            ));
+         }
+      }
+      return osImages;
    }
 
    @Override
-   public OSImage getImage(String id) {
-      // TODO Auto-generated method stub
-      return null;
+   public OSImage getImage(final String id) {
+      return Iterables.find(api.getOSImageApi().list(), new Predicate<OSImage>() {
+         @Override
+         public boolean apply(OSImage input) {
+            return input.name().equals(id);
+         }
+      });
    }
 
    @Override
-   public Iterable<String> listLocations() {
-      // TODO Auto-generated method stub
-      return null;
+   public Iterable<Location> listLocations() {
+      return api.getLocationApi().list();
    }
 
    @Override
-   public Deployment getNode(String id) {
-      // TODO Auto-generated method stub
-      return null;
+   public Deployment getNode(final String id) {
+      return FluentIterable.from(api.getCloudServiceApi().list())
+              .transform(new Function<CloudService, Deployment>() {
+                 @Override
+                 public Deployment apply(CloudService input) {
+                    return api.getDeploymentApiForService(input.name()).get(id);
+                 }
+              })
+              .firstMatch(notNull())
+              .orNull();
    }
 
    @Override
-   public void destroyNode(String id) {
-      // TODO Auto-generated method stub
+   public void destroyNode(final String id) {
+      CloudService cloudService = api.getCloudServiceApi().get(id);
+      if (cloudService != null) {
+         // TODO detach disk before deleting node
 
+         final String cloudServiceName = cloudService.name();
+         logger.debug("Deleting deployment(%s) of cloud service (%s)", id, cloudServiceName);
+         deleteDeployment(id, cloudServiceName);
+         logger.debug("Deployment (%s) deleted in cloud service (%s).", id, cloudServiceName);
+
+         logger.debug("Deleting cloud service (%s) ...", cloudServiceName);
+         deleteCloudService(cloudServiceName);
+         logger.debug("Cloud service (%s) deleted.", cloudServiceName);
+      }
    }
 
    @Override
-   public void rebootNode(String id) {
-      // TODO Auto-generated method stub
+   public void rebootNode(final String id) {
+      throw new UnsupportedOperationException();
    }
 
    @Override
    public void resumeNode(String id) {
-      // TODO Auto-generated method stub
+      throw new UnsupportedOperationException();
    }
 
    @Override
    public void suspendNode(String id) {
-      // TODO Auto-generated method stub
-
+      throw new UnsupportedOperationException();
    }
 
    @Override
    public Iterable<Deployment> listNodes() {
-      // TODO Auto-generated method stub
-      return null;
+      Set<Deployment> deployments = FluentIterable.from(api.getCloudServiceApi().list())
+              .transform(new Function<CloudService, Deployment>() {
+                 @Override
+                 public Deployment apply(CloudService cloudService) {
+                    return api.getDeploymentApiForService(cloudService.name()).get(cloudService.name());
+                 }
+              })
+              .filter(notNull())
+              .toSet();
+      return deployments;
+   }
+
+   @Override
+   public Iterable<Deployment> listNodesByIds(final Iterable<String> ids) {
+      return Iterables.filter(listNodes(), new Predicate<Deployment>() {
+         @Override
+         public boolean apply(Deployment input) {
+            return Iterables.contains(ids, input.name());
+         }
+      });
+   }
+
+   @VisibleForTesting
+   public static URI createMediaLink(String storageServiceName, String diskName) {
+      return URI.create(String.format("https://%s.blob.core.windows.net/vhds/disk-%s.vhd", storageServiceName, diskName));
    }
 
-   @Override public Iterable<Deployment> listNodesByIds(Iterable<String> ids) {
-      // TODO Auto-generated method stub
-      return null;
+   private void deleteCloudService(String name) {
+      String deleteCloudServiceId = api.getCloudServiceApi().delete(name);
+      if (!operationSucceededPredicate.apply(deleteCloudServiceId)) {
+         final String deleteMessage = generateIllegalStateExceptionMessage(deleteCloudServiceId, azureComputeConstants.operationTimeout());
+         logger.warn(deleteMessage);
+         throw new IllegalStateException(deleteMessage);
+      }
    }
+
+   private void deleteDeployment(String id, String cloudServiceName) {
+      String deleteDeploymentId = api.getDeploymentApiForService(cloudServiceName).delete(id);
+      if (!operationSucceededPredicate.apply(deleteDeploymentId)) {
+         final String deleteMessage = generateIllegalStateExceptionMessage(deleteDeploymentId, azureComputeConstants.operationTimeout());
+         logger.warn(deleteMessage);
+         throw new IllegalStateException(deleteMessage);
+      }
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e97ddaee/azurecompute/src/main/java/org/jclouds/azurecompute/compute/config/AzureComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/compute/config/AzureComputeServiceContextModule.java b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/config/AzureComputeServiceContextModule.java
index b09b867..30e2569 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/compute/config/AzureComputeServiceContextModule.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/config/AzureComputeServiceContextModule.java
@@ -16,28 +16,56 @@
  */
 package org.jclouds.azurecompute.compute.config;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.azurecompute.config.AzureComputeProperties.OPERATION_POLL_INITIAL_PERIOD;
+import static org.jclouds.azurecompute.config.AzureComputeProperties.OPERATION_POLL_MAX_PERIOD;
+import static org.jclouds.azurecompute.config.AzureComputeProperties.OPERATION_TIMEOUT;
+import static org.jclouds.azurecompute.config.AzureComputeProperties.TCP_RULE_FORMAT;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.azurecompute.AzureComputeApi;
 import org.jclouds.azurecompute.compute.AzureComputeServiceAdapter;
+import org.jclouds.azurecompute.compute.extensions.AzureComputeSecurityGroupExtension;
 import org.jclouds.azurecompute.compute.functions.DeploymentToNodeMetadata;
+import org.jclouds.azurecompute.compute.functions.LocationToLocation;
 import org.jclouds.azurecompute.compute.functions.OSImageToImage;
 import org.jclouds.azurecompute.compute.functions.RoleSizeToHardware;
+import org.jclouds.azurecompute.compute.strategy.GetOrCreateStorageServiceAndVirtualNetworkThenCreateNodes;
+import org.jclouds.azurecompute.compute.strategy.UseNodeCredentialsButOverrideFromTemplate;
 import org.jclouds.azurecompute.domain.Deployment;
+import org.jclouds.azurecompute.domain.Location;
 import org.jclouds.azurecompute.domain.OSImage;
+import org.jclouds.azurecompute.domain.Operation;
 import org.jclouds.azurecompute.domain.RoleSize;
+import org.jclouds.azurecompute.options.AzureComputeTemplateOptions;
 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.extensions.SecurityGroupExtension;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
+import org.jclouds.compute.strategy.PrioritizeCredentialsFromTemplate;
+import org.jclouds.util.Predicates2;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Provides;
 import com.google.inject.TypeLiteral;
 
 public class AzureComputeServiceContextModule
-      extends ComputeServiceAdapterContextModule<Deployment, RoleSize, OSImage, String> {
+      extends ComputeServiceAdapterContextModule<Deployment, RoleSize, OSImage, Location> {
 
    @Override
    protected void configure() {
       super.configure();
-      bind(new TypeLiteral<ComputeServiceAdapter<Deployment, RoleSize, OSImage, String>>() {
+      bind(new TypeLiteral<ComputeServiceAdapter<Deployment, RoleSize, OSImage, Location>>() {
       }).to(AzureComputeServiceAdapter.class);
       bind(new TypeLiteral<Function<OSImage, org.jclouds.compute.domain.Image>>() {
       }).to(OSImageToImage.class);
@@ -45,5 +73,94 @@ public class AzureComputeServiceContextModule
       }).to(RoleSizeToHardware.class);
       bind(new TypeLiteral<Function<Deployment, NodeMetadata>>() {
       }).to(DeploymentToNodeMetadata.class);
+
+      bind(PrioritizeCredentialsFromTemplate.class).to(UseNodeCredentialsButOverrideFromTemplate.class);
+      bind(new TypeLiteral<Function<Location, org.jclouds.domain.Location>>() {
+      }).to(LocationToLocation.class);
+
+      bind(TemplateOptions.class).to(AzureComputeTemplateOptions.class);
+
+      bind(new TypeLiteral<SecurityGroupExtension>() {}).to(AzureComputeSecurityGroupExtension.class);
+      bind(CreateNodesInGroupThenAddToSet.class).to(GetOrCreateStorageServiceAndVirtualNetworkThenCreateNodes.class);
+
+      // to have the compute service adapter override default locations
+      install(new LocationsFromComputeServiceAdapterModule<Deployment, RoleSize, OSImage, Location>(){});
+   }
+
+   @Override
+   protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) {
+      return Optional.of(i.getInstance(SecurityGroupExtension.class));
+   }
+
+   @Provides
+   @Singleton
+   protected Predicate<String> provideOperationSucceededPredicate(final AzureComputeApi api, AzureComputeConstants azureComputeConstants) {
+      return Predicates2.retry(new OperationSucceededPredicate(api),
+              azureComputeConstants.operationTimeout(), azureComputeConstants.operationPollInitialPeriod(),
+              azureComputeConstants.operationPollMaxPeriod());
    }
+
+   @VisibleForTesting
+   static class OperationSucceededPredicate implements Predicate<String> {
+
+      private final AzureComputeApi api;
+
+      public OperationSucceededPredicate(AzureComputeApi api) {
+         this.api = checkNotNull(api, "api must not be null");
+      }
+
+      @Override
+      public boolean apply(String input) {
+         Operation operation = api.getOperationApi().get(input);
+         switch (operation.status()) {
+            case SUCCEEDED:
+               return true;
+            case IN_PROGRESS:
+               return false;
+            case FAILED:
+               return false;
+            case UNRECOGNIZED:
+               return false;
+            default:
+               throw new IllegalStateException("Operation is in invalid status: " + operation.status().name());
+         }
+      }
+
+   }
+
+   @Singleton
+   public static class AzureComputeConstants {
+      @Named(OPERATION_TIMEOUT)
+      @Inject
+      private String operationTimeoutProperty;
+
+      @Named(OPERATION_POLL_INITIAL_PERIOD)
+      @Inject
+      private String operationPollInitialPeriodProperty;
+
+      @Named(OPERATION_POLL_MAX_PERIOD)
+      @Inject
+      private String operationPollMaxPeriodProperty;
+
+      @Named(TCP_RULE_FORMAT)
+      @Inject
+      private String tcpRuleFormatProperty;
+
+      public Long operationTimeout() {
+         return Long.parseLong(operationTimeoutProperty);
+      }
+
+      public Integer operationPollInitialPeriod() {
+         return Integer.parseInt(operationPollInitialPeriodProperty);
+      }
+
+      public Integer operationPollMaxPeriod() {
+         return Integer.parseInt(operationPollMaxPeriodProperty);
+      }
+
+      public String tcpRuleFormat() {
+         return tcpRuleFormatProperty;
+      }
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e97ddaee/azurecompute/src/main/java/org/jclouds/azurecompute/compute/extensions/AzureComputeSecurityGroupExtension.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/compute/extensions/AzureComputeSecurityGroupExtension.java b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/extensions/AzureComputeSecurityGroupExtension.java
new file mode 100644
index 0000000..d5e2db1
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/extensions/AzureComputeSecurityGroupExtension.java
@@ -0,0 +1,449 @@
+/*
+ * 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.compute.extensions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.azurecompute.compute.AzureComputeServiceAdapter.generateIllegalStateExceptionMessage;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.jclouds.azurecompute.AzureComputeApi;
+import org.jclouds.azurecompute.compute.config.AzureComputeServiceContextModule.AzureComputeConstants;
+import org.jclouds.azurecompute.config.AzureComputeProperties;
+import org.jclouds.azurecompute.domain.CloudService;
+import org.jclouds.azurecompute.domain.Deployment;
+import org.jclouds.azurecompute.domain.NetworkConfiguration;
+import org.jclouds.azurecompute.domain.NetworkConfiguration.VirtualNetworkSite;
+import org.jclouds.azurecompute.domain.NetworkSecurityGroup;
+import org.jclouds.azurecompute.domain.Role;
+import org.jclouds.azurecompute.domain.Rule;
+import org.jclouds.azurecompute.util.NetworkSecurityGroups;
+import org.jclouds.compute.domain.SecurityGroup;
+import org.jclouds.compute.domain.SecurityGroupBuilder;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.domain.Location;
+import org.jclouds.logging.Logger;
+import org.jclouds.net.domain.IpPermission;
+import org.jclouds.net.domain.IpProtocol;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.base.Splitter;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+
+/**
+ * An extension to compute service to allow for the manipulation of {@link org.jclouds.compute.domain.SecurityGroup}s. Implementation
+ * is optional by providers.
+ *
+ * It considers only the custom rules added by the user and ignores the default rules created by Azure
+ */
+public class AzureComputeSecurityGroupExtension implements SecurityGroupExtension {
+
+   protected final AzureComputeApi api;
+   private final Predicate<String> operationSucceededPredicate;
+   private final AzureComputeConstants azureComputeConstants;
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   @Inject
+   AzureComputeSecurityGroupExtension(final AzureComputeApi api, Predicate<String> operationSucceededPredicate, AzureComputeConstants azureComputeConstants) {
+      this.api = api;
+      this.operationSucceededPredicate = operationSucceededPredicate;
+      this.azureComputeConstants = azureComputeConstants;
+   }
+
+   @Override
+   public Set<SecurityGroup> listSecurityGroups() {
+      return FluentIterable.from(api.getNetworkSecurityGroupApi().list())
+                           .transform(new NetworkSecurityGroupSecurityGroupFunction())
+                           .toSet();
+   }
+
+   @Override
+   public Set<SecurityGroup> listSecurityGroupsInLocation(final Location location) {
+      return FluentIterable.from(api.getNetworkSecurityGroupApi().list())
+              .transform(new NetworkSecurityGroupSecurityGroupFunction())
+              .toSet();
+   }
+
+   /**
+    * @param name it represents both cloudservice and deployment name
+    * @return Set<SecurityGroup>
+    */
+   @Override
+   public Set<SecurityGroup> listSecurityGroupsForNode(String name) {
+      checkNotNull(name, "name");
+
+      Deployment deployment = api.getDeploymentApiForService(name).get(name);
+      final String virtualNetworkName = deployment.virtualNetworkName();
+
+      List<String> subnetNames = FluentIterable.from(deployment.roles())
+                    .transformAndConcat(new Function<Role, Iterable<Role.ConfigurationSet>>() {
+                       @Override
+                       public Iterable<Role.ConfigurationSet> apply(Role input) {
+                          return input.configurationSets();
+                       }
+                    })
+                    .transformAndConcat(new Function<Role.ConfigurationSet, Iterable<Role.ConfigurationSet.SubnetName>>() {
+                       @Override
+                       public Iterable<Role.ConfigurationSet.SubnetName> apply(Role.ConfigurationSet input) {
+                          return input.subnetNames();
+                       }
+                    })
+                    .transform(new Function<Role.ConfigurationSet.SubnetName, String>() {
+                       @Override
+                       public String apply(Role.ConfigurationSet.SubnetName input) {
+                          return input.name();
+                       }
+                    })
+                    .toList();
+
+      return FluentIterable.from(subnetNames)
+              .transform(new Function<String, NetworkSecurityGroup>() {
+                 @Override
+                 public NetworkSecurityGroup apply(String input) {
+                    return api.getNetworkSecurityGroupApi().getNetworkSecurityGroupAppliedToSubnet(virtualNetworkName, input);
+                 }
+              })
+              .transform(new NetworkSecurityGroupSecurityGroupFunction())
+              .toSet();
+   }
+
+   @Override
+   public SecurityGroup getSecurityGroupById(String id) {
+      return transformNetworkSecurityGroupToSecurityGroup(id);
+   }
+
+   @Override
+   public SecurityGroup createSecurityGroup(final String name, Location location) {
+      checkNotNull(name, "name");
+      checkNotNull(location, "location");
+
+      final NetworkSecurityGroup networkSecurityGroup = NetworkSecurityGroup.create(name, name, location.getId(), null);
+      String createNSGRequestId = api.getNetworkSecurityGroupApi().create(networkSecurityGroup);
+      if (operationSucceededPredicate.apply(createNSGRequestId)) {
+         final String message = generateIllegalStateExceptionMessage(createNSGRequestId, azureComputeConstants.operationTimeout());
+         logger.warn(message);
+         throw new IllegalStateException(message);
+      }
+      return transformNetworkSecurityGroupToSecurityGroup(name);
+   }
+
+   private SecurityGroup transformNetworkSecurityGroupToSecurityGroup(String name) {
+      final NetworkSecurityGroup fullDetails = api.getNetworkSecurityGroupApi().getFullDetails(name);
+      if (fullDetails == null) return null;
+      return new NetworkSecurityGroupSecurityGroupFunction().apply(fullDetails);
+   }
+
+   @Override
+   public boolean removeSecurityGroup(String id) {
+
+      final NetworkConfiguration networkConfiguration = api.getVirtualNetworkApi().getNetworkConfiguration();
+      if (networkConfiguration != null) {
+         for (VirtualNetworkSite virtualNetworkSite : networkConfiguration.virtualNetworkConfiguration().virtualNetworkSites()) {
+            for (NetworkConfiguration.Subnet subnet : virtualNetworkSite.subnets()) {
+               final String virtualNetworkName = virtualNetworkSite.name();
+               final String subnetName = subnet.name();
+               if (virtualNetworkName != null && subnetName != null) {
+                  NetworkSecurityGroup networkSecurityGroupAppliedToSubnet = api.getNetworkSecurityGroupApi()
+                          .getNetworkSecurityGroupAppliedToSubnet(virtualNetworkName, subnetName);
+                  if (networkSecurityGroupAppliedToSubnet != null) {
+                     if (!networkSecurityGroupAppliedToSubnet.name().equals(id)) {
+                        logger.debug("Removing a networkSecurityGroup %s is already applied to subnet '%s' ...", id, subnetName);
+                        // remove existing nsg from subnet
+                        String removeFromSubnetRequestId = api.getNetworkSecurityGroupApi().removeFromSubnet
+                                (virtualNetworkName, subnetName, networkSecurityGroupAppliedToSubnet.name());
+                        if (operationSucceededPredicate.apply(removeFromSubnetRequestId)) {
+                           final String message = generateIllegalStateExceptionMessage(removeFromSubnetRequestId, azureComputeConstants.operationTimeout());
+                           logger.warn(message);
+                           throw new IllegalStateException(message);
+                        }
+                     }
+                  }
+               }
+            }
+         }
+      }
+      String deleteRequestId = api.getNetworkSecurityGroupApi().delete(id);
+      return operationSucceededPredicate.apply(deleteRequestId);
+   }
+
+   @Override
+   public SecurityGroup addIpPermission(IpPermission ipPermission, SecurityGroup group) {
+      checkNotNull(group, "group");
+      checkNotNull(ipPermission, "ipPermission");
+      String id = checkNotNull(group.getId(), "group.getId()");
+
+      NetworkSecurityGroup networkSecurityGroup = api.getNetworkSecurityGroupApi().getFullDetails(group.getName());
+      List<Rule> filteredRules = NetworkSecurityGroups.getCustomRules(networkSecurityGroup);
+      int priority = NetworkSecurityGroups.getFirstAvailablePriority(filteredRules);
+
+      String ruleName = NetworkSecurityGroups.createRuleName(ipPermission.getFromPort(), ipPermission.getToPort());
+
+      // add rule to NSG
+      addRuleToNetworkSecurityGroup(id, ruleName, priority, ipPermission);
+
+      // add endpoint to VM
+      Set<Deployment> deployments = FluentIterable.from(api.getCloudServiceApi().list())
+              .transform(new Function<CloudService, Deployment>() {
+                 @Override
+                 public Deployment apply(CloudService cloudService) {
+                    return api.getDeploymentApiForService(cloudService.name()).get(cloudService.name());
+                 }
+              })
+              .filter(Predicates.notNull())
+              .toSet();
+      // TODO filter deployments
+      for (Deployment deployment : deployments) {
+         Deployment.VirtualIP virtualIP = Iterables.tryFind(deployment.virtualIPs(), Predicates.notNull()).orNull();
+         if (virtualIP == null) throw new IllegalStateException("Deployment doesn't contain virtual IPs");
+
+         for (Role role : deployment.roles()) {
+            for (Role.ConfigurationSet configurationSet : role.configurationSets()) {
+               if (ipPermission.getFromPort() < ipPermission.getToPort()) {
+                  for (int i = ipPermission.getFromPort(); i <= ipPermission.getToPort(); i++) {
+                     String name = String.format(AzureComputeProperties.TCP_RULE_FORMAT, i, i);
+                     configurationSet.inputEndpoints().add(createInputEndpoint(name, ipPermission.getIpProtocol().name(),
+                             virtualIP.address(), i));
+                  }
+               } else {
+                  configurationSet.inputEndpoints().add(createInputEndpoint(ruleName, ipPermission.getIpProtocol().name(),
+                          virtualIP.address(), ipPermission.getToPort()));
+               }
+               String updateRoleRequestId = api.getVirtualMachineApiForDeploymentInService(deployment.name(), deployment.name()).updateRole(role.roleName(), role);
+               if (operationSucceededPredicate.apply(updateRoleRequestId)) {
+                  final String message = generateIllegalStateExceptionMessage(updateRoleRequestId, azureComputeConstants.operationTimeout());
+                  logger.warn(message);
+                  throw new IllegalStateException(message);
+               }
+            }
+         }
+
+      }
+      return transformNetworkSecurityGroupToSecurityGroup(id);
+   }
+
+   @Override
+   public SecurityGroup addIpPermission(IpProtocol protocol, int startPort, int endPort,
+                                        Multimap<String, String> tenantIdGroupNamePairs,
+                                        Iterable<String> ipRanges,
+                                        Iterable<String> groupIds, SecurityGroup group) {
+      IpPermission.Builder permBuilder = IpPermission.builder();
+      permBuilder.ipProtocol(protocol);
+      permBuilder.fromPort(startPort);
+      permBuilder.toPort(endPort);
+      permBuilder.tenantIdGroupNamePairs(tenantIdGroupNamePairs);
+      permBuilder.cidrBlocks(ipRanges);
+      permBuilder.groupIds(groupIds);
+
+      return addIpPermission(permBuilder.build(), group);
+   }
+
+   @Override
+   public SecurityGroup removeIpPermission(IpPermission ipPermission, SecurityGroup group) {
+      checkNotNull(group, "group");
+      checkNotNull(ipPermission, "ipPermission");
+      String id = checkNotNull(group.getId(), "group.getId()");
+
+      String ruleName = NetworkSecurityGroups.createRuleName(ipPermission.getFromPort(), ipPermission.getToPort());
+      // remove rule to NSG
+      removeRuleFromNetworkSecurityGroup(id, ruleName);
+
+      // TODO remove endpoint from VM
+      Set<Deployment> deployments = FluentIterable.from(api.getCloudServiceApi().list())
+              .transform(new Function<CloudService, Deployment>() {
+                 @Override
+                 public Deployment apply(CloudService cloudService) {
+                    return api.getDeploymentApiForService(cloudService.name()).get(cloudService.name());
+                 }
+              })
+              .filter(Predicates.notNull())
+              .toSet();
+      // TODO filter deployments
+      for (Deployment deployment : deployments) {
+         Deployment.VirtualIP virtualIP = Iterables.tryFind(deployment.virtualIPs(), Predicates.notNull()).orNull();
+         if (virtualIP == null) throw new IllegalStateException("");
+
+         for (Role role : deployment.roles()) {
+            for (Role.ConfigurationSet configurationSet : role.configurationSets()) {
+               for (int i = ipPermission.getFromPort(); i <= ipPermission.getToPort(); i++) {
+                  String name = String.format(AzureComputeProperties.TCP_RULE_FORMAT, i, i);
+                  configurationSet.inputEndpoints().remove(createInputEndpoint(name, ipPermission.getIpProtocol()
+                          .name().toLowerCase(), virtualIP.address(), i));
+               }
+               String updateRoleRequestId = api.getVirtualMachineApiForDeploymentInService(deployment.name(), deployment.name()).updateRole(role.roleName(), role);
+               if (operationSucceededPredicate.apply(updateRoleRequestId)) {
+                  final String message = generateIllegalStateExceptionMessage(updateRoleRequestId, azureComputeConstants.operationTimeout());
+                  logger.warn(message);
+                  throw new IllegalStateException(message);
+               }
+            }
+         }
+      }
+      return transformNetworkSecurityGroupToSecurityGroup(id);
+   }
+
+   @Override
+   public SecurityGroup removeIpPermission(IpProtocol protocol, int startPort, int endPort,
+                                           Multimap<String, String> tenantIdGroupNamePairs,
+                                           Iterable<String> ipRanges,
+                                           Iterable<String> groupIds, SecurityGroup group) {
+      IpPermission.Builder permBuilder = IpPermission.builder();
+      permBuilder.ipProtocol(protocol);
+      permBuilder.fromPort(startPort);
+      permBuilder.toPort(endPort);
+      permBuilder.tenantIdGroupNamePairs(tenantIdGroupNamePairs);
+      permBuilder.cidrBlocks(ipRanges);
+      permBuilder.groupIds(groupIds);
+
+      return removeIpPermission(permBuilder.build(), group);
+   }
+
+   @Override
+   public boolean supportsTenantIdGroupNamePairs() {
+      return false;
+   }
+
+   @Override
+   public boolean supportsTenantIdGroupIdPairs() {
+      return false;
+   }
+
+   @Override
+   public boolean supportsGroupIds() {
+      return false;
+   }
+
+   @Override
+   public boolean supportsPortRangesForGroups() {
+      return false;
+   }
+
+   @Override
+   public boolean supportsExclusionCidrBlocks() {
+      return false;
+   }
+
+   public static class RuleToIpPermission implements Function<Rule, IpPermission> {
+
+      @Override
+      public IpPermission apply(Rule rule) {
+         IpPermission.Builder builder = IpPermission.builder();
+         if (rule.name().matches("tcp_\\d{1,5}-\\d{1,5}")) {
+            builder.fromPort(extractPort(rule.name(), 0))
+                    .toPort(extractPort(rule.name(), 1));
+         }
+         builder.ipProtocol(rule.protocol().equals("*") ? IpProtocol.ALL : IpProtocol.valueOf(rule.protocol()));
+         if (rule.destinationAddressPrefix().equals("*")) {
+            builder.cidrBlock("0.0.0.0/0");
+         } else {
+            builder.cidrBlock(rule.destinationAddressPrefix());
+         }
+         return builder.build();
+      }
+
+      private int extractPort(String ruleName, int position) {
+         return Integer.parseInt(Iterables.get(Splitter.on("-").omitEmptyStrings().split(ruleName.substring(4, ruleName.length())),
+                 position));
+      }
+   }
+
+   private static class NetworkSecurityGroupSecurityGroupFunction implements Function<NetworkSecurityGroup, SecurityGroup> {
+      @Override
+      public SecurityGroup apply(NetworkSecurityGroup networkSecurityGroup) {
+         SecurityGroupBuilder securityGroupBuilder = new SecurityGroupBuilder()
+                 .id(networkSecurityGroup.name())
+                 .providerId(networkSecurityGroup.label())
+                 .name(networkSecurityGroup.name());
+         if (networkSecurityGroup.rules() != null) {
+
+            List<Rule> filteredRules = NetworkSecurityGroups.getCustomRules(networkSecurityGroup);
+
+            Iterable<IpPermission> permissions = Iterables.transform(filteredRules, new RuleToIpPermission());
+            securityGroupBuilder.ipPermissions(permissions);
+         }
+         return securityGroupBuilder.build();
+      }
+   }
+
+   private void addRuleToNetworkSecurityGroup(String networkSecurityGroupId, String ruleName, int
+           priority, IpPermission ipPermission) {
+
+      String protocol = ipPermission.getIpProtocol().name();
+      String destinationPortRange;
+      if (ipPermission.getFromPort() != ipPermission.getToPort()) {
+         destinationPortRange = String.format("%s-%s", ipPermission.getFromPort(), ipPermission.getToPort());
+      } else {
+         destinationPortRange = String.valueOf(ipPermission.getToPort());
+      }
+      final String destinationAddressPrefix =
+              ipPermission.getCidrBlocks().isEmpty() || Iterables.get(ipPermission.getCidrBlocks(), 0).equals("0.0.0.0/0") ?
+                      "*" : Iterables.get(ipPermission.getCidrBlocks(), 0);
+      String setRuleToNSGRequestId = api.getNetworkSecurityGroupApi().setRule(networkSecurityGroupId, ruleName,
+              Rule.create(ruleName, // name
+                      "Inbound", // type
+                      String.valueOf(priority), // priority
+                      "Allow", // action
+                      "INTERNET", // sourceAddressPrefix
+                      "*", // sourcePortRange
+                      destinationAddressPrefix, // destinationAddressPrefix
+                      destinationPortRange, // destinationPortRange
+                      protocol,  // protocol
+                      "Active", // state
+                      true // isDefault
+              ));
+      if (operationSucceededPredicate.apply(setRuleToNSGRequestId)) {
+         final String message = generateIllegalStateExceptionMessage(setRuleToNSGRequestId, azureComputeConstants.operationTimeout());
+         logger.warn(message);
+         throw new IllegalStateException(message);
+      }
+   }
+
+   private void removeRuleFromNetworkSecurityGroup(String id, String ruleName) {
+      String setRuleToNSGRequestId = api.getNetworkSecurityGroupApi().deleteRule(id, ruleName);
+      if (operationSucceededPredicate.apply(setRuleToNSGRequestId)) {
+         final String message = generateIllegalStateExceptionMessage(setRuleToNSGRequestId, azureComputeConstants.operationTimeout());
+         logger.warn(message);
+         throw new IllegalStateException(message);
+      }
+   }
+
+   private Role.ConfigurationSet.InputEndpoint createInputEndpoint(String ruleName, String protocol, String address, int port) {
+      return Role.ConfigurationSet.InputEndpoint.create(
+              ruleName,
+              protocol,
+              port,
+              port,
+              address,
+              false, // enabledDirectServerReturn
+              null, // loadBalancerName
+              null, // loadBalancerProbe
+              null //idleTimeoutInMinutes
+      );
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e97ddaee/azurecompute/src/main/java/org/jclouds/azurecompute/compute/functions/DeploymentToNodeMetadata.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/compute/functions/DeploymentToNodeMetadata.java b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/functions/DeploymentToNodeMetadata.java
index 127ea02..0d099e8 100644
--- a/azurecompute/src/main/java/org/jclouds/azurecompute/compute/functions/DeploymentToNodeMetadata.java
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/functions/DeploymentToNodeMetadata.java
@@ -16,16 +16,108 @@
  */
 package org.jclouds.azurecompute.compute.functions;
 
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+
 import org.jclouds.azurecompute.domain.Deployment;
+import org.jclouds.azurecompute.domain.Deployment.RoleInstance;
+import org.jclouds.collect.Memoized;
 import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.domain.Credentials;
+import org.jclouds.domain.Location;
 
 import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
 
 public class DeploymentToNodeMetadata implements Function<Deployment, NodeMetadata> {
 
+	private final Supplier<Set<? extends Location>> locations;
+	private final GroupNamingConvention nodeNamingConvention;
+	private final OSImageToImage osImageToImage;
+	private final RoleSizeToHardware roleSizeToHardware;
+	private final Map<String, Credentials> credentialStore;
+
+	public static final Map<Deployment.Status, NodeMetadata.Status> serverStateToNodeStatus = ImmutableMap
+			  .<Deployment.Status, NodeMetadata.Status> builder()
+			  .put(Deployment.Status.DELETING, NodeMetadata.Status.PENDING)
+			  .put(Deployment.Status.SUSPENDED_TRANSITIONING, NodeMetadata.Status.PENDING)
+			  .put(Deployment.Status.RUNNING_TRANSITIONING, NodeMetadata.Status.PENDING)
+			  .put(Deployment.Status.DEPLOYING, NodeMetadata.Status.PENDING)
+			  .put(Deployment.Status.STARTING, NodeMetadata.Status.PENDING)
+			  .put(Deployment.Status.SUSPENDED, NodeMetadata.Status.SUSPENDED)
+			  .put(Deployment.Status.RUNNING, NodeMetadata.Status.RUNNING)
+			  .put(Deployment.Status.UNRECOGNIZED, NodeMetadata.Status.UNRECOGNIZED).build();
+
+	@Inject
+	DeploymentToNodeMetadata(@Memoized Supplier<Set<? extends Location>> locations,
+										GroupNamingConvention.Factory namingConvention, OSImageToImage osImageToImage,
+										RoleSizeToHardware roleSizeToHardware, Map<String, Credentials> credentialStore) {
+		this.nodeNamingConvention = namingConvention.createWithoutPrefix();
+		this.locations = locations;
+		this.osImageToImage = osImageToImage;
+		this.roleSizeToHardware = roleSizeToHardware;
+		this.credentialStore = credentialStore;
+	}
+
 	@Override
-	public NodeMetadata apply(Deployment input) {
-		return null;
+	public NodeMetadata apply(Deployment from) {
+		NodeMetadataBuilder builder = new NodeMetadataBuilder();
+		builder.id(from.name());
+      builder.providerId(from.name());
+		builder.name(from.name());
+		builder.hostname(getHostname(from));
+		/* TODO
+		if (from.getDatacenter() != null) {
+			builder.location(from(locations.get()).firstMatch(
+					  LocationPredicates.idEquals(from.getDatacenter().getId() + "")).orNull());
+		}
+		builder.group(nodeNamingConvention.groupInUniqueNameOrNull(from.getHostname()));
+		builder.hardware(roleSizeToHardware.apply(from.instanceSize()));
+		Image image = osImageToImage.apply(from);
+		if (image != null) {
+			builder.imageId(image.getId());
+			builder.operatingSystem(image.getOperatingSystem());
+		}
+		*/
+		if (from.status() != null) {
+			builder.status(serverStateToNodeStatus.get(from.status()));
+		}
+		Set<String> publicIpAddresses = Sets.newLinkedHashSet();
+		if (from.virtualIPs() != null) {
+			for (Deployment.VirtualIP virtualIP : from.virtualIPs()) {
+				publicIpAddresses.add(virtualIP.address());
+			}
+			builder.publicAddresses(publicIpAddresses);
+		}
+		Set<String> privateIpAddresses = Sets.newLinkedHashSet();
+		if (from.roleInstanceList() != null) {
+			for (RoleInstance roleInstance : from.roleInstanceList()) {
+				privateIpAddresses.add(roleInstance.ipAddress());
+			}
+			builder.privateAddresses(privateIpAddresses);
+		}
+		return builder.build();
 	}
 
+	private String getHostname(Deployment from) {
+      final Optional<RoleInstance> roleInstanceOptional = tryFindFirstRoleInstanceInDeployment(from);
+      if (!roleInstanceOptional.isPresent()) {
+         return from.name();
+      } else {
+         return roleInstanceOptional.get().hostname();
+      }
+   }
+
+   private Optional<RoleInstance> tryFindFirstRoleInstanceInDeployment(Deployment deployment) {
+      if (deployment.roleInstanceList() == null || deployment.roleInstanceList().isEmpty()) return Optional.absent();
+      return Optional.of(deployment.roleInstanceList().get(0));
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/e97ddaee/azurecompute/src/main/java/org/jclouds/azurecompute/compute/functions/LocationToLocation.java
----------------------------------------------------------------------
diff --git a/azurecompute/src/main/java/org/jclouds/azurecompute/compute/functions/LocationToLocation.java b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/functions/LocationToLocation.java
new file mode 100644
index 0000000..228d875
--- /dev/null
+++ b/azurecompute/src/main/java/org/jclouds/azurecompute/compute/functions/LocationToLocation.java
@@ -0,0 +1,56 @@
+/*
+ * 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.compute.functions;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.azurecompute.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.location.suppliers.all.JustProvider;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Converts an Location into a Location.
+ */
+@Singleton
+public class LocationToLocation implements Function<Location, org.jclouds.domain.Location> {
+   private final JustProvider justProvider;
+
+   // allow us to lazy discover the provider of a resource
+   @Inject
+   LocationToLocation(JustProvider justProvider) {
+      this.justProvider = justProvider;
+   }
+
+   @Override
+   public org.jclouds.domain.Location apply(Location location) {
+      LocationBuilder builder = new LocationBuilder();
+      builder.id(location.name());
+      builder.description(location.displayName());
+      builder.scope(LocationScope.REGION);
+      builder.parent(getOnlyElement(justProvider.get()));
+      builder.iso3166Codes(ImmutableSet.<String> of("name", location.name()));
+      return builder.build();
+   }
+
+}