Home Leveraging your CIPP-SAM app
Post
Cancel

Leveraging your CIPP-SAM app

Maybe you’ve recently deployed CIPP, maybe you’ve been working with it for a while. Either way its good to understand that the core of CIPP is the CIPP-SAM app, this app gets created in your tenant the first time you run through the SAM Wizard.
During the SAM Wizard CIPP will save several details of the app to a Keyvault resource in Azure, details which will be used to authenticate to your customer tenants.
The neat thing is that nothing is stopping you from using those details yourself for your own scripting!

Desktop View Unlock the door to endless possibilities!

Getting access to Keyvault

First you’ll need to find the keyvault resource CIPP created when it was deployed.
If you were not the person who deployed CIPP you may need to request someone to else to do these actions or to grant you access to the subscription and/or resource group CIPP was deployed in.
The resource will be named something along the line of “cipp".

Desktop View

First go to Access Control (IAM). Here you can give yourself or others role based permissions.
In our case we will only need to read secrets so “Key Vault Secrets User” will be perfect.

Second, go to Access policies and create a new Access policy for yourself.
You will only need to add the “GET” and “LIST” secret permissions.

Once you’ve added the permissions you should be able to see the secrets in the Azure UI and request them using the Az.Keyvault module.

Retrieving the secrets from Keyvault

Retrieving the secrets from Keyvault is very simple, you just need to know 3 things.

  • The Keyvault name
  • The Secret name
  • The command to use!

The following is as basic as it gets,

1
$retrievedSecret = Get-AzKeyVaultSecret -VaultName 'mykeyvaultname' -Name 'secretname' -AsPlainText -ErrorAction Stop

If you need to request multiple secrets and save them to variables, you can for example use this little snippet.
It will retrieve them and store them in variables that are named the same as the secrets themselves.

1
2
3
@('ApplicationId','ApplicationSecret','RefreshToken', 'tenantid') | Foreach-Object {
    New-Variable -Name $_ -Value (Get-AzKeyVaultSecret -VaultName $keyVaultName -Name $_ -AsPlainText -ErrorAction Stop) -Force
}

Using the secrets to authenticate

For demonstration purposes I will be using my own function here, but there are an endless number of ways to Rome.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
function Get-MicrosoftToken {
    Param(
        # Tenant Id
        [Parameter(Mandatory=$false)]
        [guid]$TenantId,

        # Scope
        [Parameter(Mandatory=$false)]
        [string]$Scope = 'https://graph.microsoft.com/.default',

        # ApplicationID
        [Parameter(Mandatory=$true)]
        [guid]$ApplicationID,

        # ApplicationSecret
        [Parameter(Mandatory=$true)]
        [string]$ApplicationSecret,

        # RefreshToken
        [Parameter(Mandatory=$true)]
        [string]$RefreshToken
    )

    if ($TenantId) {
        $Uri = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token"
    }
    else {
        $Uri = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
    }

    # Define the parameters for the token request
    $Body = @{
        client_id       = $ApplicationID
        client_secret   = $ApplicationSecret
        scope           = $Scope
        refresh_token   = $RefreshToken
        grant_type      = 'refresh_token'
    }

    $Params = @{
        Uri = $Uri
        Method = 'POST'
        Body = $Body
        ContentType = 'application/x-www-form-urlencoded'
        UseBasicParsing = $true
    }

    try {
        $AuthResponse = (Invoke-WebRequest @Params).Content | ConvertFrom-Json
    } catch {
        throw "Authentication Error Occured $_"
    }

    return $AuthResponse
}

try {
    $customertenantId = 'customertenantidhere'
    $keyVaultName = 'yourkeyvaultnamehere'

    # Our secrets from the CIPP Keyvault
    @('ApplicationId','ApplicationSecret','RefreshToken', 'tenantid') | Foreach-Object {
        New-Variable -Name $_ -Value (Get-AzKeyVaultSecret -VaultName $keyVaultName -Name $_ -AsPlainText -ErrorAction Stop) -Force
    }

    # To keep the code clean we'll define some of the secrets in a splat
    $commonTokenSplat = @{
        ApplicationID = $ApplicationId
        ApplicationSecret = $ApplicationSecret
        RefreshToken = $RefreshToken
    }

    # Here we're requesting a Graph token for $customertenantId
    if ($token = (Get-MicrosoftToken @commonTokenSplat -TenantID $customertenantid -Scope "https://graph.microsoft.com/.default").Access_Token) {
        $header = @{
            Authorization = 'bearer {0}' -f $token
            Accept        = "application/json"
        }
    }
} catch {
    throw "Auth failed: $($_.Exception.Message)"
}

$header should now contain a valid graph token for $tenant and alllow you to make a call like

1
Invoke-RestMethod -Method GET -Uri 'https://graph.microsoft.com/beta/organization' -Headers $header

We are of course not limited to Graph API, you can also use the secrets and the CIPP-SAM app to request tokens for Exchang Online, Sharepoint, Teams, Partner Center and other Microsoft services.

Needless to say, these secrets should be treated like nuclear secrets. They will give unattended access to all of your customer tenants, if a malicious actor manages to get a hold of them your company is likely bankrupt.

Happy scripting!

This post is licensed under CC BY 4.0 by the author.