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

[2/2] jclouds-labs git commit: JCLOUDS-664 Azurecompute-arm ResourceGroupApi

JCLOUDS-664 Azurecompute-arm ResourceGroupApi


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

Branch: refs/heads/master
Commit: a4334d46d8ac45859945978771fc481303d65af9
Parents: ff028de
Author: Rita Zhang <ri...@gmail.com>
Authored: Thu Feb 11 18:43:45 2016 -0800
Committer: Ignasi Barrera <na...@apache.org>
Committed: Wed Apr 6 21:23:08 2016 +0200

----------------------------------------------------------------------
 README.md                                       |   2 +-
 azurecompute-arm/README.md                      | 109 ++++++++++++
 azurecompute-arm/pom.xml                        | 177 +++++++++++++++++++
 .../azurecompute.arm/AzureComputeApi.java       |  43 +++++
 .../AzureComputeProviderMetadata.java           |  94 ++++++++++
 .../AzureManagementApiMetadata.java             |  86 +++++++++
 .../config/AzureComputeHttpApiModule.java       |  57 ++++++
 .../config/AzureComputeParserModule.java        |  29 +++
 .../config/AzureComputeProperties.java          |  34 ++++
 .../azurecompute.arm/domain/ResourceGroup.java  |  51 ++++++
 .../azurecompute.arm/features/JobApi.java       |  41 +++++
 .../features/ResourceGroupApi.java              |  96 ++++++++++
 .../functions/ParseJobStatus.java               |  54 ++++++
 .../azurecompute.arm/functions/URIParser.java   |  39 ++++
 .../handlers/AzureComputeErrorHandler.java      |  86 +++++++++
 .../AzureComputeProviderMetadataTest.java       |  28 +++
 .../features/JobApiMockTest.java                |  62 +++++++
 .../features/ResourceGroupApiLiveTest.java      | 137 ++++++++++++++
 .../features/ResourceGroupApiMockTest.java      | 150 ++++++++++++++++
 .../functions/URIParserTest.java                |  51 ++++++
 .../AbstractAzureComputeApiLiveTest.java        |  69 ++++++++
 .../internal/AzureLiveTestUtils.java            |  34 ++++
 .../internal/BaseAzureComputeApiLiveTest.java   |  65 +++++++
 .../internal/BaseAzureComputeApiMockTest.java   | 134 ++++++++++++++
 .../src/test/resources/resourcegroup.json       |  11 ++
 .../src/test/resources/resourcegroups.json      |  22 +++
 .../test/resources/resourcegroupupdated.json    |   9 +
 pom.xml                                         |   1 +
 28 files changed, 1770 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 91c4b50..b656b5d 100644
--- a/README.md
+++ b/README.md
@@ -7,4 +7,4 @@ License
 -------
 Copyright (C) 2009-2014 The Apache Software Foundation
 
-Licensed under the Apache License, Version 2.0
+Licensed under the Apache License, Version 2.0
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/README.md
----------------------------------------------------------------------
diff --git a/azurecompute-arm/README.md b/azurecompute-arm/README.md
new file mode 100644
index 0000000..a3abe63
--- /dev/null
+++ b/azurecompute-arm/README.md
@@ -0,0 +1,109 @@
+jclouds Labs - Azure Compute ARM Provider
+============
+
+Build status for azurecomputearm module:
+[![Build Status](http://devopsfunjenkins.westus.cloudapp.azure.com:8080/buildStatus/icon?job=jclouds-labs-azurecompute-arm/org.apache.jclouds.labs:azurecomputearm)](http://devopsfunjenkins.westus.cloudapp.azure.com:8080/job/jclouds-labs-azurecompute-arm/org.apache.jclouds.labs$azurecomputearm/)
+
+
+## Setting Up Test Credentials
+
+### Create a Service Principal
+
+Install and configure Azure CLI following these [steps](http://azure.microsoft.com/en-us/documentation/articles/xplat-cli/).
+
+Using the Azure CLI, run the following commands to create a service principal
+
+```bash
+# Set mode to ARM
+azure config mode arm
+
+# Enter your Microsoft account credentials when prompted
+azure login
+
+# Set current subscription to create a service principal
+azure account set <Subscription-id>
+
+# Create an AAD application with your information.
+azure ad app create --name <name> --password <password> --home-page <home-page> --identifier-uris <identifier-uris>
+
+# For example: azure ad app create --name "jcloudsarm"  --password abcd --home-page "https://jcloudsarm" --identifier-uris "https://jcloudsarm"
+
+# Output will include a value for `Application Id`, which will be used for the live tests
+
+# Create a Service Principal
+azure ad sp create <Application-id>
+
+# Output will include a value for `Object Id`
+
+```
+
+Run the following commands to assign roles to the service principal
+
+```bash
+# Assign roles for this service principal
+azure role assignment create --objectId <Object-id> -o Contributor -c /subscriptions/<Subscription-id>/
+
+```
+
+Verify service principal
+
+```bash
+azure login -u <Application-id> -p <password> --service-principal --tenant <Tenant-id>
+
+```
+
+## Run Live Tests
+
+
+Use the following to run the live tests
+
+```bash
+# ResourceGroupApiLiveTest:
+
+mvn -Dtest=ResourceGroupApiLiveTest -Dtest.azurecompute-arm.identity=<Application-id> -Dtest.azurecompute-arm.subscriptionid=<Subscription-id> -Dtest.azurecompute-arm.credential=<password> -Dtest.azurecompute-arm.endpoint="https://management.azure.com" -Dtest.jclouds.oauth.resource="https://management.azure.com/" -Dtest.oauth.endpoint="https://login.microsoftonline.com/<Tenant-id>/oauth2/token" test
+
+# AuthorizationApiLiveTest:
+
+mvn -Dtest=AuthorizationApiLiveTest -Dtest.oauth.identity=<Application-id> -Dtest.oauth.credential=<password> -Dtest.jclouds.oauth.resource="https://management.azure.com/" -Dtest.oauth.endpoint="https://login.microsoftonline.com/<Tenant-id>/oauth2/token" -Dtest.jclouds.oauth.audience="https://management.azure.com/" test
+
+# LocationApiLiveTest:
+
+mvn -Dtest=LocationApiLiveTest -Dtest.azurecompute-arm.identity=<Application-id> -Dtest.azurecompute-arm.subscriptionid=<Subscription-id> -Dtest.azurecompute-arm.credential=<password> -Dtest.azurecompute-arm.endpoint="https://management.azure.com" -Dtest.jclouds.oauth.resource="https://management.azure.com/" -Dtest.oauth.endpoint="https://login.microsoftonline.com/<Tenant-id>/oauth2/token" test
+
+# StorageAccountApiLiveTest:
+
+mvn -Dtest=StorageAccountApiLiveTest -Dtest.azurecompute-arm.identity=<Application-id> -Dtest.azurecompute-arm.subscriptionid=<Subscription-id> -Dtest.azurecompute-arm.credential=<password> -Dtest.azurecompute-arm.endpoint="https://management.azure.com" -Dtest.jclouds.oauth.resource="https://management.azure.com/" -Dtest.oauth.endpoint="https://login.microsoftonline.com/<Tenant-id>/oauth2/token" test
+
+# VirtualNetworkApiLiveTest:
+
+mvn -Dtest=VirtualNetworkApiLiveTest -Dtest.azurecompute-arm.identity=<Application-id> -Dtest.azurecompute-arm.subscriptionid=<Subscription-id> -Dtest.azurecompute-arm.credential=<password> -Dtest.azurecompute-arm.resourcegroup="jcloudstest" -Dtest.azurecompute-arm.endpoint="https://management.azure.com" -Dtest.jclouds.oauth.resource="https://management.azure.com/" -Dtest.oauth.endpoint="https://login.microsoftonline.com/<Tenant-id>/oauth2/token" test
+
+# SubnetApiLiveTest
+
+mvn -Dtest=SubnetApiLiveTest -Dtest.azurecompute-arm.identity=<Application-id> -Dtest.azurecompute-arm.subscriptionid=<Subscription-id> -Dtest.azurecompute-arm.credential=<password> -Dtest.azurecompute-arm.resourcegroup="jcloudstest" -Dtest.azurecompute-arm.endpoint="https://management.azure.com" -Dtest.jclouds.oauth.resource="https://management.azure.com/" -Dtest.oauth.endpoint="https://login.microsoftonline.com/<Tenant-id>/oauth2/token" test
+
+# NetworkInterfaceCardApiLiveTest
+
+mvn -Dtest=NetworkInterfaceCardApiLiveTest -Dtest.azurecompute-arm.identity=<Application-id> -Dtest.azurecompute-arm.subscriptionid=<Subscription-id> -Dtest.azurecompute-arm.credential=<password> -Dtest.azurecompute-arm.resourcegroup="jcloudstest" -Dtest.azurecompute-arm.endpoint="https://management.azure.com" -Dtest.jclouds.oauth.resource="https://management.azure.com/" -Dtest.oauth.endpoint="https://login.microsoftonline.com/<Tenant-id>/oauth2/token" test
+
+# VirtualMachineApiLiveTest:
+
+mvn -Dtest=VirtualMachineApiLiveTest -Dtest.azurecompute-arm.identity=<Application-id> -Dtest.azurecompute-arm.subscriptionid=<Subscription-id> -Dtest.azurecompute-arm.credential=<password> -Dtest.azurecompute-arm.endpoint="https://management.azure.com" -Dtest.jclouds.oauth.resource="https://management.azure.com/" -Dtest.oauth.endpoint="https://login.microsoftonline.com/<Tenant-id>/oauth2/token" test
+
+# VMSizeApiLiveTest:
+
+mvn -Dtest=VMSizeApiLiveTest -Dtest.azurecompute-arm.identity=<Application-id> -Dtest.azurecompute-arm.subscriptionid=<Subscription-id> -Dtest.azurecompute-arm.credential=<password> -Dtest.azurecompute-arm.endpoint="https://management.azure.com" -Dtest.jclouds.oauth.resource="https://management.azure.com/" -Dtest.oauth.endpoint="https://login.microsoftonline.com/<Tenant-id>/oauth2/token" test
+
+# PublicIPAddressApiLiveTest
+
+mvn -Dtest=PublicIPAddressApiLiveTest -Dtest.azurecompute-arm.identity=<Application-id> -Dtest.azurecompute-arm.subscriptionid=<Subscription-id> -Dtest.azurecompute-arm.credential=<password> -Dtest.azurecompute-arm.endpoint="https://management.azure.com" -Dtest.jclouds.oauth.resource="https://management.azure.com/" -Dtest.oauth.endpoint="https://login.microsoftonline.com/<Tenant-id>/oauth2/token" test
+
+# OSImageApiLiveTest:
+
+mvn -Dtest=OSImageApiLiveTest -Dtest.azurecompute-arm.identity=<Application-id> -Dtest.azurecompute-arm.subscriptionid=<Subscription-id> -Dtest.azurecompute-arm.credential=<password> -Dtest.azurecompute-arm.endpoint="https://management.azure.com" -Dtest.jclouds.oauth.resource="https://management.azure.com/" -Dtest.oauth.endpoint="https://login.microsoftonline.com/<Tenant-id>/oauth2/token" test
+
+# DeploymentApiLiveTest:
+
+mvn -Dtest=DeploymentApiLiveTest -Dtest.azurecompute-arm.identity=<Application-id> -Dtest.azurecompute-arm.subscriptionid=<Subscription-id> -Dtest.azurecompute-arm.credential=<password> -Dtest.azurecompute-arm.endpoint="https://management.azure.com/" -Dtest.jclouds.oauth.resource="https://management.azure.com/" -Dtest.oauth.endpoint="https://login.microsoftonline.com/<Tenant-id>/oauth2/token" test
+
+```
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/pom.xml
----------------------------------------------------------------------
diff --git a/azurecompute-arm/pom.xml b/azurecompute-arm/pom.xml
new file mode 100644
index 0000000..807ab20
--- /dev/null
+++ b/azurecompute-arm/pom.xml
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.jclouds.labs</groupId>
+    <artifactId>jclouds-labs</artifactId>
+    <version>2.0.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>azurecompute-arm</artifactId>
+  <name>jclouds Azure Compute ARM API</name>
+  <description>jclouds components to access an implementation of Azure's ARM Compute Service</description>
+  <packaging>bundle</packaging>
+
+  <properties>
+    <test.jclouds.azurecompute-arm.subscription-id>FIXME_subscription-id</test.jclouds.azurecompute-arm.subscription-id>
+    <test.jclouds.azurecompute-arm.tenant-id>FIXME_tenant-id</test.jclouds.azurecompute-arm.tenant-id>
+    <test.azurecompute-arm.endpoint>https://management.azure.com/subscriptions/${test.jclouds.azurecompute-arm.subscription-id}</test.azurecompute-arm.endpoint>
+    <test.azurecompute-arm.api-version>2014-04-01-preview</test.azurecompute-arm.api-version>
+    <test.azurecompute-arm.build-version />
+    <test.azurecompute-arm.identity>app id</test.azurecompute-arm.identity>
+    <test.azurecompute-arm.credential>app password</test.azurecompute-arm.credential>
+    <jclouds.osgi.export>org.jclouds.azurecompute-arm*;version="${project.version}"</jclouds.osgi.export>
+    <jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.jclouds</groupId>
+      <artifactId>jclouds-compute</artifactId>
+      <version>${project.parent.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.auto.service</groupId>
+      <artifactId>auto-service</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.auto.value</groupId>
+      <artifactId>auto-value</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds</groupId>
+      <artifactId>jclouds-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds.api</groupId>
+      <artifactId>oauth</artifactId>
+      <version>${project.version}</version>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds.api</groupId>
+      <artifactId>oauth</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds</groupId>
+      <artifactId>jclouds-compute</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds</groupId>
+      <artifactId>jclouds-compute</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds</groupId>
+      <artifactId>jclouds-core</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds.driver</groupId>
+      <artifactId>jclouds-slf4j</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds.driver</groupId>
+      <artifactId>jclouds-sshj</artifactId>
+      <version>${project.version}</version>
+      <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-okhttp</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.squareup.okhttp</groupId>
+      <artifactId>mockwebserver</artifactId>
+      <scope>test</scope>
+      <exclusions>
+        <!-- Already provided by jclouds-sshj -->
+        <exclusion>
+          <groupId>org.bouncycastle</groupId>
+          <artifactId>bcprov-jdk15on</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+  </dependencies>
+
+  <profiles>
+    <profile>
+      <id>live</id>
+
+      <build>
+        <defaultGoal>clean verify</defaultGoal>
+
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>integration</id>
+                <phase>integration-test</phase>
+                <goals>
+                  <goal>test</goal>
+                </goals>
+                <configuration>
+                  <systemPropertyVariables>
+                    <test.azurecompute-arm.endpoint>${test.azurecompute-arm.endpoint}</test.azurecompute-arm.endpoint>
+                    <test.azurecompute-arm.api-version>${test.azurecompute-arm.api-version}</test.azurecompute-arm.api-version>
+                    <test.azurecompute-arm.build-version>${test.azurecompute-arm.build-version}</test.azurecompute-arm.build-version>
+                    <test.azurecompute-arm.identity>${test.azurecompute-arm.identity}</test.azurecompute-arm.identity>
+                    <test.azurecompute-arm.credential>${test.azurecompute-arm.credential}</test.azurecompute-arm.credential>
+                    <test.jclouds.azurecompute-arm.subscription-id>${test.jclouds.azurecompute-arm.subscription-id}</test.jclouds.azurecompute-arm.subscription-id>
+                    <test.jclouds.azurecompute-arm.tenant-id>${test.jclouds.azurecompute-arm.tenant-id}</test.jclouds.azurecompute-arm.tenant-id>
+                  </systemPropertyVariables>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+</project>

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/AzureComputeApi.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/AzureComputeApi.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/AzureComputeApi.java
new file mode 100644
index 0000000..a897c9a
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/AzureComputeApi.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.arm;
+
+import java.io.Closeable;
+
+import org.jclouds.azurecompute.arm.features.JobApi;
+import org.jclouds.azurecompute.arm.features.ResourceGroupApi;
+import org.jclouds.rest.annotations.Delegate;
+
+/**
+ * The Azure Resource Manager API is a REST API for managing your services and deployments.
+ * <p/>
+ *
+ * @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.
+    *
+    * @see <a href="https://msdn.microsoft.com/en-us/library/azure/dn790546.aspx">docs</a>
+    */
+   @Delegate
+   ResourceGroupApi getResourceGroupApi();
+
+   @Delegate
+   JobApi getJobApi();
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/AzureComputeProviderMetadata.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/AzureComputeProviderMetadata.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/AzureComputeProviderMetadata.java
new file mode 100644
index 0000000..68d7a86
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/AzureComputeProviderMetadata.java
@@ -0,0 +1,94 @@
+/*
+ * 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;
+
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_INITIAL_PERIOD;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_MAX_PERIOD;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_TIMEOUT;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_FORMAT;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_REGEXP;
+import static org.jclouds.oauth.v2.config.CredentialType.CLIENT_CREDENTIALS_SECRET;
+import static org.jclouds.oauth.v2.config.OAuthProperties.RESOURCE;
+import static org.jclouds.oauth.v2.config.OAuthProperties.CREDENTIAL_TYPE;
+
+import java.net.URI;
+import java.util.Properties;
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadata;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(ProviderMetadata.class)
+public class AzureComputeProviderMetadata extends BaseProviderMetadata {
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   @Override
+   public Builder toBuilder() {
+      return builder().fromProviderMetadata(this);
+   }
+
+   public AzureComputeProviderMetadata() {
+      super(builder());
+   }
+
+   public static Properties defaultProperties() {
+      final Properties properties = AzureManagementApiMetadata.defaultProperties();
+      properties.setProperty(OPERATION_TIMEOUT, "60000");
+      properties.setProperty(OPERATION_POLL_INITIAL_PERIOD, "5");
+      properties.setProperty(OPERATION_POLL_MAX_PERIOD, "15");
+      properties.setProperty(TCP_RULE_FORMAT, "tcp_%s-%s");
+      properties.setProperty(TCP_RULE_REGEXP, "tcp_\\d{1,5}-\\d{1,5}");
+      properties.put("oauth.endpoint", "https://login.microsoftonline.com/oauth2/token");
+      properties.put(RESOURCE, "https://management.azure.com");
+      properties.put(CREDENTIAL_TYPE, CLIENT_CREDENTIALS_SECRET.toString());
+      return properties;
+   }
+
+   public AzureComputeProviderMetadata(final Builder builder) {
+      super(builder);
+   }
+
+   public static class Builder extends BaseProviderMetadata.Builder {
+
+      protected Builder() {
+         super();
+
+         id("azurecompute-arm")
+                 .name("Azure Resource Management ")
+                 .apiMetadata(new AzureManagementApiMetadata())
+                 .endpoint("https://management.azure.com/subscriptions/SUBSCRIPTION_ID")
+                 .homepage(URI.create("https://www.windowsazure.com/"))
+                 .console(URI.create("https://windows.azure.com/default.aspx"))
+                 .linkedServices("azureblob")
+                 .defaultProperties(AzureComputeProviderMetadata.defaultProperties());
+      }
+
+      @Override
+      public AzureComputeProviderMetadata build() {
+         return new AzureComputeProviderMetadata(this);
+      }
+
+      @Override
+      public Builder fromProviderMetadata(final ProviderMetadata providerMetadata) {
+         super.fromProviderMetadata(providerMetadata);
+         return this;
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/AzureManagementApiMetadata.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/AzureManagementApiMetadata.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/AzureManagementApiMetadata.java
new file mode 100644
index 0000000..9a3292c
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/AzureManagementApiMetadata.java
@@ -0,0 +1,86 @@
+/*
+ * 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;
+
+import static org.jclouds.reflect.Reflection2.typeToken;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.azurecompute.arm.config.AzureComputeHttpApiModule;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.rest.internal.BaseHttpApiMetadata;
+import org.jclouds.oauth.v2.config.OAuthModule;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+import org.jclouds.http.okhttp.config.OkHttpCommandExecutorServiceModule;
+
+/**
+ * Implementation of {@link ApiMetadata} for Microsoft Azure Resource Manager REST API
+ */
+public class AzureManagementApiMetadata extends BaseHttpApiMetadata<AzureComputeApi> {
+
+   @Override
+   public Builder toBuilder() {
+      return new Builder().fromApiMetadata(this);
+   }
+
+   public AzureManagementApiMetadata() {
+      this(new Builder());
+   }
+
+   protected AzureManagementApiMetadata(final Builder builder) {
+      super(builder);
+   }
+
+   public static Properties defaultProperties() {
+      final Properties properties = BaseHttpApiMetadata.defaultProperties();
+      return properties;
+   }
+
+   public static class Builder extends BaseHttpApiMetadata.Builder<AzureComputeApi, Builder> {
+
+      protected Builder() {
+         super();
+
+         id("azurecompute-arm")
+                 .name("Microsoft Azure Resource Manager REST API")
+                 .identityName("Azure Service Principal Application Id")
+                 .credentialName("Azure Service Principal Application Password")
+                 .endpointName("Resource Manager Endpoint ending in your Subscription Id")
+                 .documentation(URI.create("https://msdn.microsoft.com/en-us/library/azure/dn790568.aspx"))
+                 .defaultProperties(AzureManagementApiMetadata.defaultProperties())
+                 .view(typeToken(ComputeServiceContext.class))
+                 .defaultModules(ImmutableSet.<Class<? extends Module>>builder()
+                         .add(OAuthModule.class)
+                         .add(OkHttpCommandExecutorServiceModule.class)
+                         .add(AzureComputeHttpApiModule.class).build());
+      }
+
+      @Override
+      public AzureManagementApiMetadata build() {
+         return new AzureManagementApiMetadata(this);
+      }
+
+      @Override
+      protected Builder self() {
+         return this;
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/config/AzureComputeHttpApiModule.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/config/AzureComputeHttpApiModule.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/config/AzureComputeHttpApiModule.java
new file mode 100644
index 0000000..9041d96
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/config/AzureComputeHttpApiModule.java
@@ -0,0 +1,57 @@
+/*
+ * 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.config;
+import org.jclouds.azurecompute.arm.AzureComputeApi;
+import org.jclouds.azurecompute.arm.handlers.AzureComputeErrorHandler;
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.annotation.ClientError;
+import org.jclouds.http.annotation.Redirection;
+import org.jclouds.http.annotation.ServerError;
+import org.jclouds.location.suppliers.ImplicitLocationSupplier;
+import org.jclouds.location.suppliers.implicit.OnlyLocationOrFirstRegionOptionallyMatchingRegionId;
+
+import org.jclouds.rest.ConfiguresHttpApi;
+import org.jclouds.rest.config.HttpApiModule;
+import org.jclouds.oauth.v2.config.OAuthScopes;
+
+import com.google.inject.Scopes;
+
+@ConfiguresHttpApi
+public class AzureComputeHttpApiModule extends HttpApiModule<AzureComputeApi> {
+
+   @Override
+   protected void bindErrorHandlers() {
+      bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(AzureComputeErrorHandler.class);
+      bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(AzureComputeErrorHandler.class);
+      bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(AzureComputeErrorHandler.class);
+   }
+
+   @Override
+   protected void installLocations() {
+      super.installLocations();
+      bind(ImplicitLocationSupplier.class).
+              to(OnlyLocationOrFirstRegionOptionallyMatchingRegionId.class).
+              in(Scopes.SINGLETON);
+   }
+
+   @Override
+   protected void configure() {
+      install(new AzureComputeParserModule());
+      super.configure();
+      bind(OAuthScopes.class).toInstance(OAuthScopes.ReadOrWriteScopes.create("read", "read write"));
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/config/AzureComputeParserModule.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/config/AzureComputeParserModule.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/config/AzureComputeParserModule.java
new file mode 100644
index 0000000..396f5be
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/config/AzureComputeParserModule.java
@@ -0,0 +1,29 @@
+/*
+ * 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.config;
+
+import org.jclouds.json.config.GsonModule;
+
+import com.google.inject.AbstractModule;
+
+public class AzureComputeParserModule extends AbstractModule {
+
+   @Override
+   protected void configure() {
+      bind(GsonModule.DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/config/AzureComputeProperties.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/config/AzureComputeProperties.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/config/AzureComputeProperties.java
new file mode 100644
index 0000000..e16b5da
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/config/AzureComputeProperties.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.arm.config;
+
+/**
+ * Configuration properties and constants used in Azure Resource Manager connections.
+ */
+public class AzureComputeProperties {
+
+   public static final String OPERATION_TIMEOUT = "jclouds.azurecompute.arm.operation.timeout";
+
+   public static final String OPERATION_POLL_INITIAL_PERIOD = "jclouds.azurecompute.arm.operation.poll.initial.period";
+
+   public static final String OPERATION_POLL_MAX_PERIOD = "jclouds.azurecompute.arm.operation.poll.max.period";
+
+   public static final String TCP_RULE_FORMAT = "jclouds.azurecompute.arm.tcp.rule.format";
+
+   public static final String TCP_RULE_REGEXP = "jclouds.azurecompute.arm.tcp.rule.regexp";
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/domain/ResourceGroup.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/domain/ResourceGroup.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/domain/ResourceGroup.java
new file mode 100644
index 0000000..1be93ab
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/domain/ResourceGroup.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.arm.domain;
+
+import com.google.auto.value.AutoValue;
+import java.util.Map;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+import com.google.common.collect.ImmutableMap;
+
+
+@AutoValue
+public abstract class ResourceGroup {
+
+   @AutoValue
+   public abstract static class ResourceGroupProperties{
+      @Nullable
+      public abstract String provisioningState();
+
+      @SerializedNames({"provisioningState"})
+      public static ResourceGroupProperties create(final String provisioningState) {
+         return new AutoValue_ResourceGroup_ResourceGroupProperties(provisioningState);
+      }
+   }
+
+   public abstract String id();
+   public abstract String name();
+   public abstract String location();
+   @Nullable
+   public abstract Map<String, String> tags();
+   public abstract ResourceGroupProperties properties();
+
+   @SerializedNames({"id", "name", "location", "tags", "properties"})
+   public static ResourceGroup create(String id, String name, String location, @Nullable Map<String, String> tags, ResourceGroupProperties properties) {
+      return new AutoValue_ResourceGroup(id, name, location, tags == null ? ImmutableMap.<String, String>builder().build() : ImmutableMap.copyOf(tags), properties);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/features/JobApi.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/features/JobApi.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/features/JobApi.java
new file mode 100644
index 0000000..7dd75a9
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/features/JobApi.java
@@ -0,0 +1,41 @@
+/*
+ * 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.features;
+import java.io.Closeable;
+import java.net.URI;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.GET;
+import org.jclouds.oauth.v2.filters.OAuthFilter;
+import org.jclouds.rest.annotations.EndpointParam;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.azurecompute.arm.functions.ParseJobStatus;
+import org.jclouds.azurecompute.arm.functions.ParseJobStatus.JobStatus;
+
+/**
+ * The Azure Resource Manager API checks for job status and progress.
+ */
+
+@RequestFilters(OAuthFilter.class)
+@Consumes(MediaType.APPLICATION_JSON)
+public interface JobApi extends Closeable{
+   @GET
+   @ResponseParser(ParseJobStatus.class)
+   JobStatus jobStatus(@EndpointParam URI jobURI);
+}
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/features/ResourceGroupApi.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/features/ResourceGroupApi.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/features/ResourceGroupApi.java
new file mode 100644
index 0000000..1ad47d9
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/features/ResourceGroupApi.java
@@ -0,0 +1,96 @@
+/*
+ * 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.features;
+import java.io.Closeable;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+
+import org.jclouds.Fallbacks.EmptyListOnNotFoundOr404;
+import org.jclouds.Fallbacks.NullOnNotFoundOr404;
+import org.jclouds.azurecompute.arm.domain.ResourceGroup;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.oauth.v2.filters.OAuthFilter;
+import org.jclouds.rest.annotations.QueryParams;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.SelectJson;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.MapBinder;
+import org.jclouds.rest.annotations.PayloadParam;
+import org.jclouds.rest.annotations.PATCH;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.azurecompute.arm.functions.URIParser;
+
+
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+/**
+ * The Azure Resource Manager API includes operations for managing resource groups in your subscription.
+ *
+ * @see <a href="https://msdn.microsoft.com/en-us/library/azure/dn790546.aspx">docs</a>
+ */
+@Path("/resourcegroups")
+
+@QueryParams(keys = "api-version", values = "2015-01-01")
+@RequestFilters(OAuthFilter.class)
+@Consumes(MediaType.APPLICATION_JSON)
+public interface ResourceGroupApi extends Closeable{
+
+   @Named("resourcegroup:list")
+   @SelectJson("value")
+   @GET
+   @Fallback(EmptyListOnNotFoundOr404.class)
+   List<ResourceGroup> list();
+
+   @Named("resourcegroup:create")
+   @PUT
+   @Path("/{name}")
+   @Produces(MediaType.APPLICATION_JSON)
+   @MapBinder(BindToJsonPayload.class)
+   ResourceGroup create(@PathParam("name") String name, @PayloadParam("location") String location, @Nullable @PayloadParam("tags")Map<String, String> tags);
+
+   @Named("resourcegroup:get")
+   @GET
+   @Path("/{name}")
+   @Fallback(NullOnNotFoundOr404.class)
+   @Nullable
+   ResourceGroup get(@PathParam("name") String name);
+
+   @Named("resourcegroup:update")
+   @PATCH
+   @Produces(MediaType.APPLICATION_JSON)
+   @Path("/{name}")
+   @MapBinder(BindToJsonPayload.class)
+   ResourceGroup update(@PathParam("name") String name, @Nullable @PayloadParam("tags")Map<String, String> tags);
+
+   @Named("resourcegroup:delete")
+   @DELETE
+   @ResponseParser(URIParser.class)
+   @Path("/{name}")
+   @Fallback(NullOnNotFoundOr404.class)
+   URI delete(@PathParam("name") String name);
+}
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/functions/ParseJobStatus.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/functions/ParseJobStatus.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/functions/ParseJobStatus.java
new file mode 100644
index 0000000..f39db90
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/functions/ParseJobStatus.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.arm.functions;
+import com.google.common.base.Function;
+import org.jclouds.http.HttpResponse;
+
+import javax.inject.Singleton;
+/**
+ * Parses job status from http response
+ */
+@Singleton
+public class ParseJobStatus implements Function<HttpResponse, ParseJobStatus.JobStatus> {
+   public enum JobStatus {
+
+      DONE,
+      IN_PROGRESS,
+      FAILED,
+      UNRECOGNIZED;
+
+      public static JobStatus fromString(final String text) {
+         if (text != null) {
+            for (JobStatus status : JobStatus.values()) {
+               if (text.equalsIgnoreCase(status.name())) {
+                  return status;
+               }
+            }
+         }
+         return UNRECOGNIZED;
+      }
+   }
+   public JobStatus apply(final HttpResponse from) {
+      if (from.getStatusCode() == 202 ){
+         return JobStatus.IN_PROGRESS;
+      } else if (from.getStatusCode() == 200 ){
+         return JobStatus.DONE;
+      } else {
+         return JobStatus.FAILED;
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/functions/URIParser.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/functions/URIParser.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/functions/URIParser.java
new file mode 100644
index 0000000..78fd10d
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/functions/URIParser.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.arm.functions;
+import com.google.common.base.Function;
+import org.jclouds.http.HttpResponse;
+
+import javax.inject.Singleton;
+
+import java.net.URI;
+/**
+ * Parses job status from http response
+ */
+@Singleton
+public class URIParser implements Function<HttpResponse, URI> {
+   public URI apply(final HttpResponse from) {
+      if (from.getStatusCode() == 202 && from.getHeaders().containsKey("Location")){
+         String uri = from.getFirstHeaderOrNull("Location");
+         return URI.create(uri);
+
+      } else if (from.getStatusCode() == 200){
+         return null;
+      }
+      throw new IllegalStateException("did not receive expected response code and header in: " + from);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/handlers/AzureComputeErrorHandler.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/handlers/AzureComputeErrorHandler.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/handlers/AzureComputeErrorHandler.java
new file mode 100644
index 0000000..d5a2d69
--- /dev/null
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute.arm/handlers/AzureComputeErrorHandler.java
@@ -0,0 +1,86 @@
+/*
+ * 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.handlers;
+
+import java.io.IOException;
+
+import javax.inject.Singleton;
+
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.ResourceNotFoundException;
+import org.jclouds.util.Closeables2;
+import org.jclouds.util.Strings2;
+
+/**
+ * This will parse and set an appropriate exception on the command object.
+ */
+@Singleton
+public class AzureComputeErrorHandler implements HttpErrorHandler {
+
+   @Override
+   public void handleError(final HttpCommand command, final HttpResponse response) {
+      // it is important to always read fully and close streams
+      String message = parseMessage(response);
+      Exception exception = message == null
+              ? new HttpResponseException(command, response)
+              : new HttpResponseException(command, response, message);
+      try {
+         message = message == null
+                 ? String.format("%s -> %s", command.getCurrentRequest().getRequestLine(), response.getStatusLine())
+                 : message;
+         switch (response.getStatusCode()) {
+            case 400:
+               exception = new IllegalArgumentException(message, exception);
+               break;
+            case 401:
+            case 403:
+               exception = new AuthorizationException(message, exception);
+               break;
+
+            case 404:
+               if (!command.getCurrentRequest().getMethod().equals("DELETE")) {
+                  exception = new ResourceNotFoundException(message, exception);
+               }
+               break;
+
+            case 409:
+               exception = new IllegalStateException(message, exception);
+               break;
+
+            default:
+         }
+      } finally {
+         Closeables2.closeQuietly(response.getPayload());
+         command.setException(exception);
+      }
+   }
+
+   public String parseMessage(final HttpResponse response) {
+      if (response.getPayload() == null) {
+         return null;
+      }
+      try {
+         return Strings2.toStringAndClose(response.getPayload().openStream());
+      } catch (IOException e) {
+         throw new RuntimeException(e);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/AzureComputeProviderMetadataTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/AzureComputeProviderMetadataTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/AzureComputeProviderMetadataTest.java
new file mode 100644
index 0000000..2003f9a
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/AzureComputeProviderMetadataTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+import org.jclouds.providers.internal.BaseProviderMetadataTest;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "AzureManagementProviderMetadataTest")
+public class AzureComputeProviderMetadataTest extends BaseProviderMetadataTest {
+
+   public AzureComputeProviderMetadataTest() {
+      super(new AzureComputeProviderMetadata(), new AzureManagementApiMetadata());
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/features/JobApiMockTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/features/JobApiMockTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/features/JobApiMockTest.java
new file mode 100644
index 0000000..ba87ca5
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/features/JobApiMockTest.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.arm.features;
+
+import java.io.IOException;
+import java.net.URI;
+import org.jclouds.azurecompute.arm.functions.ParseJobStatus.JobStatus;
+import org.jclouds.azurecompute.arm.internal.BaseAzureComputeApiMockTest;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+@Test(groups = "unit", testName = "JobApiMockTest", singleThreaded = true)
+public class JobApiMockTest extends BaseAzureComputeApiMockTest {
+
+   final String requestUrl = "/operationresults/eyJqb2JJZCI6IlJFU09VUkNFR1JPVVBERUxFVElPTkpPQi1SVEVTVC1DRU5UUkFMVVMiLCJqb2JMb2NhdGlvbiI6ImNlbnRyYWx1cyJ9?api-version=2014-04-01";
+
+   public void testGetJobStatus() throws IOException, InterruptedException {
+      server.enqueue(response200());
+
+      JobStatus status = api.getJobApi().jobStatus(URI.create(requestUrl));
+
+      assertEquals(status, JobStatus.DONE);
+
+      assertSent(server, "GET", requestUrl);
+   }
+
+   public void testGetJobStatusInProgress() throws InterruptedException {
+      server.enqueue(response202WithHeader());
+
+      JobStatus status = api.getJobApi().jobStatus(URI.create(requestUrl));
+
+      assertEquals(status, JobStatus.IN_PROGRESS);
+
+      assertSent(server, "GET", requestUrl);
+   }
+
+   public void testGetJobStatusFailed() throws InterruptedException {
+      server.enqueue(jsonResponse("/resourcegroup.json").setStatus("HTTP/1.1 204 No Content"));
+
+      JobStatus status = api.getJobApi().jobStatus(URI.create(requestUrl));
+
+      assertEquals(status, JobStatus.FAILED);
+
+      assertSent(server, "GET", requestUrl);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/features/ResourceGroupApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/features/ResourceGroupApiLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/features/ResourceGroupApiLiveTest.java
new file mode 100644
index 0000000..1ad8bf3
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/features/ResourceGroupApiLiveTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.features;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+import java.net.URI;
+import java.util.List;
+
+import org.jclouds.azurecompute.arm.domain.ResourceGroup;
+import org.jclouds.azurecompute.arm.functions.ParseJobStatus.JobStatus;
+import org.jclouds.azurecompute.arm.internal.BaseAzureComputeApiLiveTest;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertEquals;
+import org.jclouds.util.Predicates2;
+
+
+@Test(groups = "live", testName = "ResourceGroupApiLiveTest")
+public class ResourceGroupApiLiveTest extends BaseAzureComputeApiLiveTest {
+   private String resourcegroup;
+
+   @BeforeClass
+   @Override
+   public void setup(){
+      super.setup();
+      resourcegroup = getResourceGroupName();
+   }
+
+   private ResourceGroupApi api() {
+      return api.getResourceGroupApi();
+   }
+
+   @Test(dependsOnMethods = "testCreate")
+   public void testList() {
+      final List<ResourceGroup> resourceGroups = api().list();
+
+      assertTrue(resourceGroups.size() > 0);
+
+      assertTrue(Iterables.any(resourceGroups, new Predicate<ResourceGroup>() {
+
+         @Override
+         public boolean apply(final ResourceGroup group) {
+            return resourcegroup.equals(group.name());
+         }
+      }));
+   }
+
+   @Test(dependsOnMethods = "testCreate")
+   public void testRead() {
+      final ResourceGroup group = api().get(resourcegroup);
+      assertNotNull(group);
+      assertEquals(group.name(), resourcegroup);
+      assertEquals(group.location(), LOCATION);
+   }
+
+   public void testCreate() {
+
+      final ResourceGroup resourceGroup = api().create("jcloudstest", LOCATION, null);
+      assertEquals(resourceGroup.name(), "jcloudstest");
+      assertEquals(resourceGroup.location(), LOCATION);
+      assertEquals(resourceGroup.tags().size(), 0);
+      assertTrue(resourceGroup.id().contains("jcloudstest"));
+      assertEquals(resourceGroup.properties().provisioningState(), "Succeeded");
+   }
+
+   @Test(dependsOnMethods = "testCreate")
+   public void testUpdateWithEmptyTag() {
+      ImmutableMap<String, String> tags = ImmutableMap.<String, String>builder().build();
+
+      final ResourceGroup resourceGroup = api().update("jcloudstest", tags);
+
+      assertEquals(resourceGroup.tags().size(), 0);
+      assertEquals(resourceGroup.properties().provisioningState(), "Succeeded");
+   }
+
+   @Test(dependsOnMethods = "testCreate")
+   public void testUpdateWithTag() {
+      ImmutableMap<String, String> tags = ImmutableMap.<String, String>builder().put("test1", "value1").build();
+
+      final ResourceGroup resourceGroup = api().update("jcloudstest", tags);
+
+      assertEquals(resourceGroup.tags().size(), 1);
+      assertEquals(resourceGroup.properties().provisioningState(), "Succeeded");
+   }
+
+   @AfterClass(alwaysRun = true)
+   public void testDelete() throws Exception {
+      URI uri =  api().delete(resourcegroup);
+
+      if (uri != null){
+         assertTrue(uri.toString().contains("api-version"));
+         assertTrue(uri.toString().contains("operationresults"));
+
+         boolean jobDone = Predicates2.retry(new Predicate<URI>() {
+            @Override public boolean apply(URI uri) {
+               return JobStatus.DONE == api.getJobApi().jobStatus(uri);
+            }
+         }, 60 * 1 * 1000 /* 1 minute timeout */).apply(uri);
+         assertTrue(jobDone, "delete operation did not complete in the configured timeout");
+      }
+
+      uri =  api().delete("jcloudstest");
+      if (uri != null){
+         assertTrue(uri.toString().contains("api-version"));
+         assertTrue(uri.toString().contains("operationresults"));
+
+         boolean jobDone = Predicates2.retry(new Predicate<URI>() {
+            @Override public boolean apply(URI uri) {
+               return JobStatus.DONE == api.getJobApi().jobStatus(uri);
+            }
+         }, 60 * 1 * 1000 /* 1 minute timeout */).apply(uri);
+         assertTrue(jobDone, "delete operation did not complete in the configured timeout");
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/features/ResourceGroupApiMockTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/features/ResourceGroupApiMockTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/features/ResourceGroupApiMockTest.java
new file mode 100644
index 0000000..91cb2b1
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/features/ResourceGroupApiMockTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.features;
+
+import static com.google.common.collect.Iterables.isEmpty;
+import static com.google.common.collect.Iterables.size;
+import static org.testng.Assert.assertNotNull;
+
+import java.net.URI;
+import java.util.List;
+import com.google.common.collect.ImmutableMap;
+
+import org.jclouds.azurecompute.arm.domain.ResourceGroup;
+import org.jclouds.azurecompute.arm.internal.BaseAzureComputeApiMockTest;
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertEquals;
+
+@Test(groups = "unit", testName = "ResourceGroupApiMockTest", singleThreaded = true)
+public class ResourceGroupApiMockTest extends BaseAzureComputeApiMockTest {
+
+   final String subscriptionid = "12345678-1234-1234-1234-123456789012";
+   final String requestUrl = "/subscriptions/" + subscriptionid + "/resourcegroups";
+   final String version = "?api-version=2015-01-01";
+
+   public void testListResourceGroups() throws InterruptedException {
+      server.enqueue(jsonResponse("/resourcegroups.json"));
+
+      List<ResourceGroup> resourceGroups = api.getResourceGroupApi().list();
+
+      assertEquals(size(resourceGroups), 2);
+
+      assertSent(server, "GET", requestUrl + version);
+   }
+
+   public void testListResourceGroupsReturns404() throws InterruptedException {
+      server.enqueue(response404());
+
+      List<ResourceGroup> resourceGroups = api.getResourceGroupApi().list();
+
+      assertTrue(isEmpty(resourceGroups));
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", requestUrl + version);
+   }
+
+   public void testCreateResourceGroup() throws InterruptedException {
+      server.enqueue(jsonResponse("/resourcegroup.json").setStatus("HTTP/1.1 201 Created"));
+
+      ImmutableMap<String, String> tags = ImmutableMap.<String, String>builder().put("tagname1", "tagvalue1").build();
+
+      ResourceGroup resourceGroup = api.getResourceGroupApi().create("jcloudstest", "West US", tags);
+
+      assertEquals(resourceGroup.name(), "jcloudstest");
+      assertEquals(resourceGroup.location(), "westus");
+      assertEquals(resourceGroup.tags().size(), 1);
+      assertTrue(resourceGroup.id().contains("jcloudstest"));
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "PUT", requestUrl + "/jcloudstest" + version, String.format("{\"location\":\"%s\", \"tags\":{\"tagname1\":\"tagvalue1\"}}", "West US"));
+   }
+
+   public void testCreateResourceGroupWithNoTag() throws InterruptedException {
+      server.enqueue(jsonResponse("/resourcegroup.json").setStatus("HTTP/1.1 201 Created"));
+
+      ResourceGroup resourceGroup = api.getResourceGroupApi().create("jcloudstest", "West US", null);
+
+      assertEquals(resourceGroup.name(), "jcloudstest");
+      assertEquals(resourceGroup.location(), "westus");
+      assertTrue(resourceGroup.id().contains("jcloudstest"));
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "PUT", requestUrl + "/jcloudstest" + version, String.format("{\"location\":\"%s\"}", "West US"));
+   }
+
+   public void testGetResourceGroup() throws InterruptedException {
+      server.enqueue(jsonResponse("/resourcegroup.json"));
+
+      ResourceGroup resourceGroup = api.getResourceGroupApi().get("jcloudstest");
+
+      assertEquals(resourceGroup.name(), "jcloudstest");
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", requestUrl + "/jcloudstest" + version);
+   }
+
+   public void testGetResourceGroupReturns404() throws InterruptedException {
+      server.enqueue(response404());
+
+      ResourceGroup resourceGroup = api.getResourceGroupApi().get("jcloudstest");
+
+      assertNull(resourceGroup);
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", requestUrl + "/jcloudstest" + version);
+   }
+
+   public void testUpdateResourceGroupTags() throws InterruptedException {
+      server.enqueue(jsonResponse("/resourcegroupupdated.json"));
+
+      ImmutableMap<String, String> tags = ImmutableMap.<String, String>builder().build();
+
+      ResourceGroup resourceGroup = api.getResourceGroupApi().update("jcloudstest", tags);
+
+
+      assertEquals(resourceGroup.tags().size(), 0);
+      assertEquals(resourceGroup.properties().provisioningState(), "Succeeded");
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "PATCH", requestUrl + "/jcloudstest" + version, "{\"tags\":{}}");
+   }
+
+   public void testDeleteResourceGroup() throws InterruptedException {
+      server.enqueue(response202WithHeader());
+
+      URI uri = api.getResourceGroupApi().delete("jcloudstest");
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "DELETE", requestUrl + "/jcloudstest" + version);
+      assertNotNull(uri);
+
+      assertTrue(uri.toString().contains("api-version"));
+      assertTrue(uri.toString().contains("operationresults"));
+   }
+
+   public void testDeleteResourceGroupReturns404() throws InterruptedException {
+      server.enqueue(response404());
+
+      URI uri = api.getResourceGroupApi().delete("jcloudstest");
+      assertNull(uri);
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "DELETE", requestUrl + "/jcloudstest" + version);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/functions/URIParserTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/functions/URIParserTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/functions/URIParserTest.java
new file mode 100644
index 0000000..f7b234d
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/functions/URIParserTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.arm.functions;
+
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertNotNull;
+
+import java.net.URI;
+
+import org.jclouds.http.HttpResponse;
+import org.testng.annotations.Test;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.LinkedHashMultimap;
+
+@Test(groups = "unit", testName = "URIParserTest")
+public class URIParserTest {
+
+   public void testApply() {
+      URIParser parser = new URIParser();
+      Multimap<String, String> headers = LinkedHashMultimap.<String, String> create();
+
+      URI uri = parser.apply(HttpResponse.builder().statusCode(200).build());
+      assertNull(uri);
+
+      try {
+         uri = parser.apply(HttpResponse.builder().statusCode(202).build());
+      } catch (IllegalStateException ex){
+         assertNotNull(ex);
+      }
+
+      headers.put("Location", "https://someuri");
+
+      uri = parser.apply(HttpResponse.builder().statusCode(202).headers(headers).build());
+      assertNotNull(uri);
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/internal/AbstractAzureComputeApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/internal/AbstractAzureComputeApiLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/internal/AbstractAzureComputeApiLiveTest.java
new file mode 100644
index 0000000..aa0663a
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/internal/AbstractAzureComputeApiLiveTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_INITIAL_PERIOD;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_POLL_MAX_PERIOD;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.OPERATION_TIMEOUT;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_FORMAT;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TCP_RULE_REGEXP;
+import java.util.Properties;
+import java.util.Random;
+
+import org.jclouds.apis.BaseApiLiveTest;
+import org.jclouds.azurecompute.arm.AzureComputeApi;
+import org.jclouds.azurecompute.arm.AzureComputeProviderMetadata;
+import org.jclouds.compute.config.ComputeServiceProperties;
+import org.jclouds.providers.ProviderMetadata;
+
+public abstract class AbstractAzureComputeApiLiveTest extends BaseApiLiveTest<AzureComputeApi> {
+
+   protected static final int RAND = new Random().nextInt(999);
+
+   public AbstractAzureComputeApiLiveTest() {
+      provider = "azurecompute-arm";
+   }
+
+   @Override protected Properties setupProperties() {
+      Properties properties = super.setupProperties();
+      properties.put(ComputeServiceProperties.POLL_INITIAL_PERIOD, 1000);
+      properties.put(ComputeServiceProperties.POLL_MAX_PERIOD, 10000);
+      properties.setProperty(OPERATION_TIMEOUT, "60000");
+      properties.setProperty(OPERATION_POLL_INITIAL_PERIOD, "5");
+      properties.setProperty(OPERATION_POLL_MAX_PERIOD, "15");
+      properties.setProperty(TCP_RULE_FORMAT, "tcp_%s-%s");
+      properties.setProperty(TCP_RULE_REGEXP, "tcp_\\d{1,5}-\\d{1,5}");
+
+      // for oauth
+      AzureLiveTestUtils.defaultProperties(properties);
+      checkNotNull(setIfTestSystemPropertyPresent(properties, "jclouds.oauth.resource"), "test.jclouds.oauth.resource");
+      checkNotNull(setIfTestSystemPropertyPresent(properties, "oauth.endpoint"), "test.oauth.endpoint");
+      return properties;
+   }
+
+   @Override
+   protected ProviderMetadata createProviderMetadata() {
+      AzureComputeProviderMetadata pm = AzureComputeProviderMetadata.builder().build();
+      String endpoint = null;
+      if (System.getProperty("test.azurecompute-arm.endpoint") != null){
+         endpoint = System.getProperty("test.azurecompute-arm.endpoint");
+         pm.toBuilder().endpoint(endpoint);
+      }
+      return pm;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/internal/AzureLiveTestUtils.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/internal/AzureLiveTestUtils.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/internal/AzureLiveTestUtils.java
new file mode 100644
index 0000000..c578e84
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/internal/AzureLiveTestUtils.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azurecompute.arm.internal;
+
+import java.util.Properties;
+import static org.jclouds.oauth.v2.config.OAuthProperties.CREDENTIAL_TYPE;
+import static org.jclouds.oauth.v2.config.CredentialType.CLIENT_CREDENTIALS_SECRET;
+
+public class AzureLiveTestUtils {
+
+    public static Properties defaultProperties(Properties properties) {
+       properties = properties == null ? new Properties() : properties;
+       properties.put("oauth.identity", "foo");
+       properties.put("oauth.credential", "password");
+       properties.put("oauth.endpoint", "https://login.microsoftonline.com/oauth2/token");
+       properties.put(CREDENTIAL_TYPE, CLIENT_CREDENTIALS_SECRET.toString());
+       return properties;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/internal/BaseAzureComputeApiLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/internal/BaseAzureComputeApiLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/internal/BaseAzureComputeApiLiveTest.java
new file mode 100644
index 0000000..fb2e42a
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/internal/BaseAzureComputeApiLiveTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.internal;
+import static org.testng.Assert.assertNotNull;
+
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.azurecompute.arm.domain.ResourceGroup;
+
+import org.testng.annotations.AfterClass;
+
+public class BaseAzureComputeApiLiveTest extends AbstractAzureComputeApiLiveTest {
+   public static final String LOCATION = "westeurope";
+   private String resourceGroupName = null;
+
+   protected String getEndpoint() {
+      String endpoint = null;
+      if (System.getProperty("test.azurecompute-arm.endpoint") != null) {
+         endpoint = System.getProperty("test.azurecompute-arm.endpoint");
+      }
+      assertNotNull(endpoint);
+      return endpoint;
+   }
+
+   protected String getResourceGroupName() {
+      if (resourceGroupName == null) {
+         resourceGroupName = String.format("%3.24s",
+                 System.getProperty("user.name") + RAND + "groupjclouds");
+         createResourceGroup(resourceGroupName);
+      }
+      return resourceGroupName;
+   }
+
+   private void createResourceGroup(String name) {
+      ImmutableMap<String, String> tags = ImmutableMap.<String, String>builder().build();
+
+      final ResourceGroup resourceGroup = api.getResourceGroupApi().create(
+              name, LOCATION, tags);
+   }
+
+   private void deleteResourceGroup(String name) {
+      api.getResourceGroupApi().delete(name);
+   }
+
+
+   @AfterClass(alwaysRun = true)
+   @Override
+   protected void tearDown() {
+      super.tearDown();
+      deleteResourceGroup(getResourceGroupName());
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/internal/BaseAzureComputeApiMockTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/internal/BaseAzureComputeApiMockTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/internal/BaseAzureComputeApiMockTest.java
new file mode 100644
index 0000000..fdbdf70
--- /dev/null
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute.arm/internal/BaseAzureComputeApiMockTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.internal;
+import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
+import static org.testng.Assert.assertEquals;
+import static org.jclouds.oauth.v2.config.CredentialType.BEARER_TOKEN_CREDENTIALS;
+import static org.jclouds.oauth.v2.config.OAuthProperties.CREDENTIAL_TYPE;
+
+import java.io.IOException;
+import java.util.Properties;
+import java.util.Set;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.concurrent.config.ExecutorServiceModule;
+import org.jclouds.azurecompute.arm.AzureComputeApi;
+import org.jclouds.azurecompute.arm.AzureComputeProviderMetadata;
+import org.jclouds.json.Json;
+import org.jclouds.rest.ApiContext;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.Resources;
+import com.google.gson.JsonParser;
+import com.google.inject.Module;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import com.squareup.okhttp.mockwebserver.RecordedRequest;
+
+public class BaseAzureComputeApiMockTest {
+
+   private static final String MOCK_BEARER_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSIsImtpZCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSJ9";
+   private static final String DEFAULT_ENDPOINT = new AzureComputeProviderMetadata().getEndpoint();
+
+   private final Set<Module> modules = ImmutableSet.<Module> of(new ExecutorServiceModule(sameThreadExecutor()));
+
+   protected MockWebServer server;
+   protected AzureComputeApi api;
+   private Json json;
+
+   // So that we can ignore formatting.
+   private final JsonParser parser = new JsonParser();
+
+   @BeforeMethod
+   public void start() throws IOException {
+      server = new MockWebServer();
+      server.play();
+      Properties properties = new Properties();
+      properties.put(CREDENTIAL_TYPE, BEARER_TOKEN_CREDENTIALS.toString());
+      AzureComputeProviderMetadata pm = AzureComputeProviderMetadata.builder().build();
+      ApiContext<AzureComputeApi> ctx = ContextBuilder.newBuilder(pm)
+              .credentials("", MOCK_BEARER_TOKEN)
+              .endpoint(server.getUrl("/").toString() + "subscriptions/12345678-1234-1234-1234-123456789012")
+              .modules(modules)
+              .overrides(properties)
+              .build();
+      json = ctx.utils().injector().getInstance(Json.class);
+      api = ctx.getApi();
+   }
+
+   @AfterMethod(alwaysRun = true)
+   public void stop() throws IOException {
+      server.shutdown();
+      api.close();
+   }
+
+   protected String url(String path) {
+      return server.getUrl(path).toString();
+   }
+
+   protected MockResponse jsonResponse(String resource) {
+      return new MockResponse().addHeader("Content-Type", "application/json").setBody(stringFromResource(resource));
+   }
+
+   protected MockResponse response404() {
+      return new MockResponse().setStatus("HTTP/1.1 404 Not Found");
+   }
+
+   protected MockResponse response200() {
+      return new MockResponse().setStatus("HTTP/1.1 200 OK");
+   }
+
+   protected MockResponse response202() {
+      return new MockResponse().setStatus("HTTP/1.1 202 Accepted");
+   }
+
+   protected MockResponse response202WithHeader() {
+      return new MockResponse()
+              .setStatus("HTTP/1.1 202 Accepted")
+              .addHeader("Location", "https://management.azure.com/subscriptions/12345678-1234-1234-1234-123456789012/operationresults/eyJqb2JJZCI6IlJFU09VUkNFR1JPVVBERUxFVElPTkpPQi1SVEVTVC1DRU5UUkFMVVMiLCJqb2JMb2NhdGlvbiI6ImNlbnRyYWx1cyJ9?api-version=2014-04-01");
+   }
+
+   protected String stringFromResource(String resourceName) {
+      try {
+         return Resources.toString(getClass().getResource(resourceName), Charsets.UTF_8)
+                 .replace(DEFAULT_ENDPOINT, url(""));
+      } catch (IOException e) {
+         throw Throwables.propagate(e);
+      }
+   }
+
+   protected RecordedRequest assertSent(MockWebServer server, String method, String path) throws InterruptedException {
+      RecordedRequest request = server.takeRequest();
+      assertEquals(request.getMethod(), method);
+      assertEquals(request.getPath(), path);
+      assertEquals(request.getHeader("Accept"), "application/json");
+      assertEquals(request.getHeader("Authorization"), "Bearer " + MOCK_BEARER_TOKEN);
+      return request;
+   }
+
+   protected RecordedRequest assertSent(MockWebServer server, String method, String path, String json)
+           throws InterruptedException {
+      RecordedRequest request = assertSent(server, method, path);
+      assertEquals(request.getHeader("Content-Type"), "application/json");
+      assertEquals(parser.parse(new String(request.getBody(), Charsets.UTF_8)), parser.parse(json));
+      return request;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/a4334d46/azurecompute-arm/src/test/resources/resourcegroup.json
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/resources/resourcegroup.json b/azurecompute-arm/src/test/resources/resourcegroup.json
new file mode 100644
index 0000000..1d0cc68
--- /dev/null
+++ b/azurecompute-arm/src/test/resources/resourcegroup.json
@@ -0,0 +1,11 @@
+{
+  "id": "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/jcloudstest",
+  "name": "jcloudstest",
+  "location": "westus",
+  "tags": {
+    "tagname1": "tagvalue1"
+  },
+  "properties": {
+    "provisioningState": "Succeeded"
+  }
+}
\ No newline at end of file