Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds documentation and sample app for msal go #22

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This repository contains samples that show how to use [Azure Managed Identity](h
| Sample | Platform | Build status | Description |
|:-------|:---------|:-------------|:------------|
| [`ms-activedirectory-managedidentity`](https://github.com/Azure-Samples/msal-managed-identity/tree/main/src/dotnet) | .NET | [![.NET Build](https://github.com/Azure-Samples/msal-managed-identity/actions/workflows/ms-activedirectory-managedidentity-build.yml/badge.svg)](https://github.com/Azure-Samples/msal-managed-identity/actions/workflows/ms-activedirectory-managedidentity-build.yml) | This sample showcases how to acquire a secret from an Azure Key Vault using the Microsoft identity platform. It shows you how to use the [managed identity for app service](https://learn.microsoft.com/azure/app-service/overview-managed-identity) and acquire a token for an Azure Key Vault resource. |
| [`msal-go-managedidentity`](https://github.com/Azure-Samples/msal-managed-identity/tree/main/src/go) | GO | [![GO Build](https://github.com/Azure-Samples/msal-managed-identity/actions/workflows/ms-activedirectory-managedidentity-build.yml/badge.svg)](https://github.com/Azure-Samples/msal-managed-identity/actions/workflows/ms-activedirectory-managedidentity-build.yml) | This sample showcases how to acquire a secret from an Azure Key Vault using the Microsoft identity platform for MSAL GO. It shows you how to use the [managed identity for app service](https://learn.microsoft.com/azure/app-service/overview-managed-identity) and acquire a token for an Azure Key Vault resource. |

## Additional resources

Expand Down
Binary file added src/go/images/call-kv.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/go/images/identity.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/go/images/menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/go/images/rbac.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/go/images/sami.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
234 changes: 234 additions & 0 deletions src/go/images/web-app-access-graph.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
217 changes: 217 additions & 0 deletions src/go/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
---
services: active-directory
platforms: go
author: andrewohart
level: 400
client: Azure functions
service: Azure Key Vault
endpoint: Microsoft identity platform
page_type: sample
languages:
- go
products:
- azure
- azure-active-directory
- go
- managed identity
- key vault
description: "This sample showcases how to develop an Azure function that gets a secret from a key Vault using Managed Identities."
---
# GO - Acquire a secret from an Azure Key Vault using Azure Managed Identity

## About this sample

### Overview

This sample showcases how to acquire a secret from an Azure Key Vault using Azure Managed Identity. It shows you how to use the [Azure Functions](https://learn.microsoft.com/azure/azure-functions/functions-overview?pivots=programming-language-java) and acquire a token for an Azure Key Vault resource.
AndyOHart marked this conversation as resolved.
Show resolved Hide resolved

The sample shows how to use [MSAL (Microsoft Authentication Library) for GO](https://github.com/AzureAD/microsoft-authentication-library-for-go) to obtain an access token for [Azure Key Vault](https://vault.azure.net). Specifically, the sample shows how to retrieve the secret value from a vault.

Finally, the sample also demonstrates how to use the different [types of managed identities](https://learn.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview#managed-identity-types) to get an access token.

For more information about how the protocols work in this scenario and other scenarios, see [Authentication Scenarios for Azure AD](http://go.microsoft.com/fwlink/?LinkId=394414).

For more information about Managed Identity, please visit the [Managed Identities for Azure Resources homepage](https://learn.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview).

## Topology

<img alt="Overview" src="./images/call-kv.png" />

### Scenario

You want to access an Azure Key Vault secret from a function deployed in Azure. And, you don't want to worry about managing secrets or app credentials.

## How To Run This Sample

To run this sample, you'll need:

- An IDE of your choice, for example [Visual Studio Code](https://code.visualstudio.com/download)
- An Internet connection
- An Azure account to create, deploy, and manage applications. If you do not have an Azure Account, follow the [instructions](https://azure.microsoft.com/free/) to get a free account.

### Step 1: Clone or download this repository

From your shell or command line:

```Shell
git clone https://github.com/Azure-Samples/msal-managed-identity.git
```

or download and extract the repository `.ZIP` file.

The GO sample is located in the [`/src/go/devapps/managedidentity`](https://github.com/AzureAD/microsoft-authentication-library-for-go/blob/c5febcbae287a26a0cfedd45f4edeaf3c41ad7dc/apps/tests/devapps/managedidentity/managedidentity_sample.go) folder.

### Step 2: Modify the Key Vault URI and Secret name values in the code

Following are the changes you need to make:

- In the [`handler.go`](https://github.com/Azure-Samples/msal-managed-identity/blob/main/src/go/sample/AcquireTokenMSI.go) file under the getSecretFromAzureVault method modify the following values,

```go
keyVaultUri := "your-key-vault-uri"
secretName := "your-secret-name"
```

- Change these to match your key vault uri and secret name. These can be found in the following locations:

1. Key Vault URI - In your Azure home page, go to your key vault, on the Overview page our key vault URI can be found under **'Essentials'**
1. Secret Name - On the Key Vault Overview page, go to the Panel on the left and expand the **'Objects'** dropdown
Click into **'Secrets'**
Click into the secret you want to use
Click ont the version you would like to use
Copy the part after the key vault URI and use that as your secret name

## Publish your function
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the app service is not implemented, will it be relevant to have a sample for the functions? Can we have a sample for VM instead since that is implemented?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I marked this as a draft PR so its ready to go when App Service is implemented. The other docs in this sample project are using AppService so I think we want to keep this the same, and then the other PR that is open is more about using the library


[Deploy your function](https://learn.microsoft.com/en-gb/azure/azure-functions/create-first-function-vs-code-other?tabs=go%2Cmacos) using an IDE of your choice, for example Visual Studio Code

## After you deploy the sample to Azure

There are few important settings you need to change for this sample to work:

### Enable managed identity on the function

- After you publish the function to Azure, go to your resource in the [Azure Portal](https://portal.azure.com/)
- Select the `Identity` blade of the function
- [Enable the System Assigned managed identity](https://learn.microsoft.com/azure/azure-functions/functions-identity-access-azure-sql-with-managed-identity#enable-system-assigned-managed-identity-on-azure-function) of the resource.

### Assign Azure roles using the Azure portal

Azure role-based access control (Azure RBAC) is the authorization system you use to manage access to Azure resources. To grant access, you assign roles to users, groups, service principals, or managed identities at a particular scope. This [article](https://learn.microsoft.com/azure/role-based-access-control/role-assignments-portal) describes how to assign roles using the Azure portal.

You will need to authorize the managed identity resource to access the vault.

<img alt="RBAC" src="./images/rbac.png" />

## Launch the function

To launch the function you can use the following:

1. {your host}/api/AcquireTokenMsi - to acquire a token for system assigned managed identity
2. {your host}/api/AcquireTokenMsi?userAssignedClientId=<client id of the user assigned managed identity> - to acquire a token for a user assigned managed identity.
3. {your host}/api/AcquireTokenMsi?userAssignedResourceId=<resource id of the user assigned managed identity> - to acquire a token for a user assigned managed identity.

> **Note**
> Did the sample not work for you as expected? Did you encounter issues trying this sample? Then please reach out to us using the [GitHub Issues](https://github.com/Azure-Samples/msal-managed-identity/issues) page.

## About the code

Here there's a quick guide to the most interesting authentication-related bits of the sample.

### Acquiring the managed identity token

MSAL GO supports acquiring tokens through the managed identity capability when used with applications running inside Azure infrastructure. You can read more about MSAL GO support for managed identities in the [official documentation](https://learn.microsoft.com/entra/msal/go/advanced/managed-identity).

### Using the access tokens in the app

The `AcquireToken` function in the `managedidentity.go` class demonstrates how to take advantage of the Managed Identity Client for calling Microsoft Key Vault without having to worry about secrets or certificates.

Here is the relevant code:

```go
// Get the access token using MSAL, or an error if there was one
accessToken, err := miClient.AcquireToken(context.Background(), "https://vault.azure.net")
if err != nil {
log.Fatalf("failed to acquire token: %v", err)
return
}

// Create an HttpClient and set the Authorization Header
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatalf("Error creating request: %v", err)
}

req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken.AccessToken))

// Create the URL and send the request, then read and save the response body, handling errors along the way
url := fmt.Sprintf("%ssecrets/%s?api-version=7.2", keyVaultUri, secretName)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Fatalf("Error sending request: %v", err)
}
defer resp.Body.Close()

// Combine all received buffer streams into one buffer, and then into a string
var parsedData map[string]interface{}
if err := json.Unmarshal(body, &parsedData); err != nil {
log.Fatalf("Error parsing JSON: %v", err)
}

// Print the response body which contains the secret value
println(fmt.Sprintf("The secret, %s, has a value of: %s", secretName, string(body)))
```

- First, you need to acquire an access token.
- Once you have the access token, create an HTTP client to call the Key Vault APIs.
- Add the access token as an authorization header to the HTTP client. Set the Authorization header to Bearer followed by the access token.
- Once you have set up the authorization header, you can make calls to Key Vault APIs.

The terminal will then show you the secret response that contains the value of the secret

## Common Errors

Following are the most common errors you would see if any step was missed during setup:

### An attempt was made to access a socket in a way forbidden by its access permissions. (169.254.169.254:80)

This error indicates that the managed identity endpoint is not reachable. Please refer to the Azure Web App documentation on [how to turn on Managed Identity](https://learn.microsoft.com/azure/azure-app-configuration/howto-integrate-azure-managed-service-identity?pivots=framework-dotnet&tabs=core5x#add-a-managed-identity).

> Causes: Managed identity is not enabled for the Azure Resource.

### Access Denied errors

```json
{
"error": {
"code": "Forbidden",
"message": "The user, group or application 'appid=xyz;oid=xyz;iss=https://sts.windows.net/xyz/' does not have secrets get permission on key vault '<key vault name>;location=xyz'. For help resolving this issue, please see https://go.microsoft.com/fwlink/?linkid=2125287",
"innererror": {
"code": "AccessDenied"
}
}
}
```

This error indicates that the managed identity service principal was not granted access to the key vault. Please refer to ["Assign a Key Vault access policy"](https://learn.microsoft.com/azure/key-vault/general/assign-access-policy?tabs=azure-portal) for more information.

> Causes: Managed identity resource was not granted access to the Key Vault

## Community Help and Support

Use [Stack Overflow](http://stackoverflow.com/questions/tagged/azure-ad-msal) to get support from the community. Ask your questions on Stack Overflow first and browse existing issues to see if someone has asked your question before.

Make sure that your questions or comments are tagged with `azure-ad-msal`, `go`, and `microsoft-graph`.

If you find a bug in the sample, please raise the issue on [GitHub Issues](/issues).

## Contributing

If you'd like to contribute to this sample, see [our contribution guidelines](https://github.com/Azure-Samples/msal-managed-identity/blob/main/CONTRIBUTING.md).

This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [[email protected]](mailto:[email protected]) with any additional questions or comments.

## More information

For more information, refer to the [MSAL GO documentation](https://learn.microsoft.com/en-us/entra/msal/go/).
16 changes: 16 additions & 0 deletions src/go/sample/AcquireTokenMSI/function.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"bindings": [
{
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": ["get", "post"],
"authLevel": "anonymous"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}
11 changes: 11 additions & 0 deletions src/go/sample/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module your-module-name

go 1.22.5

require github.com/AzureAD/microsoft-authentication-library-for-go v1.2.3-0.20240925080015-6b9cd68c0fb8

require (
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
)
76 changes: 76 additions & 0 deletions src/go/sample/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package main

import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"

mi "github.com/AzureAD/microsoft-authentication-library-for-go/apps/managedidentity"
)

func getSecretFromAzureVault(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "going to try get secret from managed identity")

keyVaultUri := "your-key-vault-uri"
secretName := "your-secret-name"

// Uncomment one of the following lines to create a new managed identity client for UserAssigned. Will need to create UserAssigned managed identity on Azure
miClient, err := mi.New(mi.SystemAssigned())
// miClient, err := mi.New(mi.UserAssignedClientID("my-client-id"))
// miClient, err := mi.New(mi.UserAssignedObjectID("my-object-id"))
// miClient, err := mi.New(mi.UserAssignedResourceID("my-resource-id"))
if err != nil {
fmt.Fprintf(w, "failed to create a new managed identity client: %v", err)
return
}

accessToken, err := miClient.AcquireToken(context.Background(), "https://vault.azure.net")
if err != nil {
fmt.Fprintf(w, "failed to acquire token: %v", err)
return
}

fmt.Fprintf(w, "Access token: %s", accessToken.AccessToken)

// Create http request using access token
url := fmt.Sprintf("%ssecrets/%s?api-version=7.2", keyVaultUri, secretName)

// Create a new HTTP request
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Fprintf(w, "Error creating request: %v", err)
}

// Set the authorization header
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken.AccessToken))

// Send the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Fprintf(w, "Error sending request: %v", err)
}
defer resp.Body.Close()

// Read the response body
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Fprintf(w, "Error reading response body: %v", err)
}

// Combine all received buffer streams into one buffer, and then into a string
var parsedData map[string]interface{}
if err := json.Unmarshal(body, &parsedData); err != nil {
fmt.Fprintf(w, "Error parsing JSON: %v", err)
}

// Print the response body
fmt.Fprintf(w, "The secret, %s, has a value of: %s", secretName, string(body))
}

func main() {
http.HandleFunc("/api/AcquireTokenMSI", getSecretFromAzureVault)
http.ListenAndServe(":8080", nil)
}
23 changes: 23 additions & 0 deletions src/go/sample/host.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
},
"customHandler": {
"description": {
"defaultExecutablePath": "handler",
"workingDirectory": "",
"arguments": []
},
"enableForwardingHttpRequest": true
}
}