Skip to content

Commit

Permalink
Merge pull request Azure-Samples#19 from Azure-Samples/cleanup
Browse files Browse the repository at this point in the history
Cleanup
  • Loading branch information
jmprieur authored Apr 3, 2018
2 parents b07376f + b8f306d commit f3f80f3
Show file tree
Hide file tree
Showing 11 changed files with 499 additions and 47 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,4 @@ FakesAssemblies/
/.vs/NativeClient-MultiTarget-DotNet/v15/sqlite3
/.vs/config
/.vs/NativeClient-MultiTarget-DotNet/v15/Server/sqlite3
/DirSearchClient-Universal/project.lock.json
107 changes: 107 additions & 0 deletions AppCreationScripts/AppCreationScripts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Registering the Azure Active Directory applications and updating the configuration files for this sample using PowerShell scripts

## Goal of the scripts

### Presentation of the scripts

This sample comes with two PowerShell scripts, which automate the creation of the Azure Active Directory applications, and the configuration of the code for this sample. Once you run them, you will only need to build the solution and you are good to test.

These scripts are:

- `Configure.ps1` which:
- creates Azure AD applications and their related objects (permissions, dependencies, secrets),
- changes the configuration files in the C# and JavaScript projects.
- creates a summary file named `createdApps.html` in the folder from which you ran the script, and containing, for each Azure AD application it created:
- the identifier of the application
- the AppId of the application
- the url of its registration in the [Azure portal](https://portal.azure.com).

- `Cleanup.ps1` which cleans-up the Azure AD objects created by `Configure.ps1`. Note that this script does not revert the changes done in the configuration files, though. You will need to undo the change from source control (from Visual Studio, or from the command line using, for instance, git reset).

### Usage pattern for tests and DevOps scenarios

The `Configure.ps1` will stop if it tries to create an Azure AD application which already exists in the tenant. For this, if you are using the script to try/test the sample, or in DevOps scenarios, you might want to run `Cleanup.ps1` just before `Configure.ps1`. This is what is shown in the steps below.

## How to use the app creation scripts ?

### Pre-requisites

To use the app creation scripts:

1. Open PowerShell (On Windows, press `Windows-R` and type `PowerShell` in the search window)
2. Navigate to the root directory of the project.
3. Until you change it, the default Execution Policy for scripts is usually `Restricted`. In order to run the PowerShell script you need to set the Execution Policy to `Unrestricted`. You can set this just for the current PowerShell process by running the command:
```PowerShell
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Unrestricted
```
4. If you have never done it already, in the PowerShell window, install the AzureAD PowerShell modules. For this, type:
```PowerShell
Install-Module AzureAD
```
5. Go to the `AppCreationScripts` sub-folder. From the folder where you cloned the repo,
```PowerShell
cd AppCreationScripts
```
6. Run the scripts. See below for the [four options](#four-ways-to-run-the-script) to do that.
7. Open the Visual Studio solution, and in the solution's context menu, choose **Set Startup Projects**.
8. select **Start** for the projects
You're done. this just works!
### Four ways to run the script
We advise four ways of running the script:
- Interactive: you will be prompted for credentials, and the scripts decide in which tenant to create the objects,
- non-interactive: you will provide crendentials, and the scripts decide in which tenant to create the objects,
- Interactive in specific tenant: you will be prompted for credentials, and the scripts decide in which tenant to create the objects,
- non-interactive in specific tenant: you will provide crendentials, and the scripts decide in which tenant to create the objects.
Here are the details on how to do this.
#### Option 1 (interactive)
- Just run ``. .\Configue.ps1``, and you will be prompted to sign-in (email address, password, and if needed MFA).
- The script will be run as the signed-in user and will use the tenant in which the user is defined.
Note that the script will choose the tenant in which to create the applications, based on the user. Also to run the `Cleanup.ps1` script, you will need to re-sign-in.
#### Option 2 (non-interactive)
When you know the indentity and credentials of the user in the name of whom you want to create the applications, you can use the non-interactive approach. It's more adapted to DevOps. Here is an example of script you'd want to run in a PowerShell Window
```PowerShell
$secpasswd = ConvertTo-SecureString "[Password here]" -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ("[login@tenantName here]", $secpasswd)
. .\Cleanup.ps1 -Credential $mycreds
. .\Configure.ps1 -Credential $mycreds
```

Of course, in real life, you might already get the password as a `SecureString`. You might also want to get the password from KeyVault.

#### Option 3 (Interactive, but create apps in a specified tenant)

if you want to create the apps in a particular tenant, you can use the following option:
- open the [Azure portal](https://portal.azure.com)
- Select the Azure Active directory you are interested in (in the combo-box below your name on the top right of the browser window)
- Find the "Active Directory" object in this tenant
- Go to **Properties** and copy the content of the **Directory Id** property
- Then use the full syntax to run the scripts:

```PowerShell
$tenantId = "yourTenantIdGuid"
. .\Cleanup.ps1 -TenantId $tenantId
. .\Configure.ps1 -TenantId $tenantId
```

#### Option 4 (non-interactive, and create apps in a specified tenant)

This option combines option 2 and option 3: it creates the application in a specific tenant. See option 3 for the way to get the tenant Id. Then run:

```PowerShell
$secpasswd = ConvertTo-SecureString "[Password here]" -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ("[login@tenantName here]", $secpasswd)
$tenantId = "yourTenantIdGuid"
. .\Cleanup.ps1 -Credential $mycreds -TenantId $tenantId
. .\Configure.ps1 -Credential $mycreds -TenantId $tenantId
```
62 changes: 62 additions & 0 deletions AppCreationScripts/Cleanup.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
param([Parameter(Mandatory=$false)][PSCredential]$Credential=$null, [Parameter(Mandatory=$false)][string]$TenantId)
Import-Module AzureAD
$ErrorActionPreference = 'Stop'

Function Cleanup
{
<#
.Description
This function removes the Azure AD applications for the sample. These applications were created by the Configure.ps1 script
#>
[CmdletBinding()]
param(
[Parameter(HelpMessage='Tenant ID (This is a GUID which represents the "Directory ID" of the AzureAD tenant into which you want to create the apps')]
[PSCredential] $Credential,
[string] $tenantId
)

process
{
# $tenantId is the Active Directory Tenant. This is a GUID which represents the "Directory ID" of the AzureAD tenant
# into which you want to create the apps. Look it up in the Azure portal in the "Properties" of the Azure AD.

# Login to Azure PowerShell (interactive if credentials are not already provided:
# you'll need to sign-in with creds enabling your to create apps in the tenant)
if (!$Credential -and $TenantId)
{
$creds = Connect-AzureAD -TenantId $tenantId
}
else
{
if (!$TenantId)
{
$creds = Connect-AzureAD -Credential $Credential
}
else
{
$creds = Connect-AzureAD -TenantId $tenantId -Credential $Credential
}
}

if (!$tenantId)
{
$tenantId = $creds.Tenant.Id
}
$tenant = Get-AzureADTenantDetail
$tenantName = ($tenant.VerifiedDomains | Where { $_._Default -eq $True }).Name

# Removes the applications
Write-Host "Cleaning-up applications from tenant '$tenantName'"

Write-Host "Removing 'client' (MyDirectorySearcherApp) if needed"
$app=Get-AzureADApplication -Filter "DisplayName eq 'MyDirectorySearcherApp'"
if ($app)
{
Remove-AzureADApplication -ObjectId $app.ObjectId
Write-Host "Removed."
}

}
}

Cleanup -Credential $Credential -tenantId $TenantId
184 changes: 184 additions & 0 deletions AppCreationScripts/Configure.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
<#
This script creates the Azure AD applications needed for this sample and updates the configuration files
for the visual Studio projects from the data in the Azure AD applications.
Before running this script you need to install the AzureAD cmdlets as an administrator.
For this:
1) Run Powershell as an administrator
2) in the PowerShell window, type: Install-Module AzureAD
There are four ways to run this script. For more information, read the AppCreationScripts.md file in the same folder as this script.
#>

# Adds the requiredAccesses (expressed as a pipe separated string) to the requiredAccess structure
# The exposed permissions are in the $exposedPermissions collection, and the type of permission (Scope | Role) is
# described in $permissionType
Function AddResourcePermission($requiredAccess, `
$exposedPermissions, [string]$requiredAccesses, [string]$permissionType)
{
foreach($permission in $requiredAccesses.Trim().Split("|"))
{
foreach($exposedPermission in $exposedPermissions)
{
if ($exposedPermission.Value -eq $permission)
{
$resourceAccess = New-Object Microsoft.Open.AzureAD.Model.ResourceAccess
$resourceAccess.Type = $permissionType # Scope = Delegated permissions | Role = Application permissions
$resourceAccess.Id = $exposedPermission.Id # Read directory data
$requiredAccess.ResourceAccess.Add($resourceAccess)
}
}
}
}

#
# Exemple: GetRequiredPermissions "Microsoft Graph" "Graph.Read|User.Read"
# See also: http://stackoverflow.com/questions/42164581/how-to-configure-a-new-azure-ad-application-through-powershell
Function GetRequiredPermissions([string] $applicationDisplayName, [string] $requiredDelegatedPermissions, [string]$requiredApplicationPermissions, $servicePrincipal)
{
# If we are passed the service principal we use it directly, otherwise we find it from the display name (which might not be unique)
if ($servicePrincipal)
{
$sp = $servicePrincipal
}
else
{
$sp = Get-AzureADServicePrincipal -Filter "DisplayName eq '$applicationDisplayName'"
}
$appid = $sp.AppId
$requiredAccess = New-Object Microsoft.Open.AzureAD.Model.RequiredResourceAccess
$requiredAccess.ResourceAppId = $appid
$requiredAccess.ResourceAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.ResourceAccess]

# $sp.Oauth2Permissions | Select Id,AdminConsentDisplayName,Value: To see the list of all the Delegated permissions for the application:
if ($requiredDelegatedPermissions)
{
AddResourcePermission $requiredAccess -exposedPermissions $sp.Oauth2Permissions -requiredAccesses $requiredDelegatedPermissions -permissionType "Scope"
}

# $sp.AppRoles | Select Id,AdminConsentDisplayName,Value: To see the list of all the Application permissions for the application
if ($requiredApplicationPermissions)
{
AddResourcePermission $requiredAccess -exposedPermissions $sp.AppRoles -requiredAccesses $requiredApplicationPermissions -permissionType "Role"
}
return $requiredAccess
}


Function UpdateLine([string] $line, [string] $value)
{
$index = $line.IndexOf(':')
if ($index -eq -1)
{
$index = $line.IndexOf('=')
}
if ($index -ige 0)
{
$line = $line.Substring(0, $index+1) + " """+$value + ""","
}
return $line
}

Function UpdateTextFile([string] $configFilePath, [System.Collections.HashTable] $dictionary)
{
$lines = Get-Content $configFilePath
$index = 0
while($index -lt $lines.Length)
{
$line = $lines[$index]
foreach($key in $dictionary.Keys)
{
if ($line.Contains($key))
{
$lines[$index] = UpdateLine $line $dictionary[$key]
}
}
$index++
}

Set-Content -Path $configFilePath -Value $lines -Force
}

Set-Content -Value "<html><body><table>" -Path createdApps.html
Add-Content -Value "<thead><tr><th>Application</th><th>AppId</th><th>Url in the Azure portal</th></tr></thead><tbody>" -Path createdApps.html

Function ConfigureApplications
{
<#.Description
This function creates the Azure AD applications for the sample in the provided Azure AD tenant and updates the
configuration files in the client and service project of the visual studio solution (App.Config and Web.Config)
so that they are consistent with the Applications parameters
#>
[CmdletBinding()]
param(
[PSCredential] $Credential,
[Parameter(HelpMessage='Tenant ID (This is a GUID which represents the "Directory ID" of the AzureAD tenant into which you want to create the apps')]
[string] $tenantId
)

process
{
# $tenantId is the Active Directory Tenant. This is a GUID which represents the "Directory ID" of the AzureAD tenant
# into which you want to create the apps. Look it up in the Azure portal in the "Properties" of the Azure AD.

# Login to Azure PowerShell (interactive if credentials are not already provided:
# you'll need to sign-in with creds enabling your to create apps in the tenant)
if (!$Credential -and $TenantId)
{
$creds = Connect-AzureAD -TenantId $tenantId
}
else
{
if (!$TenantId)
{
$creds = Connect-AzureAD -Credential $Credential
}
else
{
$creds = Connect-AzureAD -TenantId $tenantId -Credential $Credential
}
}

if (!$tenantId)
{
$tenantId = $creds.Tenant.Id
}
$tenant = Get-AzureADTenantDetail
$tenantName = ($tenant.VerifiedDomains | Where { $_._Default -eq $True }).Name

# Create the client AAD application
Write-Host "Creating the AAD appplication (MyDirectorySearcherApp)"
$clientAadApplication = New-AzureADApplication -DisplayName "MyDirectorySearcherApp" `
-ReplyUrls "https://MyDirectorySearcherApp" `
-AvailableToOtherTenants $True `
-PublicClient $True
$currentAppId = $clientAadApplication.AppId
$clientServicePrincipal = New-AzureADServicePrincipal -AppId $currentAppId -Tags {WindowsAzureActiveDirectoryIntegratedApp}
Write-Host "Done."

# URL of the AAD application in the Azure portal
$clientPortalUrl = "https://portal.azure.com/#@"+$tenantName+"/blade/Microsoft_AAD_IAM/ApplicationBlade/appId/"+$clientAadApplication.AppId+"/objectId/"+$clientAadApplication.ObjectId
Add-Content -Value "<tr><td>client</td><td>$currentAppId</td><td><a href='$clientPortalUrl'>MyDirectorySearcherApp</a></td></tr>" -Path createdApps.html

# Add Required Resources Access (from 'client' to 'Microsoft Graph')
Write-Host "Getting access from 'client' to 'Microsoft Graph'"
$requiredResourcesAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.RequiredResourceAccess]
$requiredPermissions = GetRequiredPermissions -applicationDisplayName "Microsoft Graph" `
-requiredDelegatedPermissions "User.Read|User.ReadBasic.All";
$requiredResourcesAccess.Add($requiredPermissions)
Set-AzureADApplication -ObjectId $clientAadApplication.ObjectId -RequiredResourceAccess $requiredResourcesAccess
Write-Host "Granted."

# Update config file for 'client'
$configFile = $pwd.Path + "\..\DirectorySearcherLib\DirectorySearcher.cs"
Write-Host "Updating the sample code ($configFile)"
$dictionary = @{ "public static string clientId" = $clientAadApplication.AppId };
UpdateTextFile -configFilePath $configFile -dictionary $dictionary
Add-Content -Value "</tbody></table></body></html>" -Path createdApps.html

}
}


# Run interactively (will ask you for the tenant ID)
ConfigureApplications -Credential $Credential -tenantId $TenantId
Loading

0 comments on commit f3f80f3

Please sign in to comment.