Last active
April 23, 2024 09:44
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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