Skip to content

Instantly share code, notes, and snippets.

@joerodgers
Last active April 23, 2024 09:44
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save joerodgers/a295df1ed8603cb8ff0b65c432504668 to your computer and use it in GitHub Desktop.
Save joerodgers/a295df1ed8603cb8ff0b65c432504668 to your computer and use it in GitHub Desktop.
Example using REST and PowerShell to retrieve a secret from Azure Key Vault via AAD Service Principal credential
function Get-AccessToken
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true,ParameterSetName='Resource')]
[Parameter(Mandatory=$true,ParameterSetName='Scope')]
[string]$ClientId,
[Parameter(Mandatory=$true,ParameterSetName='Resource')]
[Parameter(Mandatory=$true,ParameterSetName='Scope')]
[string]$ClientSecret,
[Parameter(Mandatory=$true,ParameterSetName='Resource')]
[Parameter(Mandatory=$true,ParameterSetName='Scope')]
[string]$TenantId,
[Parameter(Mandatory=$true,ParameterSetName='Resource')]
[string]$Resource,
[Parameter(Mandatory=$true,ParameterSetName='Scope')]
[string]$Scope
)
begin
{
$body = @{
"grant_type" = "client_credentials"
"client_id" = $ClientId
"client_secret" = $ClientSecret
}
switch ( $PSCmdlet.ParameterSetName )
{
"Resource"
{
$body["resource"] = $Resource
}
"Scope"
{
$body["scope"] = $Scope
}
}
}
process
{
Invoke-RestMethod -Uri https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token -Method Post -Body $body | SELECT -ExpandProperty access_token
}
end
{
}
}
function Get-AzureActiveDirectoryUser
{
[CmdletBinding()]
param
(
[Parameter(ValueFromPipeline=$false)][string]$UserPrincipalName,
[Parameter(ValueFromPipeline=$true)][string]$AccessToken
)
begin
{
$headers = @{ 'Authorization' = "Bearer $($AccessToken)" }
}
process
{
try
{
Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/users/$UserPrincipalName" –Headers $headers –Method GET
}
catch
{
if( $_.Exception.Response.StatusCode.value__ -ne 404 )
{
throw $_.Exception
}
}
}
end
{
}
}
function Get-VaultSecret
{
[CmdletBinding()]
param
(
[parameter(Mandatory=$true)][string]$AccessToken,
[parameter(Mandatory=$true)][string]$SecretName,
[parameter(Mandatory=$true)][string]$VaultBaseUrl,
[parameter(Mandatory=$true)][string]$SecretVersion
)
begin
{
$headers = @{ "Authorization" = "Bearer $AccessToken" }
$url = "$VaultBaseUrl/secrets/$SecretName/$($SecretVersion)?api-version=2016-10-01"
}
process
{
Invoke-RestMethod -Method Get -Uri $url -Headers $headers | SELECT -ExpandProperty value
}
end
{
}
}
# inspiration:
# https://medium.com/@anoopt/accessing-azure-key-vault-secret-through-azure-key-vault-rest-api-using-an-azure-ad-app-4d837fed747
$clientId = "a59cac14-1234-1234-1234-4c8ef3603194"
$clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$tenantId = "72f988bf-1234-1234-1234-2d7cd011db47"
$scope = "https://vault.azure.net/.default"
$secretName = "xxxxxxxxxxx"
$secretVersion = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$vaultBaseUrl = "https://xxxxxxxxxx.vault.azure.net"
$accessToken = Get-AccessToken -ClientId $clientId -ClientSecret $clientSecret -TenantId $tenantId -Scope $scope
$value = Get-VaultSecret -VaultBaseUrl $vaultBaseUrl -AccessToken $accessToken -SecretName $secretName -SecretVersion $secretVersion -ApiVersion "2016-10-01"
using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
$clientId = "a59cac14-1234-1234-1234-4c8ef3603194"
$clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$tenantId = "72f988bf-1234-1234-1234-2d7cd011db47"
$resource = "https://graph.microsoft.com"
function Get-AccessToken
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true)][string]$ClientId,
[Parameter(Mandatory=$true)][string]$ClientSecret,
[Parameter(Mandatory=$true)][string]$TenantId,
[Parameter(Mandatory=$true)][string]$Resource
)
$body = @{
"grant_type" = "client_credentials"
"resource" = $Resource
"client_id" = $ClientId
"client_secret" = $ClientSecret
}
Invoke-RestMethod -Uri https://login.microsoftonline.com/$TenantId/oauth2/token?api-version=1.0 -Method Post -Body $body | SELECT -ExpandProperty access_token
}
function Get-Office365Group
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true)][string]$AccessToken,
[Parameter(Mandatory=$true)][string]$MailNickName
)
$headers = @{
"Authorization" = "Bearer $AccessToken"
"Content-Type" = "application/json"
}
Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/groups?`$filter=mailnickname eq '$MailNickName'" -Method Get -Headers $headers | SELECT -ExpandProperty value
}
$mailNickname = $Request.Body.MailNickname
$body = ""
if( $mailNickname )
{
Write-Host "Getting Microsoft Graph Access Token"
$accessToken = Get-AccessToken -ClientId $clientId -ClientSecret $clientSecret -TenantId $TenantId -Resource $resource
if( $accessToken )
{
$group = Get-Office365Group -AccessToken $accessToken -MailNickName $mailNickname
if( $group )
{
$status = [HttpStatusCode]::Ok
$body = $group
}
else
{
$status = [HttpStatusCode]::NotFound
$body = "Group not found"
}
}
else
{
$status = [HttpStatusCode]::InternalServerError
$body = "Unable to generate access token"
}
}
else
{
$status = [HttpStatusCode]::BadRequest
$body = "Please pass a mailNickname in the request body."
}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = $status
Body = $body
})
using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
$clientId = "a59cac14-1234-1234-1234-4c8ef3603194"
$clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$tenantId = "72f988bf-1234-1234-1234-2d7cd011db47"
$resource = "https://graph.microsoft.com"
function Get-AccessToken
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true)][string]$ClientId,
[Parameter(Mandatory=$true)][string]$ClientSecret,
[Parameter(Mandatory=$true)][string]$TenantId,
[Parameter(Mandatory=$true)][string]$Resource
)
$body = @{
"grant_type" = "client_credentials"
"resource" = $Resource
"client_id" = $ClientId
"client_secret" = $ClientSecret
}
Invoke-RestMethod -Uri https://login.microsoftonline.com/$TenantId/oauth2/token?api-version=1.0 -Method Post -Body $body | SELECT -ExpandProperty access_token
}
function Get-SharePointOnlineSite
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true)][string]$AccessToken,
[Parameter(Mandatory=$true)][string]$SiteUrl
)
$headers = @{
"Authorization" = "Bearer $AccessToken"
"Content-Type" = "application/json"
}
$uri = [System.Uri]$SiteUrl
try
{
Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/sites/$($uri.Host):$($uri.AbsolutePath)" -Method Get -Headers $headers #| SELECT -ExpandProperty value
}
catch
{
if( $_.Exception.Response.StatusCode -ne 404 )
{
throw $_.Exception
}
}
}
$siteUrl = $Request.Body.SiteUrl
$body = ""
if( $siteUrl )
{
Write-Host "Getting Microsoft Graph Access Token"
$accessToken = Get-AccessToken -ClientId $clientId -ClientSecret $clientSecret -TenantId $TenantId -Resource $resource
if( $accessToken )
{
$site = Get-SharePointOnlineSite -AccessToken $accessToken -SiteUrl $siteUrl
if( $site )
{
$status = [HttpStatusCode]::Ok
$body = $site
}
else
{
$status = [HttpStatusCode]::NotFound
$body = "Site not found"
}
}
else
{
$status = [HttpStatusCode]::InternalServerError
$body = "Unable to generate access token"
}
}
else
{
$status = [HttpStatusCode]::BadRequest
$body = "Please pass a siteUrl in the request body."
}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = $status
Body = $body
})
using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
Import-Module "D:\home\site\wwwroot\GetYammerGroup\Modules\Yammer\yammer-functions.psm1"
$clientId = "xxxxxxxxxxxxxxxxx"
$accessToken = "xxxxxxxxxxxxxxxxx"
# get the group name from the request body
$groupName = $Request.Body.GroupName
# if found, process it
if ($groupName)
{
$group = Get-YammerGroup -AccessToken $accessToken -GroupName $groupName
if( $group )
{
$status = [HttpStatusCode]::OK
$body = $group | ConvertTo-Json
}
else
{
$status = [HttpStatusCode]::NotFound
$body = ""
}
}
else
{
$status = [HttpStatusCode]::BadRequest
$body = "Please pass the group name in the request body."
}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = $status
Body = $body
})
using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
$clientId = "a59cac14-1234-1234-1234-4c8ef3603194"
$clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$tenantId = "72f988bf-1234-1234-1234-2d7cd011db47"
$resource = "https://graph.microsoft.com"
function Get-AccessToken
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true)][string]$ClientId,
[Parameter(Mandatory=$true)][string]$ClientSecret,
[Parameter(Mandatory=$true)][string]$TenantId,
[Parameter(Mandatory=$true)][string]$Resource
)
$body = @{
"grant_type" = "client_credentials"
"resource" = $Resource
"client_id" = $ClientId
"client_secret" = $ClientSecret
}
Invoke-RestMethod -Uri https://login.microsoftonline.com/$TenantId/oauth2/token?api-version=1.0 -Method Post -Body $body | SELECT -ExpandProperty access_token
}
function New-Office365Group
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true)][string]$AccessToken,
[Parameter(Mandatory=$true)][string]$GroupName,
[Parameter(Mandatory=$true)][string]$GroupDescription,
[Parameter(Mandatory=$true)][string]$OwnerId,
[Parameter(Mandatory=$false)][bool]$MailEnabled = $true,
[Parameter(Mandatory=$false)][string]$MailNickname,
[Parameter(Mandatory=$false)][bool]$SecurityEnabled = $true
)
$headers = @{ "Authorization" = "Bearer $AccessToken"
"Content-Type" = "application/json"
}
$body = @{
"description" = $GroupDescription
"displayName" = $GroupName
"groupTypes" = @("Unified")
"mailEnabled" = $MailEnabled
"mailNickname" = $MailNickname
"securityEnabled" = $SecurityEnabled
"owners@odata.bind" = @("https://graph.microsoft.com/v1.0/users/$OwnerId")
} | ConvertTo-Json
Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/groups" -Method Post -Body $body -Headers $headers
}
function Get-AzureActiveDirectoryUser
{
[CmdletBinding()]
param
(
[Parameter(ValueFromPipeline=$false)][string]$UserPrincipalName,
[Parameter(ValueFromPipeline=$true)][string]$AccessToken
)
$headers = @{
'Content-Type' = 'application/json'
'Authorization' = "Bearer $($AccessToken)"
}
try
{
Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/users/$UserPrincipalName" –Headers $headers –Method GET
}
catch
{
if( $_.Exception.Response.StatusCode.value__ -ne 404 )
{
throw $_.Exception
}
}
}
# read the UPN from the HTTP POST body
$groupDescription = $Request.Body.GroupDescription
$groupName = $Request.Body.GroupName
$ownerUPN = $Request.Body.OwnerUserPrincipalName
$mailNickname = $Request.Body.MailNickname -replace " ", ""
$body = ""
if( $groupName -and $mailNickname -and $ownerUPN )
{
Write-Host "Getting Microsoft Graph Access Token"
Write-Host "Access Token: $accessToken"
$accessToken = Get-AccessToken -ClientId $clientId -ClientSecret $clientSecret -TenantId $TenantId -Resource $resource
if( $accessToken )
{
$ownerId = Get-AzureActiveDirectoryUser -UserPrincipalName $ownerUPN -AccessToken $accessToken | SELECT -ExpandProperty id
if( $ownerId )
{
Write-Host "Creating new O365 Group"
try
{
$group = New-Office365Group -AccessToken $accessToken -GroupName $groupName -GroupDescription $groupDescription -MailEnabled $true -MailNickname $MailNickName -SecurityEnabled $true -OwnerId $ownerId
if( $group )
{
$status = [HttpStatusCode]::Ok
$body = $group
}
else
{
$status = [HttpStatusCode]::InternalServerError
$body = "Failed to create new O365 group."
}
}
catch
{
$status = [HttpStatusCode]::InternalServerError
$body = "Failed to create O365 Group. Exception: $($_.Exception)"
}
}
else
{
$status = [HttpStatusCode]::InternalServerError
$body = "Failed to find group owner: $ownerUPN"
}
}
else
{
$status = [HttpStatusCode]::InternalServerError
$body = "Unable to generate access token"
}
}
else
{
$status = [HttpStatusCode]::BadRequest
$body = "Please pass a ownerUserPrincipalName, groupName and mailNickname in the request body."
}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = $status
Body = $body
})
using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
Import-Module "D:\home\site\wwwroot\NewYammerGroup\Modules\Yammer\yammer-functions.psm1"
$clientId = "xxxxxxxxxxxxxxxxx"
$accessToken = "xxxxxxxxxxxxxxxxx"
# get the group name from the request body
$groupName = $Request.Body.GroupName
$ownerEmail = $Request.Body.OwnerEmail
$isPrivate = $Request.Body.IsPrivate
# if found, process it
if ($groupName -and $ownerEmail)
{
$group = Get-YammerGroup -AccessToken $accessToken -GroupName $groupName
if( -not $group )
{
$owner = Get-YammerUser -AccessToken $accessToken -EmailAddress $ownerEmail
if( $owner )
{
$impersonatedOwner = Get-YammerImpersonatedUser -AccessToken $accessToken -ClientId $clientId -UserId $owner.id
if( $impersonatedOwner )
{
$group = New-YammerGroup -GroupName $groupName -AccessToken $impersonatedOwner.token -IsPrivate:$isPrivate
if( $group )
{
$status = [HttpStatusCode]::OK
$body = $group | ConvertTo-Json
}
else
{
$status = [HttpStatusCode]::InternalServerError
$body = "Failed to create yammer group."
}
}
else
{
$status = [HttpStatusCode]::InternalServerError
$body = "Failed to impersonate yammer owner."
}
}
else
{
$status = [HttpStatusCode]::InternalServerError
$body = "Failed to find yammer owner."
}
}
else
{
$status = [HttpStatusCode]::InternalServerError
$body = "Yammer group already exists."
}
}
else
{
$status = [HttpStatusCode]::BadRequest
$body = "Please pass the group name and group owner's email address in the request body."
}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = $status
Body = $body
})
using namespace System.Net
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
$clientId = "a59cac14-1234-1234-1234-4c8ef3603194"
$clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$tenantId = "72f988bf-1234-1234-1234-2d7cd011db47"
$resource = "https://graph.microsoft.com"
$response = [PSCustomObject] @{
UserPrincipalName = "Not set"
IsValidOwner = $true
Error = ""
JobTitle = ""
}
function Get-AccessToken
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$true)][string]$ClientId,
[Parameter(Mandatory=$true)][string]$ClientSecret,
[Parameter(Mandatory=$true)][string]$TenantId,
[Parameter(Mandatory=$true)][string]$Resource
)
$body = @{
"grant_type" = "client_credentials"
"resource" = $Resource
"client_id" = $ClientId
"client_secret" = $ClientSecret
}
Invoke-RestMethod -Uri https://login.microsoftonline.com/$TenantId/oauth2/token?api-version=1.0 -Method Post -Body $body | SELECT -ExpandProperty access_token
}
function Get-AzureActiveDirectoryUser
{
[CmdletBinding()]
param
(
[Parameter(ValueFromPipeline=$false)][string]$UserPrincipalName,
[Parameter(ValueFromPipeline=$true)][string]$AccessToken
)
$headers = @{
'Content-Type' = 'application/json'
'Authorization' = "Bearer $($AccessToken)"
}
try
{
Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/users/$UserPrincipalName" –Headers $headers –Method GET
}
catch
{
if( $_.Exception.Response.StatusCode.value__ -ne 404 )
{
throw $_.Exception
}
}
}
# read the UPN from the HTTP POST body
$userPrincipalName = $Request.Body.UserPrincipalName
if( $userPrincipalName )
{
$response.UserPrincipalName = $userPrincipalName
Write-Host "Getting Microsoft Graph Access Token"
Write-Host "Getting Microsoft Graph Access Token"
$accessToken = Get-AccessToken -ClientId $clientId -ClientSecret $clientSecret -TenantId $TenantId -Resource $resource
if( $accessToken )
{
Write-Host "Looking up $userPrincipalName in AAD."
$azureActiveDirectoryUser = Get-AzureActiveDirectoryUser -AccessToken $accessToken -UserPrincipalName $userPrincipalName
if( $azureActiveDirectoryUser )
{
$status = [HttpStatusCode]::Ok
$response.JobTitle = $azureActiveDirectoryUser.jobTitle
# determine
if( $azureActiveDirectoryUser.jobTitle -match "Ex Dir|ExDir|ExecDir|Exec Dir|SrDir|Sr Dir|Sr\. Dir|VP|Chairman|President|CEO" )
{
$response.IsValidOwner = $false
}
}
else
{
$status = [HttpStatusCode]::NotFound
$response.Error = "$userPrincipalName not found."
}
}
else
{
$status = [HttpStatusCode]::InternalServerError
$response.Error = "Unable to generate access token"
}
}
else
{
$status = [HttpStatusCode]::BadRequest
$response.Error = "Please pass a UserPrincipalName in the request body."
}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = $status
Body = ($response | ConvertTo-Json)
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment