Skip to content

Commit

Permalink
Merge pull request #112 from ali92hm/master
Browse files Browse the repository at this point in the history
Added instructions for programatic api access
  • Loading branch information
ali92hm committed Jan 11, 2019
2 parents a620075 + 4ed3df6 commit 3f817da
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
<#
.SYNOPSIS
This script creates a service principal that can be used to access Workbench's API programmatically
.DESCRIPTION
The script performs the following tasks:
- Creates a Service Principal and assigns it to Workbench
- Can assign the service principal to be an Admin on Workbench
.INPUTS
None. You cannot pipe objects to this script.
.OUTPUTS
The status of the update
.EXAMPLE
createServicePrincipal.ps1 -TenantName [blockchaindemos.onmicrosoft.com] -WorkbenchAppId [My Workbench AppId]
#>

param(
[Parameter(Mandatory=$true)][string]$TenantName,
[Parameter(Mandatory=$true)][string]$WorkbenchAppId,
[Parameter(Mandatory=$false)][string]$SpName="Service Principal for Workbench API",
[Parameter(Mandatory=$false)][switch]$MakeAdmin
)

function Log-Debug {
param(
[Parameter(Mandatory=$false)][string]$Message
)

if ($Message) {
Write-Verbose $Message
}
}

function Log-Info {
param(
[Parameter(Mandatory=$false)][string]$Message
)

if ($Message) {
Write-Host "INFO:", $Message
}
}

function Log-Success {
param(
[Parameter(Mandatory=$false)][string]$Message
)

if ($Message) {
Write-Host "SUCCESS:", $Message -foregroundcolor green
}
}

function Log-Warning {
param(
[Parameter(Mandatory=$false)][string]$Message,
[Parameter(Mandatory=$false)][string]$Exception
)

if ($Message) {
Write-Warning $Message
}

if ($Exception) {
Write-Host "ERROR MESSAGE:" $Exception -foregroundcolor yellow
}
}

function Log-Error {
param(
[Parameter(Mandatory=$false)][string]$Message,
[Parameter(Mandatory=$false)][string]$Exception,
[Parameter(Mandatory=$false)][switch]$Exit
)

if ($Message) {
Write-Host "ERROR:" $Message -foregroundcolor red
}

if ($Exception) {
Write-Host "ERROR MESSAGE:" $Exception -foregroundcolor red
}

if ($Exit) {
exit 1
}
}

#################################################################################
# Import Azure tools
#################################################################################
# For CloudShell
if (Get-Module -ListAvailable -Name "AzureAD.Standard.Preview") {
Log-Debug "Importing module AzureAD.Standard.Preview"
Import-Module "AzureAD.Standard.Preview"
# For Windows PowerShell
} elseif (Get-Module -ListAvailable -Name "AzureADPreview") {
Log-Debug "Importing module AzureADPreview"
Import-Module "AzureADPreview"
} elseif (Get-Module -ListAvailable -Name "AzureAD") {
Log-Debug "Importing module AzureAD"
Import-Module "AzureAD"
} else {
Log-Error "This script is not compatible with your computer, Please use Azure CloudShell https://shell.azure.com/powershell" -Exit
}

Log-Debug "Running script with params:"
Log-Debug "TenantName: $TenantName"
Log-Debug "AppId: $WorkbenchAppId"

try {
Log-Debug "Trying to login to $TenantName"
Log-Info "Logging in to AAD"
$currentUser = Connect-AzureAD -TenantId $TenantName -ErrorAction SilentlyContinue
Log-Debug $currentUser
} catch {
Log-Error "There was a problem with login. Please try again." -Exception $_ -Exit
}

#################################################################################
# Retrieving Workbench Application and Service Principal
#################################################################################

try {
$workbenchApplication = Get-AzureADApplication -Filter "AppId eq '$WorkbenchAppId'"
Log-Debug $workbenchApplication
} catch {
Log-Error "Could not find an application with Id $WorkbenchAppId in directory $TenantName" -Exception $_ -Exit
}

try {
$workbenchSP = Get-AzureADServicePrincipal -Filter "AppId eq '$WorkbenchAppId'"
Log-Debug $workbenchSP
} catch {
Log-Error "Could not find a service principal with Id $WorkbenchAppId in directory $TenantName" -Exception $_ -Exit
}

#################################################################################
# Creating the App Registration
#################################################################################

try {
Log-Info 'Creating the App registration'
$application = New-AzureADApplication `
-DisplayName $SpName `
-IdentifierUris "https://$TenantName/$([System.GUID]::NewGuid().ToString())" `
-PasswordCredentials $appSecret

$password = New-AzureADApplicationPasswordCredential `
-ObjectId $application.ObjectId `
-EndDate $(Get-Date).AddYears(2)
} catch {
# The user cannot create apps in their directory
if ($_.Exception.Message -like "*Authorization_RequestDenied*") {
Log-Error "You do not have sufficient privileges to create an application in this tenant. Please chose another tenant or have the tenant admin run this script. https://aka.ms/workbenchFAQ " -Exception $_ -Exit
} else {
Log-Error "Creating application failed. Please try again. https://aka.ms/workbenchFAQ" -Exception $_ -Exit
}
}

#################################################################################
# Assigning the User Application (Optional)
#################################################################################

Log-Info "Looking for your user '$($currentUser.Account.Id)' in '$TenantName' tenant"
try {
$matchedUsers = Get-AzureADUser -Filter "mail eq '$($currentUser.Account.Id)' or userPrincipalName eq '$($currentUser.Account.Id)' or OtherMails eq '$($currentUser.Account.Id)'"
Log-Debug $matchedUsers
} catch {
Log-Warning "Failed to retrieve your user information. Please refer to the docs to manually add yourself as an admin. https://aka.ms/workbenchAADSteps" -Exception $_
}


if ($matchedUsers -And $matchedUsers.length -gt 0) {
Log-Info "$($matchedUsers.length) user(s) were found with email '$($currentUser.Account.Id)'"
$user = $matchedUsers[0]
Log-Debug $user

try {
Log-Info "Assign the current logged in user to be the owner of the Application."
$ownerAssignment = Add-AzureADApplicationOwner `
-ObjectId $application.ObjectId `
-RefObjectId $user.ObjectId
Log-Debug "Successfully assigned the user as the application owner"
Log-Debug $ownerAssignment
} catch {
Log-Debug "Failed assigned the user as the application owner. The user is probably already an owner"
Log-Debug $_
}
}

#################################################################################
# Create Service Principal
#################################################################################
Log-Info 'Creating the Service Principal for Azure Blockchain Workbench '
try {
$sp = New-AzureADServicePrincipal -AppId $application.AppId
Log-Debug "Successfully created Service Principal"
Log-Debug $sp
} catch {
Log-Error 'Failed to create Service Principal. Please try again.' -Exception $_ -Exit
}

#################################################################################
# Assign Service Principal to the admin role
#################################################################################
if ($MakeAdmin) {

$adminAppRole = $null
# Looking for the App role with the Value of "Administrator"
ForEach ($appRole in $workbenchApplication.AppRoles) {
if ($appRole.Value -eq "Administrator") {
$adminAppRole= $appRole
break;
}
}

if (-Not $adminAppRole) {
Log-Error "Could not find the Admin app role for Application with AppId $WorkbenchAppId" -Exit
}

Log-Debug $adminAppRole

try {
$roleAssignment = New-AzureADServiceAppRoleAssignment `
-Id $adminAppRole.Id `
-ObjectId $sp.ObjectId `
-PrincipalId $sp.ObjectId `
-ResourceId $workbenchSP.ObjectId

Log-Info "Successfully created role assignment"
Log-Debug $roleAssignment
} catch {
Log-Error "Could not add service principal as an admin." -Exception $_ -Exit
}
}


Log-Debug $application

Write-Host
Write-Host

Log-Success "Successfully created Service Principal"
Log-Info "AppId: $($application.AppId)"
Log-Info "AppKey: $($password.Value)"
Write-Host "=============================================================================================="
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 67 additions & 0 deletions blockchain-workbench/scripts/workbench-serviceprincipal/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Creating a Service Principal to Access Workbench API


Overview
=================
Workbench uses OAuth 2.0 to secure its resources. The Auth provider for Workbench is [Azure Active Directory](https://docs.microsoft.com/en-us/azure/active-directory/develop/authentication-scenarios).

In order to use Workbench's API you need to set the `Authorization: Bearer <access_token>` in your HTTP header. This access token can be obtained in a number of different ways depending on the [authentication scenarios](https://docs.microsoft.com/en-us/azure/active-directory/develop/app-types).

If you are trying to consume Workbench's [API programmatically](https://docs.microsoft.com/en-us/azure/active-directory/develop/service-to-service), you need to use the [client credentials flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-oauth2-client-creds-grant-flow) using a [service principal](https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals). This document will provide you a script that will create a service principal for you in order to access Workbench programatically.


Access Control
=================
By default any service principal in Workbench's active directory has access to Workbench resources just like any user in that directory. However, just like any user, all service principals by default only have User level access and not Admin level access.

Admin level access is needed if you are trying to create Applications, read all applications, create users, and create role assignments.

> Note: Since service principals act like users in a directory they can be assigned to different Workbench "application roles". For example if you are trying to use a service principal to create contracts, you need to assign that service principal to the "creator" role of that application. Today this operation has to be done using the Workbench API as the UI does not allow for service principal role assignment.

It can be tricky to create a service principal and assign it to Workbench as an Admin, so we've provided an automated a script to create a service principal and you have the option to make that service principal an Admin.

Execution Instructions
=================
To run this script you need to have the [AzureAD](https://docs.microsoft.com/en-us/powershell/module/azuread/?view=azureadps-2.0) module installed. Since the AzureAD PowerShell module only works on Windows, we recommend using [Azure CloudShell](https://shell.azure.com/powershell) since it comes with with all dependencies installed.


1. Open [Azure CloudShell](https://shell.azure.com/powershell) and select the tenant tied to your Azure Subscription.

![CloudShell open](./media/cloudshell-open.png)

2. Download the script. You can download the upgrade script automatically by using the command bellow, or you can download it from this repository manually

```powershell
cd; Invoke-WebRequest -Uri https://aka.ms/createWorkbenchServicePrincipalScript -OutFile createWorkbenchServicePrincipal.ps1
```

![CloudShell download](./media/download-script.png)

3. Locate your Workbench AAD AppId and Tenant and run the following commands. The `-MakeAdmin` parameter is optional and is only needed if want your service principal to have Admin level access.


```powershell
./createWorkbenchServicePrincipal.ps1 -TenantName <workbench tenant> -WorkbenchAppId <Workbench AAD AppId> -MakeAdmin (optional)
```

![CloudShell final](./media/final.png)

4. You can now use this `AppId` and `Key` with any [ADAL](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-libraries) library to obtain an access token.

> Note: The target resource (audience) for accessing Workbench is the Workbench AppId, and the authority is https://login.microsoftonline.com/[tenantName]
Here is an [example](https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-oauth2-client-creds-grant-flow#service-to-service-access-token-request) of a raw http call.:

```
Method: POST
URL: https://login.microsoftonline.com/<tenant>/oauth2/token
Content-Type: application/x-www-form-urlencoded
Body:
grant_type=client_credentials
&client_id=<service_principal_appId>
&client_secret=<service_principal_key>
&resource=<workbench_appId>
```

0 comments on commit 3f817da

Please sign in to comment.