Getting the Secret of a Service Prinicpal in YAML Pipeline (Terraform) - azure

I need to do an Invoke-SQLCmd in Terraform - all fine BUT I need to get the Secret for the service principal (Azure) that is being used throughout the build. So I can use this :
Import-Module SQLServer
# Note: the sample assumes that you or your DBA configured the server to accept connections using
# that Service Principal and has granted it access to the database (in this example at least
# the SELECT permission).
$clientid = "enter application id that corresponds to the Service Principal" # Do not confuse with its display name
$tenantid = "enter the tenant ID of the Service Principal"
$secret = "enter the secret associated with the Service Principal"
$request = Invoke-RestMethod -Method POST `
-Uri "https://login.microsoftonline.com/$tenantid/oauth2/token"`
-Body #{ resource="https://database.windows.net/"; grant_type="client_credentials"; client_id=$clientid; client_secret=$secret }`
-ContentType "application/x-www-form-urlencoded"
$access_token = $request.access_token
# Now that we have the token, we use it to connect to the database 'mydb' on server 'myserver'
Invoke-Sqlcmd -ServerInstance myserver.database.windows.net -Database mydb -AccessToken $access_token`
-query 'select * from Table1'
I can get the cliendId and the TenantID quite easily within PowerShell but I cannot get the secret. So how would i get it ? although i am using the same Service Prinical during the build.

As I have already mentioned you can only retrieve a secret value at the time of creation and after that it becomes hidden . So , its recommended to store the created in some secure place or keyvault.
As you can see for testing I used AzureAD Module and the below script :
## Get APP Details
$APP=Get-AzureADApplication -Filter "DisplayName eq 'ansumanterraformtest'"
## ClientID
Write-Host("clientID : ")$APP.AppId
##TenantID
$tenantID=(Get-AzureADTenantDetail).objectId
Write-Host ("TenantID :")$tenantID
## Get Secret
$getsecret=Get-AzureADApplicationPasswordCredential -ObjectId $APP.ObjectId
if($getsecret.value -ne $null){
Write-Host ("Exisitng $get Secret Value: ")$getsecret.Value
}
else{
Write-Host ("Cannot Retrieve Secret!!!!")
}
Output:
So , As a solution we can create a new secret and retrieve if you don't have it stored in anywhere like below:
$end_date = (get-date).Date.AddDays(365)
## Create new Secret
$createsecret = New-AzureADApplicationPasswordCredential -CustomKeyIdentifier "PowershellKey" -ObjectId $APP.ObjectId -EndDate $end_date
## Secret Value
Write-Host ("Secret Value For new Secret :")$createsecret.value
Output:

Related

Adding Scopes to Azure AD Application with Powershell (Expose an API)

I'd like to add a Scope to an Azure AD App / Service Principal (UI=Expose an API) with Powershell.
$app = New-MgApplication -DisplayName $name -SignInAudience "AzureADMyOrg"
Update-MgApplication -ApplicationId $app.id -IdentifierUris #("api://$($app.AppId)")
$oauth_permission_scopes = #{
AdminConsentDescription = "admin desc"
AdminConsentDisplayName = "admin name"
Type = "Admin"
Value = "Read.all"
Id = $([guid]::NewGuid())
}
$sp = New-MgServicePrincipal -AppId $app.AppId -Notes $description -Tags #("HideApp","WindowsAzureActiveDirectoryIntegratedApp") #HideApp=VisibleToUsers
Update-MgServicePrincipal -ServicePrincipalId $sp.Id -Oauth2PermissionScopes $oauth_permission_scopes
But i get the message:
Update-MgServicePrincipal_UpdateExpanded1: Property 'oauth2PermissionScopes' is read-only and cannot be set.
Can this only be added in the UI?!
I tried to reproduce the same in my environment and got below results:
I ran the same code as you to add scopes and got same error as below:
When I checked the same in Portal, application is created but scope not added like below:
To add scope to Azure AD Application with PowerShell (Expose an API), you need to modify your script as suggested by Cpt.Whale like this:
$api = #{
oauth2PermissionScopes = #(
#{
AdminConsentDescription = "admin desc"
AdminConsentDisplayName = "admin name"
Type = "Admin"
Value = "Read.all"
Id = $([guid]::NewGuid())
}
)
}
Update-MgApplication -ApplicationId $app.id -Api $api
Response:
When I checked the same in Portal, scope added successfully in Expose an API tab of Azure AD application as below:

Invoke-RestMethod :unauthorized client for getting Authentication-token

I have created app registration in azure active directory. I'm trying to invoke an azure ad authenticated with below PowerShell script, but it always display an error:
$clientID = '<clientID>'
$secretKey = '<key>'
$tenantID = '<TenantID>'
$password = ConvertTo-SecureString -String $secretKey -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($ClientID,$password)
Connect-AzureRmAccount -ServicePrincipal -Credential $credential -Tenant $tenantID
$authUrl = "https://login.microsoftonline.com/" + $tenantID + "/oauth2/v2.0/token/"
$body = #{
"scope" = "api://a193b314b-7854-9aab-bb78-6a50ffxxxxxx/";
"grant_type" = "client_credentials";
"client_id" = $ClientID
"client_secret" = $secretKey
}
Write-Output "Getting Authentication-Token"
$adlsToken = Invoke-RestMethod -Uri $authUrl –Method POST -Body $body
Write-Output $adlsToken
I am getting this error. please make me to understand Why I am getting this error
Invoke-RestMethod:
{"error":"invalid_scope","error_description":"AADSTS1002012: The provided value for scope api://3e3643c5-90af-ece is not valid. Client credential flows must have a scope value with /.default suffixed to the resource identifier (application ID URI).\r\nTrace ID:2d4f23bf-b317-4d5c-b5xxxxx\r\nCorrelation ID:fe5945b4-b2c2-4814-9xxxxxxx\r\nTimestamp:04:26:09Z","error_codes":[1002012],"timestamp":"2022-11-19
04:26:09Z","trace_id":"2d4f23bfb3174d5cb5a7xxxxxxx","correlation_id":"fe5945b4-b2c2-4814-99xxxxxxxx"}
Connect-AzAccount: ClientSecretCredential authentication failed:
AADSTS700016: Application with identifier
'3e3643c5-90af-4af6-afxxxxxxx' was not found in the directory 'Default
I tried to reproduce the same in my environment I got the same error as below:
To resolve this issue, check whether you are providing correct ClientID as below:
And, In scope the error mention you have missed /.default Make sure to include /.default like below:
"api://xxxxxx/.default";
When I ran the same script along with scope default, I got the Result successfully like below:
Can you try this by adding .default in scope,
"scope" = "api://a193b314b-7854-9aab-bb78-6a50ffxxxxxx/.default"
If it works, see the reference.

How to add a scope and ApplicationURI to Azure AD Application Registration?

I have a powershell script that creates an azure ad app reg. I've been able to create it, add ms graph permissions and also create a secret. This is what I have so far:
$newapp = New-AzADApplication -DisplayName "mynewApp" -AvailableToOtherTenants $false
Write-Output $newapp
# add a certificate / client secret
$appCredentials = New-AzADAppCredential -ApplicationId $newapp.AppId -StartDate $startDate -EndDate $endDate
$identifierUris = #()
$identifierUris += "api://$newapp.AppId"
$webAppUrl = "https://$functionAppName.azurewebsites.net"
# when you add a redirect URI Azure creates a "web" policy.
$redirectUris = #()
$redirectUris += "$webAppUrl"
Update-AzADApplication -ApplicationId $newapp.AppId -ReplyUrl $redirectUris | Out-Null
#Adds MS Graph User.Read permission
Add-AzADAppPermission -ApplicationId $newapp.AppId -ApiId "00000003-0000-0000-c000-000000000000" -PermissionId "e1fe6dd8-ba31-4d61-89e7-88639da4683d"
But now I need to know how to create the application Uri as depicted below, and also how to create the scope.
You can use New-AzureADApplication command with following parameters
To add Identifier URI - Use -IdentifierUris parameter.
To add Scope - Use -Oauth2Permissions parameter.
New-AzureADApplication
[-AvailableToOtherTenants <Boolean>]
-DisplayName <String>
[-IdentifierUris <System.Collections.Generic.List`1[System.String]>]
[-Oauth2Permissions <System.Collections.Generic.List`1[Microsoft.Open.AzureAD.Model.OAuth2Permission]>]
[-ReplyUrls <System.Collections.Generic.List`1[System.String]>]
[-RequiredResourceAccess <System.Collections.Generic.List`1[Microsoft.Open.AzureAD.Model.RequiredResourceAccess]>]
For example you can create OAuth2Permission object like -
$scope = New-Object Microsoft.Open.AzureAD.Model.OAuth2Permission
$scope.Id = New-Guid
$scope.Value = "user_impersonation"
$scope.UserConsentDisplayName = "<value>"
$scope.UserConsentDescription = "<value>"
$scope.AdminConsentDisplayName = "<value>"
$scope.AdminConsentDescription = "<value>"
$scope.IsEnabled = $true
$scope.Type = "User"
Please refer this documentation for details.
Please check below command to create ApplicationId Uri ,
$newapp = New-AzADApplication -DisplayName "mynewApp" -AvailableToOtherTenants $false
$myappId=$newApp.AppId
Set-AzADApplication -ApplicationId $newApp.AppId -IdentifierUris "api://$myappId"
It may take little time to reflect in portal.
but to create scope , we may need to use azure ad module, and create a new id for user impersonation by enabling Oauth2permissions
please check if references can help
SetupApplications ps1
azure-ad-app-registration-create-scopes
Had similar problem with Azure Application Registration scopes. Here is a simplest solution I have with Az Powershell:
$app = Get-AzAdApplication -ApplicationId "app_guid_here"
$permissionScope = New-Object Microsoft.Azure.Powershell.Cmdlets.Resources.MSGraph.Models.ApiV10.MicrosoftGraphPermissionScope
$permissionScope.Id = New-Guid
$permissionScope.AdminConsentDescription = "text_here"
$permissionScope.AdminConsentDisplayName = "text_here"
$permissionScope.IsEnabled = $true
$permissionScope.Type = "Admin" #Or "User"
$permissionScope.UserConsentDescription = "text_here"
$permissionScope.UserConsentDisplayName = "text_here"
$permissionScope.Value = "user_impersonation"
$api = $app.Api
$api.Oauth2PermissionScope = $permissionScope
Update-AzADApplication -ApplicationId $appId -Api $api

Create azure application through Az module and assign API permissions using powershell

I have written a script which creates azure application using Az module, creates secret key, assigns owner. But assigning API permission gives insufficient permission error. The user is an admin user. Still unable to assign API permission. What wrong am I doing?
$ErrorActionPreference = 'Stop'
Connect-AzAccount
Import-Module Az.Resources
$tenant = Get-AzTenant
Set-AzContext -TenantId $tenant.Id
$AppName = Read-Host -Prompt 'Enter Application name '
$myApp = New-AzADApplication -DisplayName $AppName -IdentifierUris "http://$AppName.com"
Write-Host "App registered.."
$sp = New-AzADServicePrincipal -ApplicationId $myApp.ApplicationId -Role Owner
Write-Host "Service principal registered.."
$startDate = Get-Date
$endDate = $startDate.AddYears(100)
$secret = Read-Host -Prompt 'Enter App Secret Key ' -AsSecureString
$secPassword = ConvertTo-SecureString -AsPlainText -Force -String $secret
New-AzADAppCredential -ObjectId $myApp.ObjectId -StartDate $startDate -EndDate $endDate -Password $secPassword
$ResourceAppIdURI = "https://graph.windows.net/"
# $authority = "https://login.microsoftonline.com/$tenant/oauth2/v2.0/token"
$authority = "https://login.windows.net/$tenant/oauth2/token"
$ClientCred = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential" -ArgumentList $myApp.ApplicationId, $secret
$AuthContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority,$false
$AuthContext.TokenCache.Clear()
Start-Sleep -Seconds 10
$Token = $Authcontext.AcquireTokenAsync($ResourceAppIdURI, $ClientCred)
$AuthHeader = #{"Authorization" = $Token.Result.CreateAuthorizationHeader();"Content-Type"="application/json"}
$url = "https://graph.windows.net/$tenant/applications/$($myApp.ObjectID)?api-version=1.6"
Write-Host "URL: " $url
$postData = "{`"requiredResourceAccess`":[{`"resourceAppId`":`"00000003-0000-0000-c000-000000000000`",
`"resourceAccess`":[{`"id`":`"e1fe6dd8-ba31-4d61-89e7-88639da4683d`",`"type`":`"Scope`"}]}]}";
$result = Invoke-RestMethod -Uri $url -Method "PATCH" -Headers $AuthHeader -Body $postData
Write-Host "Result of App API permission: " $result
In my case, the easiest way to do this without messing around with http requests, was to combine the Azure-powershell module and the Az cli module
So, once I have created my new app:
$myApp = New-AzADApplication -DisplayName $AppName -IdentifierUris "http://$AppName.com"
Then I would login into azure using the Az Cli, and, for instance:
Add some api permissions
Grant these permissions directory admin consent ( if needed )
. { $azcliLogin = az login }
. { az account set --subscription $config.subscriptionId }
. { az ad app permission add --id $myApp.appid --api 00000002-0000-0000-c000-000000000000 --api-permissions 78c8a3c8-a07e-4b9e-af1b-b5ccab50a175=Role }
. { $appApiGrant = az ad app permission grant --id $config.azureAccess.appid --api 00000002-0000-0000-c000-000000000000 }
. { az ad app permission admin-consent --id $myApp.appid }
Where:
--api 00000002-0000-0000-c000-000000000000 Refers to Microsoft Graph API
--api-permissions 78c8a3c8-a07e-4b9e-af1b-b5ccab50a175=Role Refers to some role on this api, as Directory.ReadWrite.All
You can get the required API and API-PERMISSIONS guids from the App manifiest in Azure
This way you create the app with the required granted api permissions, in a single powershell script.
If you want to call Azure AAD graph API to assign permissions with OAuth 2.0 client credentials flow, we need to provide enough permissions(Azure AD Graph -> Aapplication permissions -> Application.ReadWrite.All)
Besides, regarding how to assign permissions to AD application with PowerShell, we also can use PowerShell module AzureAD.
For example
Connect-AzureAD
$AppAccess = [Microsoft.Open.AzureAD.Model.RequiredResourceAccess]#{
ResourceAppId = "00000003-0000-0000-c000-000000000000";
ResourceAccess =
[Microsoft.Open.AzureAD.Model.ResourceAccess]#{
Id = "";
Type = ""},
[Microsoft.Open.AzureAD.Model.ResourceAccess]#{
Id = "";
Type = ""}
}
Set-AzureADApplication -ObjectId <the app object id> -RequiredResourceAccess $AppAccess
Update
According to my test, when we use Az module, we can use the following method to get access token and call AAD graph rest API. But please note that when you use the method, the account you use to run Connect-AzAccount should be Azure AD Global Admin
Connect-AzAccount
$context =Get-AzContext
$dexResourceUrl='https://graph.windows.net/'
$token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account,
$context.Environment,
$context.Tenant.Id.ToString(),
$null,
[Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never,
$null, $dexResourceUrl).AccessToken
# assign permissions
$headers =#{}
$headers.Add("Content-Type", "application/json")
$headers.Add("Accept", "application/json")
$headers.Add("Authorization", "Bearer $($token)")
$body = "{
`n `"requiredResourceAccess`": [{
`n `"resourceAppId`": `"00000003-0000-0000-c000-000000000000`",
`n `"resourceAccess`": [
`n {
`n `"id`": `"405a51b5-8d8d-430b-9842-8be4b0e9f324`",
`n `"type`": `"Role`"
`n },
`n {
`n `"id`": `"09850681-111b-4a89-9bed-3f2cae46d706`",
`n `"type`": `"Role`"
`n }
`n ]
`n }
`n ]
`n}
`n"
$url ='https://graph.windows.net/hanxia.onmicrosoft.com/applications/d4975420-841f-47d5-a3d2-0870901f13cd?api-version=1.6'
Invoke-RestMethod $url -Method 'PATCH' -Headers $headers -Body $body
#check if adding the permissions you need
$headers =#{}
$headers.Add("Accept", "application/json")
$headers.Add("Authorization", "Bearer $($token)")
$url ='https://graph.windows.net/hanxia.onmicrosoft.com/applications/<aad application object id>?api-version=1.6'
$response=Invoke-RestMethod $url -Method 'GET' -Headers $headers
$response.requiredResourceAccess | ConvertTo-Json

Azure Activity Log

I want to monitor who made a change in rbac assignment, I created powershell script for collection data from Azure Activity Log. I used below piece of code. Using this solution I am able to get items like:
caller - user who made a role assignment change,
timestamp,
Resource name - on this resource assignment change has been provided,
action type - write or delete
In Activity Log panel in Azure portal, in Summary portal (Message: shared with "user info"), I can see name of a user who has been granted permissions/assignment to the resource, but using my powershell script I am not able to catch this information, is there any method to get this info?
Get-AzureRmLog -StartTime (Get-Date).AddDays(-7) |
Where-Object {$_.Authorization.Action -like
'Microsoft.Authorization/roleAssignments/*'} |
Select-Object #{N="Caller";E={$_.Caller}},
#{N="Resource";E={$_.Authorization.Scope}},
#{N="Action";E={Split-Path $_.Authorization.action -leaf}},
EventTimestamp
script output:
Caller : username#xxx.com
Resource :/subscriptions/xxxx/resourceGroups/Powershell/providers/Microsoft.Compute/virtualMachines/xx/providers/Microsoft.Authorization/roleAssignments/xxxx
Action : write
EventTimestamp : 8/29/2019 10:12:31 AM
Your requirement of fetching the user name to whom the RBAC role is assigned is currently not supported using Az PowerShell cmdlet Get-AzLog or Get-AzureRmLog.
However, we can accomplish your requirement by leveraging Azure REST API for Activity Logs - List and Az PowerShell cmdlet Get-AzureADUser.
In this way as we are depending on Azure REST API for Activity Logs - List (but looks like you want PowerShell way of accomplishing the requirement) so call the REST API in PowerShell as something shown below.
$request = "https://management.azure.com/subscriptions/{subscriptionId}/providers/microsoft.insights/eventtypes/management/values?api-version=2015-04-01&`$filter={$filter}"
$auth = "eyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$authHeader = #{
'Content-Type'='application/json'
'Accept'='application/json'
'Authorization'= "Bearer $auth"
}
$Output = Invoke-RestMethod -Uri $request -Headers $authHeader -Method GET -Body $Body
$ActivityLogsFinalOutput = $Output.Value
Develop your PowerShell code to get "PrincipalId" (which is under "properties") from the output of your Azure REST API for Activity Logs - List call. The fetched "PrincipalId" is the ObjectID of the user whom you want to get ultimately.
Now leverage Az PowerShell cmdlet Get-AzureADUser and have your command something like shown below.
(Get-AzureADUser -ObjectID "<PrincipalID>").DisplayName
Hope this helps!! Cheers!!
UPDATE:
Please find PowerShell way of fetching auth token (i.e., $auth) that needs to be used in above REST API call.
$ClientID = "<ClientID>" #ApplicationID
$ClientSecret = "<ClientSecret>" #key from Application
$tennantid = "<TennantID>"
$TokenEndpoint = {https://login.windows.net/{0}/oauth2/token} -f $tennantid
$ARMResource = "https://management.core.windows.net/";
$Body1 = #{
'resource'= $ARMResource
'client_id' = $ClientID
'grant_type' = 'client_credentials'
'client_secret' = $ClientSecret
}
$params = #{
ContentType = 'application/x-www-form-urlencoded'
Headers = #{'accept'='application/json'}
Body = $Body1
Method = 'Post'
URI = $TokenEndpoint
}
$token = Invoke-RestMethod #params
$token | select access_token, #{L='Expires';E={[timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($_.expires_on))}} | fl *
I see this new way as well but I didn't get chance to test this out. If interested, you may alternatively try this or go with above approach.
UPDATE2:
$ActivityLogsFinalOutput| %{
if(($_.properties.responseBody) -like "*principalId*"){
$SplittedPrincipalID = $_.properties.responseBody -split "PrincipalID"
$SplittedComma = $SplittedPrincipalID[1] -split ","
$SplittedDoubleQuote = $SplittedComma[0] -split "`""
$PrincipalID = $SplittedDoubleQuote[2]
#Continue code for getting Azure AD User using above fetched $PrincipalID
#...
#...
}
}
Does this work for you?
Get-AzureRmLog -StartTime (Get-Date).AddDays(-7) |
Where-Object {$_.Authorization.Action -like 'Microsoft.Authorization/roleAssignments/*'} |
Select-Object #{N="Caller";E={$_.Caller}},
#{N="Resource";E={$_.Authorization.Scope}},
#{N="Action";E={Split-Path $_.Authorization.action -leaf}},
#{N="Name";E={$_.Claims.Content.name}},
EventTimestamp
My output:
Caller : username#domain.com
Resource : /subscriptions/xxxx/resourceGroups/xxxx/providers/Microsoft.Authorization/roleAssignments/xxxx
Action : write
Name : John Doe
EventTimestamp : 30.08.2019 12.05.52
NB: I used Get-AzLog. Not sure if there is any difference between Get-AzLog and Get-AzureRmLog.
Fairly certain this wouldn't be exposed with this cmdlet. I dont even see this information in the Role Assignments. So not sure what do you mean exactly.

Resources