diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AzureMsiAuthentication.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AzureMsiAuthentication.java
new file mode 100644
index 00000000..93e343cf
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AzureMsiAuthentication.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2018 the original author or authors.
+ *
+ * Licensed 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.springframework.vault.authentication;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+import org.springframework.vault.VaultException;
+import org.springframework.vault.authentication.AuthenticationSteps.HttpRequestBuilder;
+import org.springframework.vault.authentication.AuthenticationSteps.Node;
+import org.springframework.vault.support.VaultResponse;
+import org.springframework.vault.support.VaultToken;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.client.RestOperations;
+
+/**
+ * Azure MSI (Managed Service Identity) authentication using Azure as trusted third party.
+ *
+ * Azure MSI authentication uses {@link AzureVmEnvironment} and the MSI OAuth2 token
+ * (referenced as JWT token in Vault docs) to log into Vault. VM environment and OAuth2
+ * token are fetched from the Azure Instance Metadata service. Instances of this class are
+ * immutable once constructed.
+ *
+ * @author Mark Paluch
+ * @since 2.1
+ * @see AzureMsiAuthenticationOptions
+ * @see RestOperations
+ * @see Auth Backend: azure
+ * @link Azure Instance Metadata service
+ */
+public class AzureMsiAuthentication implements ClientAuthentication {
+
+ private static final Log logger = LogFactory.getLog(AzureMsiAuthentication.class);
+
+ private static final HttpEntity METADATA_HEADERS;
+
+ static {
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("Metadata", "true");
+ METADATA_HEADERS = new HttpEntity<>(headers);
+ }
+
+ private final AzureMsiAuthenticationOptions options;
+
+ private final RestOperations vaultRestOperations;
+
+ private final RestOperations azureMetadataRestOperations;
+
+ /**
+ * Create a new {@link AzureMsiAuthentication}.
+ *
+ * @param options must not be {@literal null}.
+ * @param restOperations must not be {@literal null}.
+ */
+ public AzureMsiAuthentication(AzureMsiAuthenticationOptions options,
+ RestOperations restOperations) {
+ this(options, restOperations, restOperations);
+ }
+
+ /**
+ * Create a new {@link AzureMsiAuthentication} specifying
+ * {@link AzureMsiAuthenticationOptions}, a Vault and an Azure-Metadata-specific
+ * {@link RestOperations}.
+ *
+ * @param options must not be {@literal null}.
+ * @param vaultRestOperations must not be {@literal null}.
+ * @param azureMetadataRestOperations must not be {@literal null}.
+ */
+ public AzureMsiAuthentication(AzureMsiAuthenticationOptions options,
+ RestOperations vaultRestOperations, RestOperations azureMetadataRestOperations) {
+
+ Assert.notNull(options, "AzureAuthenticationOptions must not be null");
+ Assert.notNull(vaultRestOperations, "Vault RestOperations must not be null");
+ Assert.notNull(azureMetadataRestOperations,
+ "Azure Instance Metadata RestOperations must not be null");
+
+ this.options = options;
+ this.vaultRestOperations = vaultRestOperations;
+ this.azureMetadataRestOperations = azureMetadataRestOperations;
+ }
+
+ /**
+ * Creates a {@link AuthenticationSteps} for Azure authentication given
+ * {@link AzureMsiAuthenticationOptions}.
+ *
+ * @param options must not be {@literal null}.
+ * @return {@link AuthenticationSteps} for Azure authentication.
+ */
+ public static AuthenticationSteps createAuthenticationSteps(
+ AzureMsiAuthenticationOptions options) {
+
+ Assert.notNull(options, "AzureMsiAuthenticationOptions must not be null");
+
+ return createAuthenticationSteps(options, options.getVmEnvironment());
+ }
+
+ protected static AuthenticationSteps createAuthenticationSteps(
+ AzureMsiAuthenticationOptions options,
+ @Nullable AzureVmEnvironment environment) {
+
+ Node msiToken = AuthenticationSteps.fromHttpRequest(
+ HttpRequestBuilder.get(options.getIdentityTokenServiceUri())
+ .with(METADATA_HEADERS).as(Map.class)) //
+ .map(token -> (String) token.get("access_token"));
+
+ Node environmentSteps;
+
+ if (environment == null) {
+
+ environmentSteps = AuthenticationSteps.fromHttpRequest(
+ HttpRequestBuilder.get(options.getInstanceMetadataServiceUri())
+ .with(METADATA_HEADERS).as(Map.class)) //
+ .map(AzureMsiAuthentication::toAzureVmEnvironment);
+ }
+ else {
+ environmentSteps = AuthenticationSteps.fromSupplier(() -> environment);
+ }
+
+ return environmentSteps
+ .zipWith(msiToken)
+ .map(tuple -> getAzureLogin(options.getRole(), tuple.getLeft(),
+ tuple.getRight())) //
+ .login("auth/{mount}/login", options.getPath());
+ }
+
+ @Override
+ public VaultToken login() throws VaultException {
+ return createTokenUsingAzureMsiCompute();
+ }
+
+ @SuppressWarnings("unchecked")
+ private VaultToken createTokenUsingAzureMsiCompute() {
+
+ Map login = getAzureLogin(options.getRole(), getVmEnvironment(),
+ getAccessToken());
+
+ try {
+
+ VaultResponse response = this.vaultRestOperations.postForObject(
+ "auth/{mount}/login", login, VaultResponse.class, options.getPath());
+
+ Assert.state(response != null && response.getAuth() != null,
+ "Auth field must not be null");
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Login successful using Azure authentication");
+ }
+
+ return LoginTokenUtil.from(response.getAuth());
+ }
+ catch (RestClientException e) {
+ throw VaultLoginException.create("Azure", e);
+ }
+ }
+
+ private static Map getAzureLogin(String role,
+ AzureVmEnvironment vmEnvironment, String jwt) {
+
+ Map loginBody = new LinkedHashMap<>();
+ loginBody.put("resource_group_name", vmEnvironment.getResourceGroupName());
+ loginBody.put("vm_name", vmEnvironment.getVmName());
+ loginBody.put("subscription_id", vmEnvironment.getSubscriptionId());
+ loginBody.put("jwt", jwt);
+ loginBody.put("role", role);
+
+ return loginBody;
+ }
+
+ @SuppressWarnings("unchecked")
+ private String getAccessToken() {
+
+ ResponseEntity